Skip to content

Commit 2b05e02

Browse files
committed
mir-opt: Eliminate dead statements even if they are used by debuginfos
1 parent d520046 commit 2b05e02

File tree

15 files changed

+160
-112
lines changed

15 files changed

+160
-112
lines changed

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -109,50 +109,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
109109
pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
110110
match debuginfo {
111111
StmtDebugInfo::AssignRef(dest, place) => {
112-
let place_ref = match self.locals[place.local] {
113-
LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => {
114-
Some(place_ref)
115-
}
116-
LocalRef::Operand(operand_ref) => match operand_ref.val {
117-
OperandValue::Immediate(v) => {
118-
Some(PlaceRef::new_sized(v, operand_ref.layout))
112+
let assign_ref = if let Some(place) = place {
113+
let place_ref = match self.locals[place.local] {
114+
LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => {
115+
Some(place_ref)
119116
}
120-
OperandValue::Ref(_)
121-
| OperandValue::Pair(_, _)
122-
| OperandValue::ZeroSized => None,
123-
},
124-
LocalRef::PendingOperand => None,
125-
}
126-
.filter(|place_ref| {
127-
// Drop unsupported projections.
128-
// FIXME: Add a test case.
129-
place.projection.iter().all(|p| p.can_use_in_debuginfo()) &&
130-
// Only pointers can calculate addresses.
131-
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
132-
});
133-
let (val, layout, projection) =
117+
LocalRef::Operand(operand_ref) => match operand_ref.val {
118+
OperandValue::Immediate(v) => {
119+
Some(PlaceRef::new_sized(v, operand_ref.layout))
120+
}
121+
OperandValue::Ref(_)
122+
| OperandValue::Pair(_, _)
123+
| OperandValue::ZeroSized => None,
124+
},
125+
LocalRef::PendingOperand => None,
126+
}
127+
.filter(|place_ref| {
128+
// Drop unsupported projections.
129+
// FIXME: Add a test case.
130+
place.projection.iter().all(|p| p.can_use_in_debuginfo()) &&
131+
// Only pointers can calculate addresses.
132+
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
133+
});
134134
match (place_ref, place.is_indirect_first_projection()) {
135135
(Some(place_ref), false) => {
136-
(place_ref.val, place_ref.layout, place.projection.as_slice())
136+
Some((place_ref.val, place_ref.layout, place.projection.as_slice()))
137137
}
138138
(Some(place_ref), true) => {
139139
let projected_ty =
140140
place_ref.layout.ty.builtin_deref(true).unwrap_or_else(|| {
141141
bug!("deref of non-pointer {:?}", place_ref)
142142
});
143143
let layout = bx.cx().layout_of(projected_ty);
144-
(place_ref.val, layout, &place.projection[1..])
144+
Some((place_ref.val, layout, &place.projection[1..]))
145145
}
146-
_ => {
147-
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
148-
let ty = self.monomorphize(self.mir.local_decls[*dest].ty);
149-
let layout = bx.cx().layout_of(ty);
150-
let to_backend_ty = bx.cx().immediate_backend_type(layout);
151-
let place_ref =
152-
PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
153-
(place_ref.val, layout, [].as_slice())
154-
}
155-
};
146+
_ => None,
147+
}
148+
} else {
149+
None
150+
};
151+
let (val, layout, projection) = assign_ref.unwrap_or_else(|| {
152+
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
153+
let ty = self.monomorphize(self.mir.local_decls[*dest].ty);
154+
let layout = bx.cx().layout_of(ty);
155+
let to_backend_ty = bx.cx().immediate_backend_type(layout);
156+
let place_ref =
157+
PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
158+
(place_ref.val, layout, [].as_slice())
159+
});
156160
self.debug_new_value_to_local(bx, *dest, val, layout, projection);
157161
}
158162
}

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,8 @@ impl Debug for StmtDebugInfo<'_> {
901901
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
902902
match self {
903903
StmtDebugInfo::AssignRef(local, place) => {
904-
write!(fmt, "{local:?} = &{place:?}")
904+
write!(fmt, "{local:?} = &")?;
905+
if let Some(place) = place { write!(fmt, "{place:?}") } else { write!(fmt, "?") }
905906
}
906907
}
907908
}

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<'tcx> StatementKind<'tcx> {
7979
StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place)))
8080
if let Some(local) = place.as_local() =>
8181
{
82-
Some(StmtDebugInfo::AssignRef(local, *ref_place))
82+
Some(StmtDebugInfo::AssignRef(local, Some(*ref_place)))
8383
}
8484
_ => None,
8585
}
@@ -963,5 +963,5 @@ impl RawPtrKind {
963963

964964
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
965965
pub enum StmtDebugInfo<'tcx> {
966-
AssignRef(Local, Place<'tcx>),
966+
AssignRef(Local, Option<Place<'tcx>>),
967967
}

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,13 @@ macro_rules! make_mir_visitor {
420420
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
421421
location
422422
);
423-
self.visit_place(
424-
place,
425-
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
426-
location
427-
);
423+
if let Some(place) = place {
424+
self.visit_place(
425+
place,
426+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
427+
location
428+
);
429+
}
428430
},
429431
}
430432
}

