Skip to content

Add method to mutate MIR body without invalidating CFG caches. #98872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,21 +357,15 @@ impl<'tcx> Body<'tcx> {
//
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
// invalidate the caches.
self.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
self.is_cyclic.invalidate();
self.postorder_cache.invalidate();
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.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
self.is_cyclic.invalidate();
self.postorder_cache.invalidate();
self.invalidate_cfg_cache();
(&mut self.basic_blocks, &mut self.local_decls)
}

Expand All @@ -383,11 +377,43 @@ impl<'tcx> Body<'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();
(&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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/dead_store_elimination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
return;
}

let bbs = body.basic_blocks_mut();
let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0;
for Location { block, statement_index } in patch {
bbs[block].statements[statement_index].make_nop();
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_transform/src/deaggregator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
}

fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
let (basic_blocks, local_decls, _) =
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
let local_decls = &*local_decls;
for bb in basic_blocks {
bb.expand_statements(|stmt| {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_transform/src/lower_slice_len.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
return;
};

let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
// The one successor remains unchanged, so no need to invalidate
let (basic_blocks, local_decls, _) =
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();

for block in basic_blocks {
// lower `<[_]>::len` calls
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_transform/src/normalize_array_len.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
}

pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
// We don't ever touch terminators, so no need to invalidate the CFG cache
let (basic_blocks, local_decls, _) =
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();

// 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());
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/remove_storage_markers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers {
}

trace!("Running RemoveStorageMarkers on {:?}", body.source);
for data in body.basic_blocks_mut() {
for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 {
data.statements.retain(|statement| match statement.kind {
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_transform/src/remove_zsts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
return;
}
let param_env = tcx.param_env(body.source.def_id());
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
let (basic_blocks, local_decls, _) =
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
for block in basic_blocks.iter_mut() {
for statement in block.statements.iter_mut() {
if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) =
Expand Down