Skip to content

Commit 99c8049

Browse files
committed
mir-opt: Eliminate trivial unnecessary storage annotations
1 parent e2c0c67 commit 99c8049

File tree

36 files changed

+178
-388
lines changed

36 files changed

+178
-388
lines changed

compiler/rustc_middle/src/mir/statement.rs

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

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
@@ -97,8 +98,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
9798
}
9899

99100
if patch.is_empty() && call_operands_to_move.is_empty() {
100-
return;
101+
return false;
101102
}
103+
let eliminated = !patch.is_empty();
102104

103105
let bbs = body.basic_blocks.as_mut_preserves_cfg();
104106
for (Location { block, statement_index }, drop_debuginfo) in patch {
@@ -112,6 +114,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
112114
let Operand::Copy(place) = *arg else { bug!() };
113115
*arg = Operand::Move(place);
114116
}
117+
118+
eliminated
115119
}
116120

117121
pub(super) enum DeadStoreElimination {
@@ -132,7 +136,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
132136
}
133137

134138
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
135-
eliminate(tcx, body);
139+
if eliminate(tcx, body) {
140+
UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
141+
for data in body.basic_blocks.as_mut_preserves_cfg() {
142+
data.strip_nops();
143+
}
144+
}
136145
}
137146

138147
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 & 39 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};
@@ -335,7 +335,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
335335

336336
fn strip_nops(&mut self) {
337337
for blk in self.basic_blocks.iter_mut() {
338-
blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
338+
blk.strip_nops();
339339
}
340340
}
341341
}
@@ -508,28 +508,39 @@ fn make_local_map<V>(
508508
/// Keeps track of used & unused locals.
509509
struct UsedLocals {
510510
increment: bool,
511-
arg_count: u32,
512511
use_count: IndexVec<Local, u32>,
512+
always_used: DenseBitSet<Local>,
513513
}
514514

515515
impl UsedLocals {
516516
/// Determines which locals are used & unused in the given body.
517517
fn new(body: &Body<'_>) -> Self {
518+
let mut always_used = debuginfo_locals(body);
519+
always_used.insert(RETURN_PLACE);
520+
for arg in body.args_iter() {
521+
always_used.insert(arg);
522+
}
518523
let mut this = Self {
519524
increment: true,
520-
arg_count: body.arg_count.try_into().unwrap(),
521525
use_count: IndexVec::from_elem(0, &body.local_decls),
526+
always_used,
522527
};
523528
this.visit_body(body);
524529
this
525530
}
526531

527532
/// Checks if local is used.
528533
///
529-
/// Return place and arguments are always considered used.
534+
/// Return place, arguments, var debuginfo are always considered used.
530535
fn is_used(&self, local: Local) -> bool {
531-
trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]);
532-
local.as_u32() <= self.arg_count || self.use_count[local] != 0
536+
trace!(
537+
"is_used({:?}): use_count: {:?}, always_used: {}",
538+
local,
539+
self.use_count[local],
540+
self.always_used.contains(local)
541+
);
542+
// To keep things simple, we don't handle debugging information here, these are in DSE.
543+
self.always_used.contains(local) || self.use_count[local] != 0
533544
}
534545

535546
/// Updates the use counts to reflect the removal of given statement.
@@ -574,17 +585,9 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
574585
StatementKind::ConstEvalCounter
575586
| StatementKind::Nop
576587
| StatementKind::StorageLive(..)
577-
| StatementKind::StorageDead(..) => {
578-
for debuginfo in statement.debuginfos.iter() {
579-
self.visit_statement_debuginfo(debuginfo, location);
580-
}
581-
}
582-
588+
| StatementKind::StorageDead(..) => {}
583589
StatementKind::Assign(box (ref place, ref rvalue)) => {
584590
if rvalue.is_safe_to_remove() {
585-
for debuginfo in statement.debuginfos.iter() {
586-
self.visit_statement_debuginfo(debuginfo, location);
587-
}
588591
self.visit_lhs(place, location);
589592
self.visit_rvalue(rvalue, location);
590593
} else {
@@ -595,18 +598,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
595598
StatementKind::SetDiscriminant { ref place, variant_index: _ }
596599
| StatementKind::Deinit(ref place)
597600
| StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => {
598-
for debuginfo in statement.debuginfos.iter() {
599-
self.visit_statement_debuginfo(debuginfo, location);
600-
}
601601
self.visit_lhs(place, location);
602602
}
603603
}
604604
}
605605

