diff options
| author | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2022-07-04 00:00:00 +0000 |
|---|---|---|
| committer | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2022-07-07 08:11:49 +0200 |
| commit | c9dd1d9983b9a08159c2e3c86071d1a5ef7ebb20 (patch) | |
| tree | 821f63c3785e5599f0252939c2289414b947d530 /compiler/rustc_middle | |
| parent | fac8fa56726f7a5b2d4880a4719c5f99beec8328 (diff) | |
| download | rust-c9dd1d9983b9a08159c2e3c86071d1a5ef7ebb20.tar.gz rust-c9dd1d9983b9a08159c2e3c86071d1a5ef7ebb20.zip | |
Make MIR basic blocks field public
This makes it possible to mutably borrow different fields of the MIR body without resorting to methods like `basic_blocks_local_decls_mut_and_var_debug_info`. To preserve validity of control flow graph caches in the presence of modifications, a new struct `BasicBlocks` wraps together basic blocks and control flow graph caches. The `BasicBlocks` dereferences to `IndexVec<BasicBlock, BasicBlockData>`. On the other hand a mutable access requires explicit `as_mut()` call.
Diffstat (limited to 'compiler/rustc_middle')
| -rw-r--r-- | compiler/rustc_middle/src/mir/basic_blocks.rs | 141 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/mod.rs | 151 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/traversal.rs | 31 |
3 files changed, 169 insertions, 154 deletions
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs new file mode 100644 index 00000000000..ec3a818472b --- /dev/null +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -0,0 +1,141 @@ +use crate::mir::graph_cyclic_cache::GraphIsCyclicCache; +use crate::mir::predecessors::{PredecessorCache, Predecessors}; +use crate::mir::switch_sources::{SwitchSourceCache, SwitchSources}; +use crate::mir::traversal::PostorderCache; +use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK}; + +use rustc_data_structures::graph; +use rustc_index::vec::IndexVec; + +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] +pub struct BasicBlocks<'tcx> { + basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>, + predecessor_cache: PredecessorCache, + switch_source_cache: SwitchSourceCache, + is_cyclic: GraphIsCyclicCache, + postorder_cache: PostorderCache, +} + +impl<'tcx> BasicBlocks<'tcx> { + #[inline] + pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { + BasicBlocks { + basic_blocks, + predecessor_cache: PredecessorCache::new(), + switch_source_cache: SwitchSourceCache::new(), + is_cyclic: GraphIsCyclicCache::new(), + postorder_cache: PostorderCache::new(), + } + } + + /// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`. + #[inline] + pub fn is_cfg_cyclic(&self) -> bool { + self.is_cyclic.is_cyclic(self) + } + + /// Returns predecessors for each basic block. + #[inline] + pub fn predecessors(&self) -> &Predecessors { + self.predecessor_cache.compute(&self.basic_blocks) + } + + /// Returns basic blocks in a postorder. + #[inline] + pub fn postorder(&self) -> &[BasicBlock] { + self.postorder_cache.compute(&self.basic_blocks) + } + + /// `switch_sources()[&(target, switch)]` returns a list of switch + /// values that lead to a `target` block from a `switch` block. + #[inline] + pub fn switch_sources(&self) -> &SwitchSources { + self.switch_source_cache.compute(&self.basic_blocks) + } + + /// Returns mutable reference to basic blocks. Invalidates CFG cache. + #[inline] + pub fn as_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> { + 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<BasicBlock, BasicBlockData<'tcx>> { + &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 accidentaly cache invalidation. + pub fn invalidate_cfg_cache(&mut self) { + self.predecessor_cache.invalidate(); + self.switch_source_cache.invalidate(); + self.is_cyclic.invalidate(); + self.postorder_cache.invalidate(); + } +} + +impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> { + type Target = IndexVec<BasicBlock, BasicBlockData<'tcx>>; + + #[inline] + fn deref(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> { + &self.basic_blocks + } +} + +impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> { + type Node = BasicBlock; +} + +impl<'tcx> graph::WithNumNodes for BasicBlocks<'tcx> { + #[inline] + fn num_nodes(&self) -> usize { + self.basic_blocks.len() + } +} + +impl<'tcx> graph::WithStartNode for BasicBlocks<'tcx> { + #[inline] + fn start_node(&self) -> Self::Node { + START_BLOCK + } +} + +impl<'tcx> graph::WithSuccessors for BasicBlocks<'tcx> { + #[inline] + fn successors(&self, node: Self::Node) -> <Self as graph::GraphSuccessors<'_>>::Iter { + self.basic_blocks[node].terminator().successors() + } +} + +impl<'a, 'b> graph::GraphSuccessors<'b> for BasicBlocks<'a> { + type Item = BasicBlock; + type Iter = Successors<'b>; +} + +impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for BasicBlocks<'tcx> { + type Item = BasicBlock; + type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>; +} + +impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { + #[inline] + fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter { + self.predecessors()[node].iter().copied() + } +} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index e7d7317456c..6a0aa247a94 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -5,7 +5,6 @@ use crate::mir::interpret::{ AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar, }; -use crate::mir::traversal::PostorderCache; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; @@ -28,7 +27,6 @@ use polonius_engine::Atom; pub use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::{dominators, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors}; use rustc_index::bit_set::BitMatrix; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{Decodable, Encodable}; @@ -43,11 +41,12 @@ use std::fmt::{self, Debug, Display, Formatter, Write}; use std::ops::{ControlFlow, Index, IndexMut}; use std::{iter, mem}; -use self::graph_cyclic_cache::GraphIsCyclicCache; -use self::predecessors::{PredecessorCache, Predecessors}; +use self::predecessors::Predecessors; pub use self::query::*; -use self::switch_sources::{SwitchSourceCache, SwitchSources}; +use self::switch_sources::SwitchSources; +pub use basic_blocks::BasicBlocks; +mod basic_blocks; pub mod coverage; mod generic_graph; pub mod generic_graphviz; @@ -189,7 +188,7 @@ pub struct GeneratorInfo<'tcx> { pub struct Body<'tcx> { /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`] /// that indexes into this vector. - basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>, + pub basic_blocks: BasicBlocks<'tcx>, /// Records how far through the "desugaring and optimization" process this particular /// MIR has traversed. This is particularly useful when inlining, since in that context @@ -257,11 +256,6 @@ pub struct Body<'tcx> { /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this. pub is_polymorphic: bool, - predecessor_cache: PredecessorCache, - switch_source_cache: SwitchSourceCache, - is_cyclic: GraphIsCyclicCache, - postorder_cache: PostorderCache, - pub tainted_by_errors: Option<ErrorGuaranteed>, } @@ -289,7 +283,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Built, source, - basic_blocks, + basic_blocks: BasicBlocks::new(basic_blocks), source_scopes, generator: generator_kind.map(|generator_kind| { Box::new(GeneratorInfo { @@ -307,10 +301,6 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), is_polymorphic: false, - predecessor_cache: PredecessorCache::new(), - switch_source_cache: SwitchSourceCache::new(), - is_cyclic: GraphIsCyclicCache::new(), - postorder_cache: PostorderCache::new(), tainted_by_errors, }; body.is_polymorphic = body.has_param_types_or_consts(); @@ -326,7 +316,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Built, source: MirSource::item(CRATE_DEF_ID.to_def_id()), - basic_blocks, + basic_blocks: BasicBlocks::new(basic_blocks), source_scopes: IndexVec::new(), generator: None, local_decls: IndexVec::new(), @@ -337,10 +327,6 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, - predecessor_cache: PredecessorCache::new(), - switch_source_cache: SwitchSourceCache::new(), - is_cyclic: GraphIsCyclicCache::new(), - postorder_cache: PostorderCache::new(), tainted_by_errors: None, }; body.is_polymorphic = body.has_param_types_or_consts(); @@ -354,74 +340,13 @@ 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 caches. - // - // FIXME: Use a finer-grained API for this, so only transformations that alter terminators - // invalidate the caches. - self.invalidate_cfg_cache(); - &mut self.basic_blocks - } - - #[inline] - pub fn basic_blocks_and_local_decls_mut( - &mut self, - ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) { - self.invalidate_cfg_cache(); - (&mut self.basic_blocks, &mut self.local_decls) - } - - #[inline] - pub fn basic_blocks_local_decls_mut_and_var_debug_info( - &mut self, - ) -> ( - &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, - &mut LocalDecls<'tcx>, - &mut Vec<VarDebugInfo<'tcx>>, - ) { - self.invalidate_cfg_cache(); - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) - } - - /// Get mutable access to parts of the Body without invalidating the CFG cache. - /// - /// By calling this method instead of eg [`Body::basic_blocks_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 [`Body::invalidate_cfg_cache`]. - #[inline] - pub fn basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate( - &mut self, - ) -> ( - &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, - &mut LocalDecls<'tcx>, - &mut Vec<VarDebugInfo<'tcx>>, - ) { - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) - } - - /// Invalidates cached information about the CFG. - /// - /// You will only ever need this if you have also called - /// [`Body::basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate`]. All other methods - /// that allow you to mutate the body also call this method themselves, thereby avoiding any - /// risk of accidentaly cache invalidation. - pub fn invalidate_cfg_cache(&mut self) { - self.predecessor_cache.invalidate(); - self.switch_source_cache.invalidate(); - self.is_cyclic.invalidate(); - self.postorder_cache.invalidate(); + self.basic_blocks.as_mut() } /// 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 { - self.is_cyclic.is_cyclic(self) + self.basic_blocks.is_cfg_cyclic() } #[inline] @@ -495,14 +420,6 @@ impl<'tcx> Body<'tcx> { self.local_decls.drain(self.arg_count + 1..) } - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_statement_nop(&mut self, location: Location) { - let block = &mut self.basic_blocks[location.block]; - debug_assert!(location.statement_index < block.statements.len()); - block.statements[location.statement_index].make_nop() - } - /// Returns the source info associated with `location`. pub fn source_info(&self, location: Location) -> &SourceInfo { let block = &self[location.block]; @@ -540,19 +457,19 @@ impl<'tcx> Body<'tcx> { #[inline] pub fn predecessors(&self) -> &Predecessors { - self.predecessor_cache.compute(&self.basic_blocks) + self.basic_blocks.predecessors() } /// `body.switch_sources()[&(target, switch)]` returns a list of switch /// values that lead to a `target` block from a `switch` block. #[inline] pub fn switch_sources(&self) -> &SwitchSources { - self.switch_source_cache.compute(&self.basic_blocks) + self.basic_blocks.switch_sources() } #[inline] pub fn dominators(&self) -> Dominators<BasicBlock> { - dominators(self) + dominators(&self.basic_blocks) } #[inline] @@ -599,7 +516,7 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> { impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> { #[inline] fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { - &mut self.basic_blocks_mut()[index] + &mut self.basic_blocks.as_mut()[index] } } @@ -2890,48 +2807,6 @@ fn pretty_print_const_value<'tcx>( }) } -impl<'tcx> graph::DirectedGraph for Body<'tcx> { - type Node = BasicBlock; -} - -impl<'tcx> graph::WithNumNodes for Body<'tcx> { - #[inline] - fn num_nodes(&self) -> usize { - self.basic_blocks.len() - } -} - -impl<'tcx> graph::WithStartNode for Body<'tcx> { - #[inline] - fn start_node(&self) -> Self::Node { - START_BLOCK - } -} - -impl<'tcx> graph::WithSuccessors for Body<'tcx> { - #[inline] - fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter { - self.basic_blocks[node].terminator().successors() - } -} - -impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { - type Item = BasicBlock; - type Iter = Successors<'b>; -} - -impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> { - type Item = BasicBlock; - type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>; -} - -impl<'tcx> graph::WithPredecessors for Body<'tcx> { - #[inline] - fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter { - self.predecessors()[node].iter().copied() - } -} - /// `Location` represents the position of the start of the statement; or, if /// `statement_index` equals the number of statements, then the start of the /// terminator. diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 30648679dae..627dc32f37e 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -104,22 +104,25 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { /// /// A Postorder traversal of this graph is `D B C A` or `D C B A` pub struct Postorder<'a, 'tcx> { - body: &'a Body<'tcx>, + basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>, visited: BitSet<BasicBlock>, visit_stack: Vec<(BasicBlock, Successors<'a>)>, root_is_start_block: bool, } impl<'a, 'tcx> Postorder<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> { + pub fn new( + basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>, + root: BasicBlock, + ) -> Postorder<'a, 'tcx> { let mut po = Postorder { - body, - visited: BitSet::new_empty(body.basic_blocks().len()), + basic_blocks, + visited: BitSet::new_empty(basic_blocks.len()), visit_stack: Vec::new(), root_is_start_block: root == START_BLOCK, }; - let data = &po.body[root]; + let data = &po.basic_blocks[root]; if let Some(ref term) = data.terminator { po.visited.insert(root); @@ -190,7 +193,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { }; if self.visited.insert(bb) { - if let Some(term) = &self.body[bb].terminator { + if let Some(term) = &self.basic_blocks[bb].terminator { self.visit_stack.push((bb, term.successors())); } } @@ -199,7 +202,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { } pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> { - Postorder::new(body, START_BLOCK) + Postorder::new(&body.basic_blocks, START_BLOCK) } impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> { @@ -211,12 +214,12 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> { self.traverse_successor(); } - next.map(|(bb, _)| (bb, &self.body[bb])) + next.map(|(bb, _)| (bb, &self.basic_blocks[bb])) } fn size_hint(&self) -> (usize, Option<usize>) { // All the blocks, minus the number of blocks we've visited. - let upper = self.body.basic_blocks().len() - self.visited.count(); + let upper = self.basic_blocks.len() - self.visited.count(); let lower = if self.root_is_start_block { // We will visit all remaining blocks exactly once. @@ -263,10 +266,8 @@ pub struct ReversePostorder<'a, 'tcx> { impl<'a, 'tcx> ReversePostorder<'a, 'tcx> { pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> { - let blocks: Vec<_> = Postorder::new(body, root).map(|(bb, _)| bb).collect(); - + let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect(); let len = blocks.len(); - ReversePostorder { body, blocks, idx: len } } } @@ -334,10 +335,8 @@ impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> { impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {} pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> { - let blocks = body.postorder_cache.compute(body); - + let blocks = body.basic_blocks.postorder(); let len = blocks.len(); - ReversePostorderIter { body, blocks, idx: len } } @@ -360,7 +359,7 @@ impl PostorderCache { /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR. #[inline] - pub(super) fn compute(&self, body: &Body<'_>) -> &[BasicBlock] { + pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] { self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect()) } } |
