Cache result of

This commit is contained in:
Dániel Buga 2020-12-20 10:29:02 +01:00
parent bd2f1cb278
commit 119879cd5d
2 changed files with 73 additions and 3 deletions

View 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,
}

View File

@ -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<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 @@ impl<'tcx> Body<'tcx> {
&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 @@ impl<'tcx> Body<'tcx> {
&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]