]> git.lizzy.rs Git - rust.git/commitdiff
Cache result of
authorDániel Buga <bugadani@gmail.com>
Sun, 20 Dec 2020 09:29:02 +0000 (10:29 +0100)
committerDániel Buga <bugadani@gmail.com>
Sun, 20 Dec 2020 09:29:26 +0000 (10:29 +0100)
compiler/rustc_middle/src/mir/graph_cyclic_cache.rs [new file with mode: 0644]
compiler/rustc_middle/src/mir/mod.rs

diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
new file mode 100644 (file)
index 0000000..5f02897
--- /dev/null
@@ -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,
+}
index ad48c3510484b03bb7a731443838809ec425fd57..a69555fd1a8cec34ceb0cfb2a534511aaa45bd9e 100644 (file)
 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;
@@ -227,6 +229,7 @@ pub struct Body<'tcx> {
     pub is_polymorphic: bool,
 
     predecessor_cache: PredecessorCache,
+    is_cyclic: GraphIsCyclicCache,
 }
 
 impl<'tcx> Body<'tcx> {
@@ -267,6 +270,7 @@ pub fn new(
             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
@@ -296,6 +300,7 @@ pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) ->
             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
@@ -309,11 +314,12 @@ pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
     #[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
     }
 
@@ -322,6 +328,7 @@ pub fn basic_blocks_and_local_decls_mut(
         &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)
     }
 
@@ -334,13 +341,14 @@ pub fn basic_blocks_local_decls_mut_and_var_debug_info(
         &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]