Skip to content

Commit

Permalink
Auto merge of rust-lang#78454 - bugadani:cyclic, r=oli-obk
Browse files Browse the repository at this point in the history
MIR Body: Cache result of `is_cyclic` call
  • Loading branch information
bors committed Dec 28, 2020
2 parents aef92d4 + 119879c commit 76aca66
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
62 changes: 62 additions & 0 deletions compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
Original file line number Diff line number Diff line change
@@ -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,
}
14 changes: 11 additions & 3 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -227,6 +229,7 @@ pub struct Body<'tcx> {
pub is_polymorphic: bool,

predecessor_cache: PredecessorCache,
is_cyclic: GraphIsCyclicCache,
}

impl<'tcx> Body<'tcx> {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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)
}

Expand All @@ -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]
Expand Down

0 comments on commit 76aca66

Please sign in to comment.