Skip to content

Commit 9e585d4

Browse files
committed
mir-opt: Eliminate trivial unnecessary storage annotations
1 parent 8625fa4 commit 9e585d4

File tree

36 files changed

+196
-368
lines changed

36 files changed

+196
-368
lines changed

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,17 @@ impl<'tcx> BasicBlockData<'tcx> {
14441444
self.after_last_stmt_debuginfos.append(&mut debuginfos);
14451445
}
14461446
}
1447+
1448+
pub fn strip_nops(&mut self) {
1449+
self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
1450+
}
1451+
1452+
pub fn drop_debuginfo(&mut self) {
1453+
self.after_last_stmt_debuginfos = Vec::new();
1454+
for stmt in self.statements.iter_mut() {
1455+
stmt.debuginfos = Vec::new();
1456+
}
1457+
}
14471458
}
14481459

14491460
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,10 @@ impl<'tcx> Statement<'tcx> {
2727
}
2828
let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop);
2929
if !drop_debuginfo {
30-
match replaced_stmt {
31-
StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place)))
32-
if let Some(local) = place.as_local() =>
33-
{
34-
self.debuginfos.push(StmtDebugInfo::AssignRef(local, Some(ref_place)));
35-
}
36-
_ => {
37-
bug!("debuginfo is not yet supported.")
38-
}
39-
}
30+
let Some(debuginfo) = replaced_stmt.as_debuginfo() else {
31+
bug!("debuginfo is not yet supported.")
32+
};
33+
self.debuginfos.push(debuginfo);
4034
}
4135
}
4236

compiler/rustc_mir_dataflow/src/impls/liveness.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
228228
// Compute the place that we are storing to, if any
229229
let destination = match stmt_kind {
230230
StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove()
231+
// FIXME: We are not sure how we should represent this debugging information for some statements,
232+
// keep it for now.
231233
&& (!debuginfo_locals.contains(place.local)
232-
|| (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..)))))
234+
|| (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some())))
233235
.then_some(*place),
234236
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
235237
(!debuginfo_locals.contains(place.local)).then_some(**place)

compiler/rustc_mir_transform/src/dead_store_elimination.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ use rustc_mir_dataflow::impls::{
2222
LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals,
2323
};
2424

25+
use crate::simplify::UsedInStmtLocals;
2526
use crate::util::is_within_packed;
2627

2728
/// Performs the optimization on the body
2829
///
2930
/// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this
3031
/// body. It can be generated via the [`borrowed_locals`] function.
31-
fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
32+
fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
3233
let borrowed_locals = borrowed_locals(body);
3334

3435
// If the user requests complete debuginfo, mark the locals that appear in it as live, so
@@ -89,8 +90,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
8990
}
9091

9192
if patch.is_empty() && call_operands_to_move.is_empty() {
92-
return;
93+
return false;
9394
}
95+
let eliminated = !patch.is_empty();
9496

9597
let bbs = body.basic_blocks.as_mut_preserves_cfg();
9698
for (Location { block, statement_index }, drop_debuginfo) in patch {
@@ -104,6 +106,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
104106
let Operand::Copy(place) = *arg else { bug!() };
105107
*arg = Operand::Move(place);
106108
}
109+
110+
eliminated
107111
}
108112

109113
pub(super) enum DeadStoreElimination {
@@ -124,7 +128,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
124128
}
125129

126130
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
127-
eliminate(tcx, body);
131+
if eliminate(tcx, body) {
132+
UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
133+
for data in body.basic_blocks.as_mut_preserves_cfg() {
134+
data.strip_nops();
135+
}
136+
}
128137
}
129138

130139
fn is_required(&self) -> bool {

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span};
2121

2222
use crate::cost_checker::{CostChecker, is_call_like};
2323
use crate::deref_separator::deref_finder;
24-
use crate::simplify::simplify_cfg;
24+
use crate::simplify::{UsedInStmtLocals, simplify_cfg};
2525
use crate::validate::validate_types;
2626
use crate::{check_inline, util};
2727

@@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
935935
in_cleanup_block: false,
936936
return_block,
937937
tcx,
938-
always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()),
938+
always_live_locals: UsedInStmtLocals::new(&callee_body).locals,
939939
};
940940

