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.
This commit is contained in:
Tomasz Miąsko 2022-07-04 00:00:00 +00:00
parent fac8fa5672
commit c9dd1d9983
21 changed files with 213 additions and 195 deletions

View File

@ -856,7 +856,8 @@ fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) ->
literal: ConstantKind::from_const(_const, tcx), literal: ConstantKind::from_const(_const, tcx),
})) }))
}; };
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); let blocks = self.source.basic_blocks.as_mut();
let local_decls = &mut self.source.local_decls;
let loc = candidate.location; let loc = candidate.location;
let statement = &mut blocks[loc.block].statements[loc.statement_index]; let statement = &mut blocks[loc.block].statements[loc.statement_index];
match statement.kind { match statement.kind {
@ -865,7 +866,7 @@ fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) ->
Rvalue::Ref(ref mut region, borrow_kind, ref mut place), Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
)) => { )) => {
// Use the underlying local for this (necessarily interior) borrow. // Use the underlying local for this (necessarily interior) borrow.
let ty = local_decls.local_decls()[place.local].ty; let ty = local_decls[place.local].ty;
let span = statement.source_info.span; let span = statement.source_info.span;
let ref_ty = tcx.mk_ref( let ref_ty = tcx.mk_ref(

View File

@ -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()
}
}

View File

@ -5,7 +5,6 @@
use crate::mir::interpret::{ use crate::mir::interpret::{
AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar, AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar,
}; };
use crate::mir::traversal::PostorderCache;
use crate::mir::visit::MirVisitable; use crate::mir::visit::MirVisitable;
use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
@ -28,7 +27,6 @@
pub use rustc_ast::Mutability; pub use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::graph::{self, GraphSuccessors};
use rustc_index::bit_set::BitMatrix; use rustc_index::bit_set::BitMatrix;
use rustc_index::vec::{Idx, IndexVec}; use rustc_index::vec::{Idx, IndexVec};
use rustc_serialize::{Decodable, Encodable}; use rustc_serialize::{Decodable, Encodable};
@ -43,11 +41,12 @@
use std::ops::{ControlFlow, Index, IndexMut}; use std::ops::{ControlFlow, Index, IndexMut};
use std::{iter, mem}; use std::{iter, mem};
use self::graph_cyclic_cache::GraphIsCyclicCache; use self::predecessors::Predecessors;
use self::predecessors::{PredecessorCache, Predecessors};
pub use self::query::*; 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; pub mod coverage;
mod generic_graph; mod generic_graph;
pub mod generic_graphviz; pub mod generic_graphviz;
@ -189,7 +188,7 @@ pub struct GeneratorInfo<'tcx> {
pub struct Body<'tcx> { pub struct Body<'tcx> {
/// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`] /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`]
/// that indexes into this vector. /// 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 /// Records how far through the "desugaring and optimization" process this particular
/// MIR has traversed. This is particularly useful when inlining, since in that context /// 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. /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this.
pub is_polymorphic: bool, pub is_polymorphic: bool,
predecessor_cache: PredecessorCache,
switch_source_cache: SwitchSourceCache,
is_cyclic: GraphIsCyclicCache,
postorder_cache: PostorderCache,
pub tainted_by_errors: Option<ErrorGuaranteed>, pub tainted_by_errors: Option<ErrorGuaranteed>,
} }
@ -289,7 +283,7 @@ pub fn new(
let mut body = Body { let mut body = Body {
phase: MirPhase::Built, phase: MirPhase::Built,
source, source,
basic_blocks, basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes, source_scopes,
generator: generator_kind.map(|generator_kind| { generator: generator_kind.map(|generator_kind| {
Box::new(GeneratorInfo { Box::new(GeneratorInfo {
@ -307,10 +301,6 @@ pub fn new(
span, span,
required_consts: Vec::new(), required_consts: Vec::new(),
is_polymorphic: false, is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
switch_source_cache: SwitchSourceCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
postorder_cache: PostorderCache::new(),
tainted_by_errors, tainted_by_errors,
}; };
body.is_polymorphic = body.has_param_types_or_consts(); body.is_polymorphic = body.has_param_types_or_consts();
@ -326,7 +316,7 @@ pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) ->
let mut body = Body { let mut body = Body {
phase: MirPhase::Built, phase: MirPhase::Built,
source: MirSource::item(CRATE_DEF_ID.to_def_id()), source: MirSource::item(CRATE_DEF_ID.to_def_id()),
basic_blocks, basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes: IndexVec::new(), source_scopes: IndexVec::new(),
generator: None, generator: None,
local_decls: IndexVec::new(), local_decls: IndexVec::new(),
@ -337,10 +327,6 @@ pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) ->
required_consts: Vec::new(), required_consts: Vec::new(),
var_debug_info: Vec::new(), var_debug_info: Vec::new(),
is_polymorphic: false, is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
switch_source_cache: SwitchSourceCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
postorder_cache: PostorderCache::new(),
tainted_by_errors: None, tainted_by_errors: None,
}; };
body.is_polymorphic = body.has_param_types_or_consts(); body.is_polymorphic = body.has_param_types_or_consts();
@ -354,74 +340,13 @@ pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
#[inline] #[inline]
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> { 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 self.basic_blocks.as_mut()
// 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();
} }
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
/// `START_BLOCK`. /// `START_BLOCK`.
pub fn is_cfg_cyclic(&self) -> bool { pub fn is_cfg_cyclic(&self) -> bool {
self.is_cyclic.is_cyclic(self) self.basic_blocks.is_cfg_cyclic()
} }
#[inline] #[inline]
@ -495,14 +420,6 @@ pub fn drain_vars_and_temps<'a>(&'a mut self) -> impl Iterator<Item = LocalDecl<
self.local_decls.drain(self.arg_count + 1..) 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`. /// Returns the source info associated with `location`.
pub fn source_info(&self, location: Location) -> &SourceInfo { pub fn source_info(&self, location: Location) -> &SourceInfo {
let block = &self[location.block]; let block = &self[location.block];
@ -540,19 +457,19 @@ pub fn stmt_at(&self, location: Location) -> Either<&Statement<'tcx>, &Terminato
#[inline] #[inline]
pub fn predecessors(&self) -> &Predecessors { 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 /// `body.switch_sources()[&(target, switch)]` returns a list of switch
/// values that lead to a `target` block from a `switch` block. /// values that lead to a `target` block from a `switch` block.
#[inline] #[inline]
pub fn switch_sources(&self) -> &SwitchSources { pub fn switch_sources(&self) -> &SwitchSources {
self.switch_source_cache.compute(&self.basic_blocks) self.basic_blocks.switch_sources()
} }
#[inline] #[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> { pub fn dominators(&self) -> Dominators<BasicBlock> {
dominators(self) dominators(&self.basic_blocks)
} }
#[inline] #[inline]
@ -599,7 +516,7 @@ fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> { impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
#[inline] #[inline]
fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { 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 /// `Location` represents the position of the start of the statement; or, if
/// `statement_index` equals the number of statements, then the start of the /// `statement_index` equals the number of statements, then the start of the
/// terminator. /// terminator.

View File

@ -104,22 +104,25 @@ fn size_hint(&self) -> (usize, Option<usize>) {
/// ///
/// A Postorder traversal of this graph is `D B C A` or `D C B A` /// A Postorder traversal of this graph is `D B C A` or `D C B A`
pub struct Postorder<'a, 'tcx> { pub struct Postorder<'a, 'tcx> {
body: &'a Body<'tcx>, basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>,
visited: BitSet<BasicBlock>, visited: BitSet<BasicBlock>,
visit_stack: Vec<(BasicBlock, Successors<'a>)>, visit_stack: Vec<(BasicBlock, Successors<'a>)>,
root_is_start_block: bool, root_is_start_block: bool,
} }
impl<'a, 'tcx> Postorder<'a, 'tcx> { 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 { let mut po = Postorder {
body, basic_blocks,
visited: BitSet::new_empty(body.basic_blocks().len()), visited: BitSet::new_empty(basic_blocks.len()),
visit_stack: Vec::new(), visit_stack: Vec::new(),
root_is_start_block: root == START_BLOCK, 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 { if let Some(ref term) = data.terminator {
po.visited.insert(root); po.visited.insert(root);
@ -190,7 +193,7 @@ fn traverse_successor(&mut self) {
}; };
if self.visited.insert(bb) { 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())); self.visit_stack.push((bb, term.successors()));
} }
} }
@ -199,7 +202,7 @@ fn traverse_successor(&mut self) {
} }
pub fn postorder<'a, 'tcx>(body: &'a Body<'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> { impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
@ -211,12 +214,12 @@ fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
self.traverse_successor(); 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>) { fn size_hint(&self) -> (usize, Option<usize>) {
// All the blocks, minus the number of blocks we've visited. // 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 { let lower = if self.root_is_start_block {
// We will visit all remaining blocks exactly once. // We will visit all remaining blocks exactly once.
@ -263,10 +266,8 @@ pub struct ReversePostorder<'a, 'tcx> {
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> { impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> 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(); let len = blocks.len();
ReversePostorder { body, blocks, idx: len } ReversePostorder { body, blocks, idx: len }
} }
} }
@ -334,10 +335,8 @@ fn size_hint(&self) -> (usize, Option<usize>) {
impl<'a, 'tcx> ExactSizeIterator 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> { 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(); let len = blocks.len();
ReversePostorderIter { body, blocks, idx: len } ReversePostorderIter { body, blocks, idx: len }
} }
@ -360,7 +359,7 @@ pub(super) fn invalidate(&mut self) {
/// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR. /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
#[inline] #[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()) self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
} }
} }

View File

@ -2,7 +2,7 @@
NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
}; };
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; use rustc_middle::mir::{BasicBlock, BasicBlocks, Body, Operand, TerminatorKind};
use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
@ -30,7 +30,9 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
}; };
let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs }; let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs };
if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { if let Some(NonRecursive) =
TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis)
{
return; return;
} }
if vis.reachable_recursive_calls.is_empty() { if vis.reachable_recursive_calls.is_empty() {
@ -101,7 +103,7 @@ fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> boo
} }
} }
impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
type BreakVal = NonRecursive; type BreakVal = NonRecursive;
fn node_examined( fn node_examined(

View File

@ -91,7 +91,8 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
super::add_call_guards::AllCallEdges.run_pass(tcx, body); super::add_call_guards::AllCallEdges.run_pass(tcx, body);
let (span, arg_count) = (body.span, body.arg_count); let (span, arg_count) = (body.span, body.arg_count);
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let basic_blocks = body.basic_blocks.as_mut();
let local_decls = &body.local_decls;
let needs_retag = |place: &Place<'tcx>| { let needs_retag = |place: &Place<'tcx>| {
// FIXME: Instead of giving up for unstable places, we should introduce // FIXME: Instead of giving up for unstable places, we should introduce
// a temporary and retag on that. // a temporary and retag on that.

View File

@ -80,7 +80,7 @@ fn compute_basic_coverage_blocks(
IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
IndexVec<BasicBlock, Option<BasicCoverageBlock>>, IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
) { ) {
let num_basic_blocks = mir_body.num_nodes(); let num_basic_blocks = mir_body.basic_blocks.len();
let mut bcbs = IndexVec::with_capacity(num_basic_blocks); let mut bcbs = IndexVec::with_capacity(num_basic_blocks);
let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);

View File

@ -321,7 +321,8 @@ pub(super) fn generate_coverage_spans(
} }
fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> { fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
let mut initial_spans = Vec::<CoverageSpan>::with_capacity(self.mir_body.num_nodes() * 2); let mut initial_spans =
Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2);
for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() {
initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data)); initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data));
} }

View File

@ -222,6 +222,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
bb, bb,
debug::term_type(&data.terminator().kind), debug::term_type(&data.terminator().kind),
mir_body mir_body
.basic_blocks
.successors(bb) .successors(bb)
.map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) .map(|successor| { format!(" {:?} -> {:?};", bb, successor) })
.join("\n") .join("\n")

View File

@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
return; return;
} }
let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0; let bbs = body.basic_blocks.as_mut_preserves_cfg();
for Location { block, statement_index } in patch { for Location { block, statement_index } in patch {
bbs[block].statements[statement_index].make_nop(); bbs[block].statements[statement_index].make_nop();
} }

View File

@ -11,9 +11,7 @@ fn phase_change(&self) -> Option<MirPhase> {
} }
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls, _) = let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
let local_decls = &*local_decls;
for bb in basic_blocks { for bb in basic_blocks {
bb.expand_statements(|stmt| { bb.expand_statements(|stmt| {
// FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL). // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
@ -38,7 +36,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
Some(expand_aggregate( Some(expand_aggregate(
lhs, lhs,
operands.into_iter().map(|op| { operands.into_iter().map(|op| {
let ty = op.ty(local_decls, tcx); let ty = op.ty(&body.local_decls, tcx);
(op, ty) (op, ty)
}), }),
*kind, *kind,

View File

@ -110,13 +110,13 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let patch = MirPatch::new(body); let patch = MirPatch::new(body);
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &mut body.local_decls;
let mut visitor = let mut visitor =
ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }; ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
for (block, BasicBlockData { statements, terminator, .. }) in for (block, BasicBlockData { statements, terminator, .. }) in
basic_blocks.iter_enumerated_mut() body.basic_blocks.as_mut().iter_enumerated_mut()
{ {
let mut index = 0; let mut index = 0;
for statement in statements { for statement in statements {

View File

@ -16,9 +16,8 @@ fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
} }
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let ctx = InstCombineContext { tcx, local_decls: &body.local_decls };
let ctx = InstCombineContext { tcx, local_decls }; for block in body.basic_blocks.as_mut() {
for block in basic_blocks.iter_mut() {
for statement in block.statements.iter_mut() { for statement in block.statements.iter_mut() {
match statement.kind { match statement.kind {
StatementKind::Assign(box (_place, ref mut rvalue)) => { StatementKind::Assign(box (_place, ref mut rvalue)) => {

View File

@ -11,8 +11,8 @@
impl<'tcx> MirPass<'tcx> for LowerIntrinsics { impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &body.local_decls;
for block in basic_blocks { for block in body.basic_blocks.as_mut() {
let terminator = block.terminator.as_mut().unwrap(); let terminator = block.terminator.as_mut().unwrap();
if let TerminatorKind::Call { func, args, destination, target, .. } = if let TerminatorKind::Call { func, args, destination, target, .. } =
&mut terminator.kind &mut terminator.kind

View File

@ -27,12 +27,10 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
}; };
// The one successor remains unchanged, so no need to invalidate // The one successor remains unchanged, so no need to invalidate
let (basic_blocks, local_decls, _) = let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
for block in basic_blocks { for block in basic_blocks {
// lower `<[_]>::len` calls // lower `<[_]>::len` calls
lower_slice_len_call(tcx, block, &*local_decls, slice_len_fn_item_def_id); lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id);
} }
} }

View File

@ -48,7 +48,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let def_id = body.source.def_id(); let def_id = body.source.def_id();
let param_env = tcx.param_env(def_id); let param_env = tcx.param_env(def_id);
let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut(); let bbs = body.basic_blocks.as_mut();
let mut should_cleanup = false; let mut should_cleanup = false;
'outer: for bb_idx in bbs.indices() { 'outer: for bb_idx in bbs.indices() {
if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) { if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) {
@ -108,7 +108,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// Introduce a temporary for the discriminant value. // Introduce a temporary for the discriminant value.
let source_info = bbs[bb_idx].terminator().source_info; let source_info = bbs[bb_idx].terminator().source_info;
let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span)); let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span));
// We already checked that first and second are different blocks, // We already checked that first and second are different blocks,
// and bb_idx has a different terminator from both of them. // and bb_idx has a different terminator from both of them.

View File

@ -33,8 +33,8 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// We don't ever touch terminators, so no need to invalidate the CFG cache // We don't ever touch terminators, so no need to invalidate the CFG cache
let (basic_blocks, local_decls, _) = let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); let local_decls = &mut body.local_decls;
// do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]` // do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]`
let mut interesting_locals = BitSet::new_empty(local_decls.len()); let mut interesting_locals = BitSet::new_empty(local_decls.len());

View File

@ -17,7 +17,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
} }
trace!("Running RemoveStorageMarkers on {:?}", body.source); trace!("Running RemoveStorageMarkers on {:?}", body.source);
for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 { for data in body.basic_blocks.as_mut_preserves_cfg() {
data.statements.retain(|statement| match statement.kind { data.statements.retain(|statement| match statement.kind {
StatementKind::StorageLive(..) StatementKind::StorageLive(..)
| StatementKind::StorageDead(..) | StatementKind::StorageDead(..)

View File

@ -20,11 +20,10 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let param_env = tcx.param_env_reveal_all_normalized(did); let param_env = tcx.param_env_reveal_all_normalized(did);
let mut should_simplify = false; let mut should_simplify = false;
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); for block in body.basic_blocks.as_mut() {
for block in basic_blocks {
let terminator = block.terminator_mut(); let terminator = block.terminator_mut();
if let TerminatorKind::Drop { place, target, .. } = terminator.kind { if let TerminatorKind::Drop { place, target, .. } = terminator.kind {
let ty = place.ty(local_decls, tcx); let ty = place.ty(&body.local_decls, tcx);
if ty.ty.needs_drop(tcx, param_env) { if ty.ty.needs_drop(tcx, param_env) {
continue; continue;
} }

View File

@ -18,9 +18,9 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
return; return;
} }
let param_env = tcx.param_env(body.source.def_id()); let param_env = tcx.param_env(body.source.def_id());
let (basic_blocks, local_decls, _) = let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); let local_decls = &body.local_decls;
for block in basic_blocks.iter_mut() { for block in basic_blocks {
for statement in block.statements.iter_mut() { for statement in block.statements.iter_mut() {
if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) = if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) =
statement.kind statement.kind

View File

@ -386,14 +386,17 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
trace!("running SimplifyArmIdentity on {:?}", source); trace!("running SimplifyArmIdentity on {:?}", source);
let local_uses = LocalUseCounter::get_local_uses(body); let local_uses = LocalUseCounter::get_local_uses(body);
let (basic_blocks, local_decls, debug_info) = for bb in body.basic_blocks.as_mut() {
body.basic_blocks_local_decls_mut_and_var_debug_info();
for bb in basic_blocks {
if let Some(opt_info) = if let Some(opt_info) =
get_arm_identity_info(&bb.statements, local_decls.len(), debug_info) get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info)
{ {
trace!("got opt_info = {:#?}", opt_info); trace!("got opt_info = {:#?}", opt_info);
if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) { if !optimization_applies(
&opt_info,
&body.local_decls,
&local_uses,
&body.var_debug_info,
) {
debug!("optimization skipped for {:?}", source); debug!("optimization skipped for {:?}", source);
continue; continue;
} }
@ -431,7 +434,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// Fix the debug info to point to the right local // Fix the debug info to point to the right local
for dbg_index in opt_info.dbg_info_to_adjust { for dbg_index in opt_info.dbg_info_to_adjust {
let dbg_info = &mut debug_info[dbg_index]; let dbg_info = &mut body.var_debug_info[dbg_index];
assert!( assert!(
matches!(dbg_info.value, VarDebugInfoContents::Place(_)), matches!(dbg_info.value, VarDebugInfoContents::Place(_)),
"value was not a Place" "value was not a Place"