use std::sync::OnceLock; use rustc_data_structures::graph; use rustc_data_structures::graph::dominators::{Dominators, dominators}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use smallvec::SmallVec; use crate::mir::traversal::Postorder; use crate::mir::{BasicBlock, BasicBlockData, START_BLOCK}; #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct BasicBlocks<'tcx> { basic_blocks: IndexVec>, cache: Cache, } // Typically 95%+ of basic blocks have 4 or fewer predecessors. type Predecessors = IndexVec>; #[derive(Debug, Clone, Copy)] pub enum SwitchTargetValue { // A normal switch value. Normal(u128), // The final "otherwise" fallback value. Otherwise, } #[derive(Clone, Default, Debug)] struct Cache { predecessors: OnceLock, reverse_postorder: OnceLock>, dominators: OnceLock>, } impl<'tcx> BasicBlocks<'tcx> { #[inline] pub fn new(basic_blocks: IndexVec>) -> Self { BasicBlocks { basic_blocks, cache: Cache::default() } } pub fn dominators(&self) -> &Dominators { self.cache.dominators.get_or_init(|| dominators(self)) } /// Returns predecessors for each basic block. #[inline] pub fn predecessors(&self) -> &Predecessors { self.cache.predecessors.get_or_init(|| { let mut preds = IndexVec::from_elem(SmallVec::new(), &self.basic_blocks); for (bb, data) in self.basic_blocks.iter_enumerated() { if let Some(term) = &data.terminator { for succ in term.successors() { preds[succ].push(bb); } } } preds }) } /// Returns basic blocks in a reverse postorder. /// /// See [`traversal::reverse_postorder`]'s docs to learn what is preorder traversal. /// /// [`traversal::reverse_postorder`]: crate::mir::traversal::reverse_postorder #[inline] pub fn reverse_postorder(&self) -> &[BasicBlock] { self.cache.reverse_postorder.get_or_init(|| { let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK, None).collect(); rpo.reverse(); rpo }) } /// Returns mutable reference to basic blocks. Invalidates CFG cache. #[inline] pub fn as_mut(&mut self) -> &mut IndexVec> { self.invalidate_cfg_cache(); &mut self.basic_blocks } /// Get mutable access to basic blocks without invalidating the CFG cache. /// /// By calling this method instead of e.g. [`BasicBlocks::as_mut`] you promise not to change /// the CFG. This means that /// /// 1) The number of basic blocks remains unchanged /// 2) The set of successors of each terminator remains unchanged. /// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator /// kind is not changed. /// /// If any of these conditions cannot be upheld, you should call [`BasicBlocks::invalidate_cfg_cache`]. #[inline] pub fn as_mut_preserves_cfg(&mut self) -> &mut IndexVec> { &mut self.basic_blocks } /// Invalidates cached information about the CFG. /// /// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`]. /// All other methods that allow you to mutate the basic blocks also call this method /// themselves, thereby avoiding any risk of accidentally cache invalidation. pub fn invalidate_cfg_cache(&mut self) { self.cache = Cache::default(); } } impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> { type Target = IndexSlice>; #[inline] fn deref(&self) -> &IndexSlice> { &self.basic_blocks } } impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> { type Node = BasicBlock; #[inline] fn num_nodes(&self) -> usize { self.basic_blocks.len() } } impl<'tcx> graph::StartNode for BasicBlocks<'tcx> { #[inline] fn start_node(&self) -> Self::Node { START_BLOCK } } impl<'tcx> graph::Successors for BasicBlocks<'tcx> { #[inline] fn successors(&self, node: Self::Node) -> impl Iterator { self.basic_blocks[node].terminator().successors() } } impl<'tcx> graph::Predecessors for BasicBlocks<'tcx> { #[inline] fn predecessors(&self, node: Self::Node) -> impl Iterator { self.predecessors()[node].iter().copied() } } // Done here instead of in `structural_impls.rs` because `Cache` is private, as is `basic_blocks`. TrivialTypeTraversalImpls! { Cache } impl Encodable for Cache { #[inline] fn encode(&self, _s: &mut S) {} } impl Decodable for Cache { #[inline] fn decode(_: &mut D) -> Self { Default::default() } } impl HashStable for Cache { #[inline] fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {} }