941941
// Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones
@@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
995995
// people working on rust can build with or without debuginfo while
996996
// still getting consistent results from the mir-opt tests.
997997
caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
998+
} else {
999+
for bb in callee_body.basic_blocks_mut() {
1000+
bb.drop_debuginfo();
1001+
}
9981002
}
9991003
caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut());
10001004

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
//! The normal logic that a program with UB can be changed to do anything does not apply to
3535
//! pre-"runtime" MIR!
3636
37+
use rustc_index::bit_set::DenseBitSet;
3738
use rustc_index::{Idx, IndexSlice, IndexVec};
38-
use rustc_middle::mir::visit::{
39-
MutVisitor, MutatingUseContext, NonUseContext, PlaceContext, Visitor,
40-
};
39+
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
4140
use rustc_middle::mir::*;
4241
use rustc_middle::ty::TyCtxt;
42+
use rustc_mir_dataflow::debuginfo::debuginfo_locals;
4343
use rustc_span::DUMMY_SP;
4444
use smallvec::SmallVec;
4545
use tracing::{debug, trace};
@@ -344,7 +344,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
344344

345345
fn strip_nops(&mut self) {
346346
for blk in self.basic_blocks.iter_mut() {
347-
blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
347+
blk.strip_nops();
348348
}
349349
}
350350
}
@@ -517,28 +517,39 @@ fn make_local_map<V>(
517517
/// Keeps track of used & unused locals.
518518
struct UsedLocals {
519519
increment: bool,
520-
arg_count: u32,
521520
use_count: IndexVec<Local, u32>,
521+
always_used: DenseBitSet<Local>,
522522
}
523523

524524
impl UsedLocals {
525525
/// Determines which locals are used & unused in the given body.
526526
fn new(body: &Body<'_>) -> Self {
527+
let mut always_used = debuginfo_locals(body);
528+
always_used.insert(RETURN_PLACE);
529+
for arg in body.args_iter() {
530+
always_used.insert(arg);
531+
}
527532
let mut this = Self {
528533
increment: true,
529-
arg_count: body.arg_count.try_into().unwrap(),
530534
use_count: IndexVec::from_elem(0, &body.local_decls),
535+
always_used,
531536
};
532537
this.visit_body(body);
533538
this
534539
}
535540

536541
/// Checks if local is used.
537542
///
538-
/// Return place and arguments are always considered used.
543+
/// Return place, arguments, var debuginfo are always considered used.
539544
fn is_used(&self, local: Local) -> bool {
540-
trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]);
541-
local.as_u32() <= self.arg_count || self.use_count[local] != 0
545+
trace!(
546+
"is_used({:?}): use_count: {:?}, always_used: {}",
547+
local,
548+
self.use_count[local],
549+
self.always_used.contains(local)
550+
);
551+
// To keep things simple, we don't handle debugging information here, these are in DSE.
552+
self.always_used.contains(local) || self.use_count[local] != 0
542553
}
543554

544555
/// Updates the use counts to reflect the removal of given statement.
@@ -583,13 +594,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
583594
StatementKind::ConstEvalCounter
584595
| StatementKind::Nop
585596
| StatementKind::StorageLive(..)
586-
| StatementKind::StorageDead(..) => {
587-
self.visit_statement_debuginfos(&statement.debuginfos, ___location);
588-
}
597+
| StatementKind::StorageDead(..) => {}
589598

590599
StatementKind::Assign(box (ref place, ref rvalue)) => {
591600
if rvalue.is_safe_to_remove() {
592-
self.visit_statement_debuginfos(&statement.debuginfos, ___location);
593601
self.visit_lhs(place, ___location);
594602
self.visit_rvalue(rvalue, ___location);
595603
} else {
@@ -600,16 +608,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
600608
StatementKind::SetDiscriminant { ref place, variant_index: _ }
601609
| StatementKind::Deinit(ref place)
602610
| StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => {
603-
self.visit_statement_debuginfos(&statement.debuginfos, ___location);
604611
self.visit_lhs(place, ___location);
605612
}
606613
}
607614
}
608615

609616
fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) {
617+
if matches!(ctx, PlaceContext::NonUse(_)) {
618+
return;
619+
}
610620
if self.increment {
611621
self.use_count[local] += 1;
612-
} else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
622+
} else {
613623
assert_ne!(self.use_count[local], 0);
614624
self.use_count[local] -= 1;
615625
}
@@ -629,28 +639,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
629639

