Skip to content

Commit 458ad93

Browse files
committed
mir-opt: Eliminate dead statements even if they are used by debuginfos
1 parent ec25192 commit 458ad93

19 files changed

+197
-117
lines changed

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -127,34 +127,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
127127
// Drop unsupported projections.
128128
// FIXME: Add a test case.
129129
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
130+
// Only pointers can calculate addresses.
131+
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
132+
});
133+
let assign_ref = match (place_ref, place.is_indirect_first_projection()) {
134+
(Some(place_ref), false) => {
135+
Some((place_ref.val, place_ref.layout, place.projection.as_slice()))
136+
}
137+
(Some(place_ref), true) => {
138+
let projected_ty = place_ref
139+
.layout
140+
.ty
141+
.builtin_deref(true)
142+
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", place_ref));
143+
let layout = bx.cx().layout_of(projected_ty);
144+
Some((place_ref.val, layout, &place.projection[1..]))
145+
}
146+
_ => None,
147+
};
148+
let (val, layout, projection) = assign_ref.unwrap_or_else(|| {
149+
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
150+
let ty = self.monomorphize(self.mir.local_decls[*dest].ty);
151+
let layout = bx.cx().layout_of(ty);
152+
let to_backend_ty = bx.cx().immediate_backend_type(layout);
153+
let place_ref =
154+
PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
155+
(place_ref.val, layout, [].as_slice())
132156
});
133-
let (val, layout, projection) =
134-
match (place_ref, place.is_indirect_first_projection()) {
135-
(Some(place_ref), false) => {
136-
(place_ref.val, place_ref.layout, place.projection.as_slice())
137-
}
138-
(Some(place_ref), true) => {
139-
let projected_ty =
140-
place_ref.layout.ty.builtin_deref(true).unwrap_or_else(|| {
141-
bug!("deref of non-pointer {:?}", place_ref)
142-
});
143-
let layout = bx.cx().layout_of(projected_ty);
144-
(place_ref.val, layout, &place.projection[1..])
145-
}
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-
};
156157
self.debug_new_value_to_local(bx, *dest, val, layout, projection);
157158
}
159+
StmtDebugInfo::InvalidAssign(local) => {
160+
let ty = self.monomorphize(self.mir.local_decls[*local].ty);
161+
let layout = bx.cx().layout_of(ty);
162+
let to_backend_ty = bx.cx().immediate_backend_type(layout);
163+
let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
164+
self.debug_new_value_to_local(bx, *local, place_ref.val, layout, &[]);
165+
}
158166
}
159167
}
160168

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,9 @@ impl Debug for StmtDebugInfo<'_> {
866866
StmtDebugInfo::AssignRef(local, place) => {
867867
write!(fmt, "{local:?} = &{place:?}")
868868
}
869+
StmtDebugInfo::InvalidAssign(local) => {
870+
write!(fmt, "{local:?} = &?")
871+
}
869872
}
870873
}
871874
}

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,21 @@ impl<'tcx> PlaceRef<'tcx> {
498498
})
499499
}
500500

501+
/// Return the place accessed locals that include the base local.
502+
pub fn accessed_locals(self) -> impl Iterator<Item = Local> {
503+
std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj {
504+
ProjectionElem::Index(local) => Some(*local),
505+
ProjectionElem::Deref
506+
| ProjectionElem::Field(_, _)
507+
| ProjectionElem::ConstantIndex { .. }
508+
| ProjectionElem::Subslice { .. }
509+
| ProjectionElem::Downcast(_, _)
510+
| ProjectionElem::OpaqueCast(_)
511+
| ProjectionElem::UnwrapUnsafeBinder(_)
512+
| ProjectionElem::Subtype(_) => None,
513+
}))
514+
}
515+
501516
/// Generates a new place by appending `more_projections` to the existing ones
502517
/// and interning the result.
503518
pub fn project_deeper(
@@ -1029,4 +1044,5 @@ impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> {
10291044
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
10301045
pub enum StmtDebugInfo<'tcx> {
10311046
AssignRef(Local, Place<'tcx>),
1047+
InvalidAssign(Local),
10321048
}

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,13 @@ macro_rules! make_mir_visitor {
406406
location
407407
);
408408
},
409+
StmtDebugInfo::InvalidAssign(local) => {
410+
self.visit_local(
411+
$(& $mutability)? *local,
412+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
413+
location
414+
);
415+
}
409416
}
410417
}
411418

compiler/rustc_mir_dataflow/src/debuginfo.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ use rustc_middle::mir::*;
55
/// Return the set of locals that appear in debuginfo.
66
pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet<Local> {
77
let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len()));
8-
visitor.visit_body(body);
8+
for debuginfo in body.var_debug_info.iter() {
9+
visitor.visit_var_debug_info(debuginfo);
10+
}
911
visitor.0
1012
}
1113

1214
struct DebuginfoLocals(DenseBitSet<Local>);
1315

1416
impl Visitor<'_> for DebuginfoLocals {
15-
fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) {
16-
if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
17-
self.0.insert(local);
18-
}
17+
fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
18+
self.0.insert(local);
1919
}
2020
}

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,22 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
663663
self.tcx
664664
}
665665

666+
fn visit_statement_debuginfo(
667+
&mut self,
668+
stmt_debuginfo: &mut StmtDebugInfo<'tcx>,
669+
location: Location,
670+
) {
671+
match stmt_debuginfo {
672+
StmtDebugInfo::AssignRef(local, place) => {
673+
if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) {
674+
*stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local);
675+
}
676+
}
677+
StmtDebugInfo::InvalidAssign(_) => {}
678+
}
679+
self.super_statement_debuginfo(stmt_debuginfo, location);
680+
}
681+
666682
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
667683
*l = self.map[*l].unwrap();
668684
}

compiler/rustc_mir_transform/src/strip_debuginfo.rs

Lines changed: 17 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,22 @@ 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, _) | StmtDebugInfo::InvalidAssign(local) => {
40+
debuginfo_locals.contains(*local)
41+
}
42+
});
43+
}
44+
data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo {
45+
StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => {
46+
debuginfo_locals.contains(*local)
47+
}
48+
});
49+
}
3350
}
3451

3552
fn is_required(&self) -> bool {

tests/codegen-llvm/debuginfo-dse.rs

Lines changed: 19 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
// ignore-tidy-linelength
@@ -9,6 +9,13 @@
99
#[derive(Clone, Copy)]
1010
pub struct Foo(i32, i64, i32);
1111

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

84+
#[no_mangle]
85+
pub fn deref(bar: Bar) -> i32 {
86+
// CHECK-LABEL: define {{.*}} i32 @deref
87+
// We are unable to represent dereference within this expression.
88+
// CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression()
89+
let deref_dead = &bar.foo.2;
90+
bar.a
91+
}
92+
7793
#[no_mangle]
7894
pub fn tuple(foo: (i32, &Foo)) -> i32 {
7995
// CHECK-LABEL: define {{.*}} i32 @tuple
80-
// CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]])
81-
// CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value)
96+
// Although there is no dereference here, there is a dereference in the MIR.
97+
// CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression()
8298
let tuple_dead = &foo.1.2;
8399
foo.1.0
84100
}

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: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo);
16-
// CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32)
14+
// CHECK: DBG: [[dead]] = &((*_3).2: i32)
1715
let _dead = &v.1.c;
1816
v.1.a
1917
}

0 commit comments

Comments
 (0)