Auto merge of #78454 - bugadani:cyclic, r=oli-obk
MIR Body: Cache result of `is_cyclic` call
This commit is contained in:
commit
76aca6659a
62
compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
Normal file
62
compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use rustc_data_structures::graph::{
|
||||||
|
self, DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors,
|
||||||
|
};
|
||||||
|
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
|
use rustc_data_structures::sync::OnceCell;
|
||||||
|
use rustc_serialize as serialize;
|
||||||
|
|
||||||
|
/// Helper type to cache the result of `graph::is_cyclic`.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(super) struct GraphIsCyclicCache {
|
||||||
|
cache: OnceCell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphIsCyclicCache {
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
GraphIsCyclicCache { cache: OnceCell::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn is_cyclic<G>(&self, graph: &G) -> bool
|
||||||
|
where
|
||||||
|
G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes,
|
||||||
|
{
|
||||||
|
*self.cache.get_or_init(|| graph::is_cyclic(graph))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidates the cache.
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn invalidate(&mut self) {
|
||||||
|
// Invalidating the cache requires mutating the MIR, which in turn requires a unique
|
||||||
|
// reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
|
||||||
|
// callers of `invalidate` have a unique reference to the MIR and thus to the
|
||||||
|
// cache. This means we never need to do synchronization when `invalidate` is called,
|
||||||
|
// we can simply reinitialize the `OnceCell`.
|
||||||
|
self.cache = OnceCell::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: serialize::Encoder> serialize::Encodable<S> for GraphIsCyclicCache {
|
||||||
|
#[inline]
|
||||||
|
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
|
serialize::Encodable::encode(&(), s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: serialize::Decoder> serialize::Decodable<D> for GraphIsCyclicCache {
|
||||||
|
#[inline]
|
||||||
|
fn decode(d: &mut D) -> Result<Self, D::Error> {
|
||||||
|
serialize::Decodable::decode(d).map(|_v: ()| Self::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTX> HashStable<CTX> for GraphIsCyclicCache {
|
||||||
|
#[inline]
|
||||||
|
fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrivialTypeFoldableAndLiftImpls! {
|
||||||
|
GraphIsCyclicCache,
|
||||||
|
}
|
@ -35,11 +35,13 @@ use std::ops::{ControlFlow, Index, IndexMut};
|
|||||||
use std::slice;
|
use std::slice;
|
||||||
use std::{iter, mem, option};
|
use std::{iter, mem, option};
|
||||||
|
|
||||||
|
use self::graph_cyclic_cache::GraphIsCyclicCache;
|
||||||
use self::predecessors::{PredecessorCache, Predecessors};
|
use self::predecessors::{PredecessorCache, Predecessors};
|
||||||
pub use self::query::*;
|
pub use self::query::*;
|
||||||
|
|
||||||
pub mod abstract_const;
|
pub mod abstract_const;
|
||||||
pub mod coverage;
|
pub mod coverage;
|
||||||
|
mod graph_cyclic_cache;
|
||||||
pub mod interpret;
|
pub mod interpret;
|
||||||
pub mod mono;
|
pub mod mono;
|
||||||
mod predecessors;
|
mod predecessors;
|
||||||
@ -227,6 +229,7 @@ pub struct Body<'tcx> {
|
|||||||
pub is_polymorphic: bool,
|
pub is_polymorphic: bool,
|
||||||
|
|
||||||
predecessor_cache: PredecessorCache,
|
predecessor_cache: PredecessorCache,
|
||||||
|
is_cyclic: GraphIsCyclicCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Body<'tcx> {
|
impl<'tcx> Body<'tcx> {
|
||||||
@ -267,6 +270,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
required_consts: Vec::new(),
|
required_consts: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
predecessor_cache: PredecessorCache::new(),
|
predecessor_cache: PredecessorCache::new(),
|
||||||
|
is_cyclic: GraphIsCyclicCache::new(),
|
||||||
};
|
};
|
||||||
body.is_polymorphic = body.has_param_types_or_consts();
|
body.is_polymorphic = body.has_param_types_or_consts();
|
||||||
body
|
body
|
||||||
@ -296,6 +300,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
var_debug_info: Vec::new(),
|
var_debug_info: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
predecessor_cache: PredecessorCache::new(),
|
predecessor_cache: PredecessorCache::new(),
|
||||||
|
is_cyclic: GraphIsCyclicCache::new(),
|
||||||
};
|
};
|
||||||
body.is_polymorphic = body.has_param_types_or_consts();
|
body.is_polymorphic = body.has_param_types_or_consts();
|
||||||
body
|
body
|
||||||
@ -309,11 +314,12 @@ impl<'tcx> Body<'tcx> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
||||||
// Because the user could mutate basic block terminators via this reference, we need to
|
// Because the user could mutate basic block terminators via this reference, we need to
|
||||||
// invalidate the predecessor cache.
|
// invalidate the caches.
|
||||||
//
|
//
|
||||||
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
||||||
// invalidate the predecessor cache.
|
// invalidate the caches.
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.is_cyclic.invalidate();
|
||||||
&mut self.basic_blocks
|
&mut self.basic_blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,6 +328,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.is_cyclic.invalidate();
|
||||||
(&mut self.basic_blocks, &mut self.local_decls)
|
(&mut self.basic_blocks, &mut self.local_decls)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,13 +341,14 @@ impl<'tcx> Body<'tcx> {
|
|||||||
&mut Vec<VarDebugInfo<'tcx>>,
|
&mut Vec<VarDebugInfo<'tcx>>,
|
||||||
) {
|
) {
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.is_cyclic.invalidate();
|
||||||
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
|
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
|
||||||
/// `START_BLOCK`.
|
/// `START_BLOCK`.
|
||||||
pub fn is_cfg_cyclic(&self) -> bool {
|
pub fn is_cfg_cyclic(&self) -> bool {
|
||||||
graph::is_cyclic(self)
|
self.is_cyclic.is_cyclic(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
Loading…
Reference in New Issue
Block a user