630640
for data in body.basic_blocks.as_mut_preserves_cfg() {
631641
// Remove unnecessary StorageLive and StorageDead annotations.
632-
data.retain_statements(|statement| {
633-
let keep = match &statement.kind {
642+
for statement in data.statements.iter_mut() {
643+
let keep_statement = match &statement.kind {
634644
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
635645
used_locals.is_used(*local)
636646
}
637-
StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local),
638-
639-
StatementKind::SetDiscriminant { place, .. }
640-
| StatementKind::BackwardIncompatibleDropHint { place, reason: _ }
641-
| StatementKind::Deinit(place) => used_locals.is_used(place.local),
642-
StatementKind::Nop => false,
643-
_ => true,
647+
StatementKind::Assign(box (place, _))
648+
| StatementKind::SetDiscriminant { box place, .. }
649+
| StatementKind::BackwardIncompatibleDropHint { box place, .. }
650+
| StatementKind::Deinit(box place) => used_locals.is_used(place.local),
651+
_ => continue,
644652
};
645-
646-
if !keep {
647-
trace!("removing statement {:?}", statement);
648-
modified = true;
649-
used_locals.statement_removed(statement);
653+
if keep_statement {
654+
continue;
650655
}
651-
652-
keep
653-
});
656+
trace!("removing statement {:?}", statement);
657+
modified = true;
658+
used_locals.statement_removed(statement);
659+
statement.make_nop(true);
660+
}
661+
data.strip_nops();
654662
}
655663
}
656664
}
@@ -705,3 +713,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
705713
*l = self.map[*l].unwrap();
706714
}
707715
}
716+
717+
pub(crate) struct UsedInStmtLocals {
718+
pub(crate) locals: DenseBitSet<Local>,
719+
}
720+
721+
impl UsedInStmtLocals {
722+
pub(crate) fn new(body: &Body<'_>) -> Self {
723+
let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) };
724+
this.visit_body(body);
725+
this
726+
}
727+
728+
pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) {
729+
for data in body.basic_blocks.as_mut_preserves_cfg() {
730+
// Remove unnecessary StorageLive and StorageDead annotations.
731+
for statement in data.statements.iter_mut() {
732+
let keep_statement = match &statement.kind {
733+
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
734+
self.locals.contains(*local)
735+
}
736+
_ => continue,
737+
};
738+
if keep_statement {
739+
continue;
740+
}
741+
statement.make_nop(true);
742+
}
743+
}
744+
}
745+
}
746+
747+
impl<'tcx> Visitor<'tcx> for UsedInStmtLocals {
748+
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
749+
if matches!(context, PlaceContext::NonUse(_)) {
750+
return;
751+
}
752+
self.locals.insert(local);
753+
}
754+
}

tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
- _3 = copy _2;
2020
- _2 = copy _1;
2121
- _1 = copy _5;
22-
+ nop;
23-
+ nop;
24-
+ nop;
25-
+ nop;
2622
_4 = cond() -> [return: bb1, unwind continue];
2723
}
2824

tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@
1212
}
1313

1414
bb0: {
15-
StorageLive(_2);
15+
- StorageLive(_2);
1616
- _3 = deref_copy (_1.1: &Foo);
1717
- _2 = &((*_3).2: i32);
18-
+ nop;
1918
+ // DBG: _2 = &((*_3).2: i32)
20-
+ nop;
2119
_4 = deref_copy (_1.1: &Foo);
2220
_0 = copy ((*_4).0: i32);
23-
StorageDead(_2);
21+
- StorageDead(_2);
2422
return;
2523
}
2624
}

tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
-
1515
- bb1: {
1616
- // DBG: _3 = &((*_1).0: i32)
17-
- nop;
1817
- goto -> bb2;
1918
- }
2019
-
2120
- bb2: {
2221
// DBG: _4 = &((*_1).1: i64)
23-
- nop;
2422
_0 = copy ((*_1).2: i32);
2523
return;
2624
}

tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
- bb1: {
1818
(*_2) = const true;
1919
// DBG: _3 = &((*_1).0: i32)
20-
- nop;
2120
- goto -> bb2;
2221
- }
2322
-
2423
- bb2: {
2524
// DBG: _4 = &((*_1).1: i64)
26-
- nop;
2725
_0 = copy ((*_1).2: i32);
2826
// DBG: _5 = &((*_1).2: i32)
29-
- nop;
3027
return;
3128
}
3229
}

0 commit comments

Comments
 (0)