diff options
| author | bors <bors@rust-lang.org> | 2025-10-03 11:49:42 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-10-03 11:49:42 +0000 |
| commit | 8b6b15b877fbceb1ee5d9a5a4746e7515901574a (patch) | |
| tree | 0301428b36812cac6a0647b40ce4be58757e4226 /compiler/rustc_mir_transform | |
| parent | dd091003ace19d9556c647d87f7a9cd1e8dcc17e (diff) | |
| parent | c2a03cefd8899941032940df0c6be3b364de0ed0 (diff) | |
| download | rust-8b6b15b877fbceb1ee5d9a5a4746e7515901574a.tar.gz rust-8b6b15b877fbceb1ee5d9a5a4746e7515901574a.zip | |
Auto merge of #142771 - dianqk:mir-stmt-debuginfo, r=cjgillot
Introduce debuginfo to statements in MIR The PR introduces support for debug information within dead statements. Currently, only the reference statement is supported, which is sufficient to fix rust-lang/rust#128081. I don't modify Stable MIR, as I don't think we need debug information when using it. This PR represents the debug information for the dead reference statement via `#dbg_value`. For example, `let _foo_b = &foo.b` becomes `#dbg_value(ptr %foo, !22, !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value), !26)`. You can see this here: https://rust.godbolt.org/z/d43js6adv. The general principle for handling debug information is to never provide less debug information than the optimized LLVM IR. The current rules for dropping debug information in this PR are: - If the LLVM IR cannot represent a reference address, it's replaced with poison or simply dropped. For example, see: https://rust.godbolt.org/z/shGqPec8W. I'm using poison in all such cases now. - All debuginfos is dropped when merging multiple successor BBs. An example is available here: https://rust.godbolt.org/z/TE1q3Wq6M. I doesn't drop debuginfos in `MatchBranchSimplification`, because LLVM also pick one branch for it.
Diffstat (limited to 'compiler/rustc_mir_transform')
18 files changed, 204 insertions, 91 deletions
diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 4be67b873f7..b0bf7f484be 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) | StatementKind::FakeRead(..) - | StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(), + | StatementKind::BackwardIncompatibleDropHint { .. } => { + statement.make_nop(true) + } StatementKind::Assign(box ( _, Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index cddeefca681..f0bc286a940 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -138,7 +138,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind && self.storage_to_remove.contains(l) { - stmt.make_nop(); + stmt.make_nop(true); return; } @@ -150,7 +150,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { *rhs && lhs == rhs { - stmt.make_nop(); + stmt.make_nop(true); } } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c5cd06f170c..814eded910d 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -411,7 +411,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind && self.remap.contains(l) { - s.make_nop(); + s.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index eea2b0990d7..732c3dcd44a 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -22,21 +22,22 @@ use rustc_mir_dataflow::impls::{ LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals, }; +use crate::simplify::UsedInStmtLocals; use crate::util::is_within_packed; /// Performs the optimization on the body /// /// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this /// body. It can be generated via the [`borrowed_locals`] function. -fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +/// Returns true if any instruction is eliminated. +fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let borrowed_locals = borrowed_locals(body); // If the user requests complete debuginfo, mark the locals that appear in it as live, so // we don't remove assignments to them. - let mut always_live = debuginfo_locals(body); - always_live.union(&borrowed_locals); + let debuginfo_locals = debuginfo_locals(body); - let mut live = MaybeTransitiveLiveLocals::new(&always_live) + let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -75,47 +76,36 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { - let loc = Location { block: bb, statement_index }; - if let StatementKind::Assign(assign) = &statement.kind { - if !assign.1.is_safe_to_remove() { - continue; - } - } - match &statement.kind { - StatementKind::Assign(box (place, _)) - | StatementKind::SetDiscriminant { place: box place, .. } - | StatementKind::Deinit(box place) => { - if !place.is_indirect() && !always_live.contains(place.local) { - live.seek_before_primary_effect(loc); - if !live.get().contains(place.local) { - patch.push(loc); - } - } - } - StatementKind::Retag(_, _) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Coverage(_) - | StatementKind::Intrinsic(_) - | StatementKind::ConstEvalCounter - | StatementKind::PlaceMention(_) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => {} - - StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { - bug!("{:?} not found in this MIR phase!", statement.kind) + if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead( + &statement.kind, + &borrowed_locals, + &debuginfo_locals, + ) { + let loc = Location { block: bb, statement_index }; + live.seek_before_primary_effect(loc); + if !live.get().contains(destination.local) { + let drop_debuginfo = !debuginfo_locals.contains(destination.local); + // When eliminating a dead statement, we need to address + // the debug information for that statement. + assert!( + drop_debuginfo || statement.kind.as_debuginfo().is_some(), + "don't know how to retain the debug information for {:?}", + statement.kind + ); + patch.push((loc, drop_debuginfo)); } } } } if patch.is_empty() && call_operands_to_move.is_empty() { - return; + return false; } + let eliminated = !patch.is_empty(); let bbs = body.basic_blocks.as_mut_preserves_cfg(); - for Location { block, statement_index } in patch { - bbs[block].statements[statement_index].make_nop(); + for (Location { block, statement_index }, drop_debuginfo) in patch { + bbs[block].statements[statement_index].make_nop(drop_debuginfo); } for (block, argument_index) in call_operands_to_move { let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { @@ -125,6 +115,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let Operand::Copy(place) = *arg else { bug!() }; *arg = Operand::Move(place); } + + eliminated } pub(super) enum DeadStoreElimination { @@ -145,7 +137,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - eliminate(tcx, body); + if eliminate(tcx, body) { + UsedInStmtLocals::new(body).remove_unused_storage_annotations(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + data.strip_nops(); + } + } } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 74c22ff10c1..1f38433fa5a 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -276,7 +276,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { StatementKind::StorageDead(local) | StatementKind::StorageLive(local) if self.merged_locals.contains(*local) => { - statement.make_nop(); + statement.make_nop(true); return; } _ => (), @@ -291,7 +291,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { // (this includes the original statement we wanted to eliminate). if dest == place { debug!("{:?} turned into self-assignment, deleting", location); - statement.make_nop(); + statement.make_nop(true); } } _ => {} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 99691d9e045..3ff8dc6dbb3 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1877,7 +1877,7 @@ impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.reused_locals.contains(l) => { - stmt.make_nop() + stmt.make_nop(true) } _ => self.super_statement(stmt, loc), } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 3d49eb4e8ef..8593e25d6aa 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::{CostChecker, is_call_like}; use crate::deref_separator::deref_finder; -use crate::simplify::simplify_cfg; +use crate::simplify::{UsedInStmtLocals, simplify_cfg}; use crate::validate::validate_types; use crate::{check_inline, util}; @@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( in_cleanup_block: false, return_block, tcx, - always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()), + always_live_locals: UsedInStmtLocals::new(&callee_body).locals, }; // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones @@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // people working on rust can build with or without debuginfo while // still getting consistent results from the mir-opt tests. caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } else { + for bb in callee_body.basic_blocks_mut() { + bb.drop_debuginfo(); + } } caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 1a91d6bd7da..1b90e9158f6 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -156,7 +156,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { patch.add_statement(location, stmt); } - st.make_nop(); + st.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index cc8ea76011b..2c535d011a0 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -270,7 +270,7 @@ impl<'tcx> MirPatch<'tcx> { body.local_decls.extend(self.new_locals); for loc in self.nop_statements { - bbs[loc.block].statements[loc.statement_index].make_nop(); + bbs[loc.block].statements[loc.statement_index].make_nop(true); } let mut new_statements = self.new_statements; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 48ddf5a1bca..c7dc18a4a13 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -1049,7 +1049,7 @@ fn promote_candidates<'tcx>( // Eliminate assignments to, and drops of promoted temps. let promoted = |index: Local| temps[index] == TempState::PromotedOut; for block in body.basic_blocks_mut() { - block.statements.retain(|statement| match &statement.kind { + block.retain_statements(|statement| match &statement.kind { StatementKind::Assign(box (place, _)) => { if let Some(index) = place.as_local() { !promoted(index) diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index b9d6e74ecae..deb0a146476 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -435,7 +435,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.storage_to_remove.contains(l) => { - stmt.make_nop(); + stmt.make_nop(true); } // Do not remove assignments as they may still be useful for debuginfo. _ => self.super_statement(stmt, loc), diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index cb598ceb4df..d56b51bb496 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemovePlaceMention on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::PlaceMention(..) | StatementKind::Nop => false, _ => true, }) diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 1ae33c00968..cb97d2c865a 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveStorageMarkers on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Nop => false, diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index c4dc8638b26..90c1b3520b9 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -141,7 +141,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty && self.known_to_be_zst(ty) { - statement.make_nop(); + statement.make_nop(true); } else { self.super_statement(statement, loc); } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 75917d23883..8b5efb74205 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -35,10 +35,12 @@ //! pre-"runtime" MIR! use itertools::Itertools as _; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_span::DUMMY_SP; use smallvec::SmallVec; use tracing::{debug, trace}; @@ -142,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // statements itself to avoid moving the (relatively) large statements twice. // We do not push the statements directly into the target block (`bb`) as that is slower // due to additional reallocations - let mut merged_blocks = Vec::new(); + let mut merged_blocks: Vec<BasicBlock> = Vec::new(); let mut outer_changed = false; loop { let mut changed = false; @@ -157,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { let mut terminator = self.basic_blocks[bb].terminator.take().expect("invalid terminator state"); - terminator - .successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed)); + terminator.successors_mut(|successor| { + self.collapse_goto_chain(successor, &mut changed); + }); let mut inner_changed = true; merged_blocks.clear(); @@ -175,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { if statements_to_merge > 0 { let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); statements.reserve(statements_to_merge); + let mut parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos); for &from in &merged_blocks { + if let Some(stmt) = self.basic_blocks[from].statements.first_mut() { + stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos); + } statements.append(&mut self.basic_blocks[from].statements); + parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos); } self.basic_blocks[bb].statements = statements; + self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos; } self.basic_blocks[bb].terminator = Some(terminator); @@ -218,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // goto chains. We should probably benchmark different sizes. let mut terminators: SmallVec<[_; 1]> = Default::default(); let mut current = *start; + // If each successor has only one predecessor, it's a trivial goto chain. + // We can move all debuginfos to the last basic block. + let mut trivial_goto_chain = true; while let Some(terminator) = self.take_terminator_if_simple_goto(current) { let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else { unreachable!(); }; + trivial_goto_chain &= self.pred_count[target] == 1; terminators.push((current, terminator)); current = target; } @@ -233,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { else { unreachable!(); }; + if trivial_goto_chain { + let mut pred_debuginfos = + std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos); + let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut() + { + &mut stmt.debuginfos + } else { + &mut self.basic_blocks[last].after_last_stmt_debuginfos + }; + debuginfos.prepend(&mut pred_debuginfos); + } *changed |= *target != last; *target = last; debug!("collapsing goto chain from {:?} to {:?}", current, target); @@ -303,7 +329,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + blk.strip_nops(); } } } @@ -476,17 +502,22 @@ fn make_local_map<V>( /// Keeps track of used & unused locals. struct UsedLocals { increment: bool, - arg_count: u32, use_count: IndexVec<Local, u32>, + always_used: DenseBitSet<Local>, } impl UsedLocals { /// Determines which locals are used & unused in the given body. fn new(body: &Body<'_>) -> Self { + let mut always_used = debuginfo_locals(body); + always_used.insert(RETURN_PLACE); + for arg in body.args_iter() { + always_used.insert(arg); + } let mut this = Self { increment: true, - arg_count: body.arg_count.try_into().unwrap(), use_count: IndexVec::from_elem(0, &body.local_decls), + always_used, }; this.visit_body(body); this @@ -494,10 +525,16 @@ impl UsedLocals { /// Checks if local is used. /// - /// Return place and arguments are always considered used. + /// Return place, arguments, var debuginfo are always considered used. fn is_used(&self, local: Local) -> bool { - trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]); - local.as_u32() <= self.arg_count || self.use_count[local] != 0 + trace!( + "is_used({:?}): use_count: {:?}, always_used: {}", + local, + self.use_count[local], + self.always_used.contains(local) + ); + // To keep things simple, we don't handle debugging information here, these are in DSE. + self.always_used.contains(local) || self.use_count[local] != 0 } /// Updates the use counts to reflect the removal of given statement. @@ -539,10 +576,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { self.super_statement(statement, location); } - StatementKind::ConstEvalCounter | StatementKind::Nop => {} - - StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} - + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) => {} StatementKind::Assign(box (ref place, ref rvalue)) => { if rvalue.is_safe_to_remove() { self.visit_lhs(place, location); @@ -560,7 +597,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { } } - fn visit_local(&mut self, local: Local, _ctx: PlaceContext, _location: Location) { + fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) { + if matches!(ctx, PlaceContext::NonUse(_)) { + return; + } if self.increment { self.use_count[local] += 1; } else { @@ -583,28 +623,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod for data in body.basic_blocks.as_mut_preserves_cfg() { // Remove unnecessary StorageLive and StorageDead annotations. - data.statements.retain(|statement| { - let keep = match &statement.kind { + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { used_locals.is_used(*local) } - StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local), - - StatementKind::SetDiscriminant { place, .. } - | StatementKind::BackwardIncompatibleDropHint { place, reason: _ } - | StatementKind::Deinit(place) => used_locals.is_used(place.local), - StatementKind::Nop => false, - _ => true, + StatementKind::Assign(box (place, _)) + | StatementKind::SetDiscriminant { box place, .. } + | StatementKind::BackwardIncompatibleDropHint { box place, .. } + | StatementKind::Deinit(box place) => used_locals.is_used(place.local), + _ => continue, }; - - if !keep { - trace!("removing statement {:?}", statement); - modified = true; - used_locals.statement_removed(statement); + if keep_statement { + continue; } - - keep - }); + trace!("removing statement {:?}", statement); + modified = true; + used_locals.statement_removed(statement); + statement.make_nop(true); + } + data.strip_nops(); } } } @@ -619,7 +657,62 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { self.tcx } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &mut StmtDebugInfo<'tcx>, + location: Location, + ) { + match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, place) => { + if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) { + *stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local); + } + } + StmtDebugInfo::InvalidAssign(_) => {} + } + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { *l = self.map[*l].unwrap(); } } + +pub(crate) struct UsedInStmtLocals { + pub(crate) locals: DenseBitSet<Local>, +} + +impl UsedInStmtLocals { + pub(crate) fn new(body: &Body<'_>) -> Self { + let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) }; + this.visit_body(body); + this + } + + pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) { + for data in body.basic_blocks.as_mut_preserves_cfg() { + // Remove unnecessary StorageLive and StorageDead annotations. + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + self.locals.contains(*local) + } + _ => continue, + }; + if keep_statement { + continue; + } + statement.make_nop(true); + } + } + } +} + +impl<'tcx> Visitor<'tcx> for UsedInStmtLocals { + fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { + if matches!(context, PlaceContext::NonUse(_)) { + return; + } + self.locals.insert(local); + } +} diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index c60eb566521..4597439e269 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { // delete comparison statement if it the value being switched on was moved, which means // it can not be user later on if opt.can_remove_bin_op_stmt { - bb.statements[opt.bin_op_stmt_idx].make_nop(); + bb.statements[opt.bin_op_stmt_idx].make_nop(true); } else { // if the integer being compared to a const integral is being moved into the // comparison, e.g `_2 = Eq(move _3, const 'x');` @@ -136,7 +136,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { } for (idx, bb_idx) in storage_deads_to_remove { - body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(); + body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(true); } for (idx, stmt) in storage_deads_to_insert { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 38769885f36..99f10b8d91d 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -318,7 +318,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageLive(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -327,7 +327,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageDead(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -337,7 +337,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { self.patch .add_statement(location, StatementKind::Deinit(Box::new(fl.into()))); } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -367,7 +367,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { ); } } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -429,7 +429,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { StatementKind::Assign(Box::new((new_local.into(), rvalue))), ); } - statement.make_nop(); + statement.make_nop(true); return; } } diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 9ede8aa79c4..7fec25ccb52 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -1,5 +1,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_session::config::MirStripDebugInfo; /// Conditionally remove some of the VarDebugInfo in MIR. @@ -30,6 +31,22 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE, ) }); + + let debuginfo_locals = debuginfo_locals(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + for stmt in data.statements.iter_mut() { + stmt.debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } + data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } } fn is_required(&self) -> bool { |