606606
fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) {
607+
if matches!(ctx, PlaceContext::NonUse(_)) {
608+
return;
609+
}
607610
if self.increment {
608611
self.use_count[local] += 1;
609-
} else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
612+
} else {
610613
assert_ne!(self.use_count[local], 0);
611614
self.use_count[local] -= 1;
612615
}
@@ -626,28 +629,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
626629

627630
for data in body.basic_blocks.as_mut_preserves_cfg() {
628631
// Remove unnecessary StorageLive and StorageDead annotations.
629-
data.retain_statements(|statement| {
630-
let keep = match &statement.kind {
632+
for statement in data.statements.iter_mut() {
633+
let keep_statement = match &statement.kind {
631634
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
632635
used_locals.is_used(*local)
633636
}
634-
StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local),
635-
636-
StatementKind::SetDiscriminant { place, .. }
637-
| StatementKind::BackwardIncompatibleDropHint { place, reason: _ }
638-
| StatementKind::Deinit(place) => used_locals.is_used(place.local),
639-
StatementKind::Nop => false,
640-
_ => true,
637+
StatementKind::Assign(box (place, _))
638+
| StatementKind::SetDiscriminant { box place, .. }
639+
| StatementKind::BackwardIncompatibleDropHint { box place, .. }
640+
| StatementKind::Deinit(box place) => used_locals.is_used(place.local),
641+
_ => continue,
641642
};
642-
643-
if !keep {
644-
trace!("removing statement {:?}", statement);
645-
modified = true;
646-
used_locals.statement_removed(statement);
643+
if keep_statement {
644+
continue;
647645
}
648-
649-
keep
650-
});
646+
trace!("removing statement {:?}", statement);
647+
modified = true;
648+
used_locals.statement_removed(statement);
649+
statement.make_nop(true);
650+
}
651+
data.strip_nops();
651652
}
652653
}
653654
}
@@ -666,3 +667,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
666667
*l = self.map[*l].unwrap();
667668
}
668669
}
670+
671+
pub(crate) struct UsedInStmtLocals {
672+
pub(crate) locals: DenseBitSet<Local>,
673+
}
674+
675+
impl UsedInStmtLocals {
676+
pub(crate) fn new(body: &Body<'_>) -> Self {
677+
let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) };
678+
this.visit_body(body);
679+
this
680+
}
681+
682+
pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) {
683+
for data in body.basic_blocks.as_mut_preserves_cfg() {
684+
// Remove unnecessary StorageLive and StorageDead annotations.
685+
for statement in data.statements.iter_mut() {
686+
let keep_statement = match &statement.kind {
687+
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
688+
self.locals.contains(*local)
689+
}
690+
_ => continue,
691+
};
692+
if keep_statement {
693+
continue;
694+
}
695+
statement.make_nop(true);
696+
}
697+
}
698+
}
699+
}
700+
701+
impl<'tcx> Visitor<'tcx> for UsedInStmtLocals {
702+
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
703+
if matches!(context, PlaceContext::NonUse(_)) {
704+
return;
705+
}
706+
self.locals.insert(local);
707+
}
708+
}

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.dead_first.DeadStoreElimination-initial.diff

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
StorageLive(_2);
1616
- _2 = &((*_1).2: i32);
1717
+ // DBG: _2 = &((*_1).2: i32);
18-
+ nop;
1918
StorageLive(_3);
2019
StorageLive(_4);
2120
_4 = &((*_1).0: i32);

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

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

1414
bb0: {
15-
StorageLive(_2);
15+
- StorageLive(_2);
1616
_3 = deref_copy (_1.1: &Foo);
1717
- _2 = &((*_3).2: i32);
1818
+ // DBG: _2 = &((*_3).2: i32);
19-
+ nop;
2019
_4 = deref_copy (_1.1: &Foo);
2120
_0 = copy ((*_4).0: i32);
22-
StorageDead(_2);
21+
- StorageDead(_2);
2322
return;
2423
}
2524
}

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)