--- /dev/null
+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,
+}
use std::slice;
use std::{iter, mem, option};
+use self::graph_cyclic_cache::GraphIsCyclicCache;
use self::predecessors::{PredecessorCache, Predecessors};
pub use self::query::*;
pub mod abstract_const;
pub mod coverage;
+mod graph_cyclic_cache;
pub mod interpret;
pub mod mono;
mod predecessors;
pub is_polymorphic: bool,
predecessor_cache: PredecessorCache,
+ is_cyclic: GraphIsCyclicCache,
}
impl<'tcx> Body<'tcx> {
required_consts: Vec::new(),
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
+ is_cyclic: GraphIsCyclicCache::new(),
};
body.is_polymorphic = body.has_param_types_or_consts();
body
var_debug_info: Vec::new(),
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
+ is_cyclic: GraphIsCyclicCache::new(),
};
body.is_polymorphic = body.has_param_types_or_consts();
body
#[inline]
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
- // invalidate the predecessor cache.
+ // invalidate the caches.
//
// 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.is_cyclic.invalidate();
&mut self.basic_blocks
}
&mut self,
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
self.predecessor_cache.invalidate();
+ self.is_cyclic.invalidate();
(&mut self.basic_blocks, &mut self.local_decls)
}
&mut Vec<VarDebugInfo<'tcx>>,
) {
self.predecessor_cache.invalidate();
+ self.is_cyclic.invalidate();
(&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
/// `START_BLOCK`.
pub fn is_cfg_cyclic(&self) -> bool {
- graph::is_cyclic(self)
+ self.is_cyclic.is_cyclic(self)
}
#[inline]