compiler/rustc_mir_dataflow/src/debuginfo.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet<Local> {
1212
struct DebuginfoLocals(DenseBitSet<Local>);
1313

1414
impl Visitor<'_> for DebuginfoLocals {
15-
const VISIT_DEBUG_INFO: bool = true;
16-
1715
fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) {
1816
if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
1917
self.0.insert(local);

compiler/rustc_mir_dataflow/src/impls/liveness.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,6 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
282282
if let Some(destination) =
283283
Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals)
284284
&& !state.contains(destination.local)
285-
// FIXME: We can eliminate the statement, but we'll need the statements it depends on
286-
// for debuginfos. We need a way to handle this.
287-
&& !self.debuginfo_locals.contains(destination.local)
288285
{
289286
// This store is dead
290287
return;

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,19 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
663663
}
664664
}
665665

666+
struct InvalidPlace<'a> {
667+
map: &'a IndexVec<Local, Option<Local>>,
668+
is_invalid: bool,
669+
}
670+
671+
impl<'tcx, 'a> Visitor<'tcx> for InvalidPlace<'a> {
672+
fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
673+
if self.map[local].is_none() {
674+
self.is_invalid = true;
675+
}
676+
}
677+
}
678+
666679
struct LocalUpdater<'tcx> {
667680
map: IndexVec<Local, Option<Local>>,
668681
tcx: TyCtxt<'tcx>,
@@ -675,6 +688,29 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
675688
self.tcx
676689
}
677690

691+
fn visit_statement_debuginfo(
692+
&mut self,
693+
stmt_debuginfo: &mut StmtDebugInfo<'tcx>,
694+
location: Location,
695+
) {
696+
match stmt_debuginfo {
697+
StmtDebugInfo::AssignRef(_, place) => {
698+
if place.is_some_and(|ref place| {
699+
let mut invalid_place = InvalidPlace { map: &self.map, is_invalid: false };
700+
invalid_place.visit_place(
701+
place,
702+
PlaceContext::NonUse(visit::NonUseContext::VarDebugInfo),
703+
location,
704+
);
705+
invalid_place.is_invalid
706+
}) {
707+
*place = None;
708+
}
709+
}
710+
}
711+
self.super_statement_debuginfo(stmt_debuginfo, location);
712+
}
713+
678714
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
679715
*l = self.map[*l].unwrap();
680716
}

compiler/rustc_mir_transform/src/strip_debuginfo.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_middle::mir::*;
22
use rustc_middle::ty::TyCtxt;
3+
use rustc_mir_dataflow::debuginfo::debuginfo_locals;
34
use rustc_session::config::MirStripDebugInfo;
45

56
/// Conditionally remove some of the VarDebugInfo in MIR.
@@ -30,6 +31,18 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo {
3031
if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE,
3132
)
3233
});
34+
35+
let debuginfo_locals = debuginfo_locals(body);
36+
for data in body.basic_blocks.as_mut_preserves_cfg() {
37+
for stmt in data.statements.iter_mut() {
38+
stmt.debuginfos.retain(|debuginfo| match debuginfo {
39+
StmtDebugInfo::AssignRef(local, _) => debuginfo_locals.contains(*local),
40+
});
41+
}
42+
data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo {
43+
StmtDebugInfo::AssignRef(local, _) => debuginfo_locals.contains(*local),
44+
});
45+
}
3346
}
3447

3548
fn is_required(&self) -> bool {

tests/codegen/debuginfo-dse.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir
1+
//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir -Zmerge-functions=disabled
22
//@ revisions: CODEGEN OPTIMIZED
33
//@[CODEGEN] compile-flags: -Cno-prepopulate-passes
44

@@ -8,6 +8,13 @@
88
#[derive(Clone, Copy)]
99
pub struct Foo(i32, i64, i32);
1010

11+
#[repr(C)]
12+
pub struct Bar<'a> {
13+
a: i32,
14+
b: i64,
15+
foo: &'a Foo,
16+
}
17+
1118
#[no_mangle]
1219
fn r#ref(ref_foo: &Foo) -> i32 {
1320
// CHECK-LABEL: define {{.*}} i32 @ref
@@ -58,11 +65,20 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo {
5865
fragment_v2
5966
}
6067

68+
#[no_mangle]
69+
pub fn deref(bar: Bar) -> i32 {
70+
// CHECK-LABEL: define {{.*}} i32 @deref
71+
// We are unable to represent dereference within this expression.
72+
// CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression()
73+
let deref_dead = &bar.foo.2;
74+
bar.a
75+
}
76+
6177
#[no_mangle]
6278
pub fn tuple(foo: (i32, &Foo)) -> i32 {
6379
// CHECK-LABEL: define {{.*}} i32 @tuple
64-
// CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]])
65-
// CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value)
80+
// Although there is no dereference here, there is a dereference in the MIR.
81+
// CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression()
6682
let tuple_dead = &foo.1.2;
6783
foo.1.0
6884
}
@@ -77,3 +93,4 @@ pub fn tuple(foo: (i32, &Foo)) -> i32 {
7793
// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref"
7894
// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f"
7995
// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead"
96+
// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead"

tests/mir-opt/dead-store-elimination/ref.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ pub fn tuple(v: (i32, &Foo)) -> i32 {
1111
// CHECK-LABEL: fn tuple
1212
// CHECK: debug _dead => [[dead:_[0-9]+]];
1313
// CHECK: bb0:
14-
// FIXME: Preserve `tmp` for debuginfo, but we can merge it into the debuginfo.
15-
// CHECK-NEXT: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo);
16-
// CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32)
14+
// CHECK-NEXT: DBG: [[dead]] = &((*_3).2: i32)
1715
let _dead = &v.1.c;
1816
v.1.a
1917
}

0 commit comments

Comments
 (0)