Skip to content

Commit 8da0428

Browse files
committed
mir-opt: Eliminate dead statements even if they are used by debuginfos
1 parent 1bd89bd commit 8da0428

20 files changed

+170
-101
lines changed

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
155155
self.debug_poison_to_local(bx, *dest);
156156
}
157157
}
158+
StmtDebugInfo::InvalidAssign(local) => {
159+
self.debug_poison_to_local(bx, *local);
160+
}
158161
}
159162
}
160163

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,9 @@ impl Debug for StmtDebugInfo<'_> {
844844
StmtDebugInfo::AssignRef(local, place) => {
845845
write!(fmt, "{local:?} = &{place:?}")
846846
}
847+
StmtDebugInfo::InvalidAssign(local) => {
848+
write!(fmt, "{local:?} = &?")
849+
}
847850
}
848851
}
849852
}

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,20 @@ impl<'tcx> PlaceRef<'tcx> {
527527
})
528528
}
529529

530+
/// Return the place accessed locals that include the base local.
531+
pub fn accessed_locals(self) -> impl Iterator<Item = Local> {
532+
std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj {
533+
ProjectionElem::Index(local) => Some(*local),
534+
ProjectionElem::Deref
535+
| ProjectionElem::Field(_, _)
536+
| ProjectionElem::ConstantIndex { .. }
537+
| ProjectionElem::Subslice { .. }
538+
| ProjectionElem::Downcast(_, _)
539+
| ProjectionElem::OpaqueCast(_)
540+
| ProjectionElem::UnwrapUnsafeBinder(_) => None,
541+
}))
542+
}
543+
530544
/// Generates a new place by appending `more_projections` to the existing ones
531545
/// and interning the result.
532546
pub fn project_deeper(
@@ -1057,4 +1071,5 @@ impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> {
10571071
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
10581072
pub enum StmtDebugInfo<'tcx> {
10591073
AssignRef(Local, Place<'tcx>),
1074+
InvalidAssign(Local),
10601075
}

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
@@ -287,9 +287,6 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
287287
if let Some(destination) =
288288
Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals)
289289
&& !state.contains(destination.local)
290-
// FIXME: We can eliminate the statement, but we'll need the statements it depends on
291-
// for debuginfos. We need a way to handle this.
292-
&& !self.debuginfo_locals.contains(destination.local)
293290
{
294291
// This store is dead
295292
return;

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,22 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
657657
self.tcx
658658
}
659659

660+
fn visit_statement_debuginfo(
661+
&mut self,
662+
stmt_debuginfo: &mut StmtDebugInfo<'tcx>,
663+
location: Location,
664+
) {
665+
match stmt_debuginfo {
666+
StmtDebugInfo::AssignRef(local, place) => {
667+
if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) {
668+
*stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local);
669+
}
670+
}
671+
StmtDebugInfo::InvalidAssign(_) => {}
672+
}
673+
self.super_statement_debuginfo(stmt_debuginfo, location);
674+
}
675+
660676
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
661677
*l = self.map[*l].unwrap();
662678
}

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: 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
// 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
@@ -78,11 +85,20 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo {
7885
fragment_v2
7986
}
8087

88+
#[no_mangle]
89+
pub fn deref(bar: Bar) -> i32 {
90+
// CHECK-LABEL: define {{.*}} i32 @deref
91+
// We are unable to represent dereference within this expression.
92+
// CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression()
93+
let deref_dead = &bar.foo.2;
94+
bar.a
95+
}
96+
8197
#[no_mangle]
8298
pub fn tuple(foo: (i32, &Foo)) -> i32 {
8399
// CHECK-LABEL: define{{.*}} i32 @tuple
84-
// CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]])
85-
// CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value)
100+
// Although there is no dereference here, there is a dereference in the MIR.
101+
// CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression()
86102
let tuple_dead = &foo.1.2;
87103
foo.1.0
88104
}
@@ -148,6 +164,7 @@ pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 {
148164
// CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2"
149165
// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref"
150166
// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f"
167+
// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead"
151168
// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead"
152169
// CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo"
153170
// CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0"

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)