From 119879cd5d5d90c385bcadd6865ac4052ed7b623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 20 Dec 2020 10:29:02 +0100 Subject: [PATCH] Cache result of --- .../src/mir/graph_cyclic_cache.rs | 62 +++++++++++++++++++ compiler/rustc_middle/src/mir/mod.rs | 14 ++++- 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_middle/src/mir/graph_cyclic_cache.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 index 0000000000000..5f028975bd0e3 --- /dev/null +++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs @@ -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, +} + +impl GraphIsCyclicCache { + #[inline] + pub(super) fn new() -> Self { + GraphIsCyclicCache { cache: OnceCell::new() } + } + + pub(super) fn is_cyclic(&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 serialize::Encodable for GraphIsCyclicCache { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + serialize::Encodable::encode(&(), s) + } +} + +impl serialize::Decodable for GraphIsCyclicCache { + #[inline] + fn decode(d: &mut D) -> Result { + serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + } +} + +impl HashStable for GraphIsCyclicCache { + #[inline] + fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) { + // do nothing + } +} + +TrivialTypeFoldableAndLiftImpls! { + GraphIsCyclicCache, +} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ad48c3510484b..a69555fd1a8ce 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -35,11 +35,13 @@ use std::ops::{ControlFlow, Index, IndexMut}; 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 @@ 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 @@ -296,6 +300,7 @@ impl<'tcx> Body<'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 @@ impl<'tcx> Body<'tcx> { #[inline] pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { // 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 @@ impl<'tcx> Body<'tcx> { &mut self, ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls) } @@ -334,13 +341,14 @@ impl<'tcx> Body<'tcx> { &mut Vec>, ) { 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]