Skip to content

Commit a0b4fe1

Browse files
committed
simplifycfg: Preserve debuginfos when merging bbs
1 parent 29357bc commit a0b4fe1

7 files changed

+231
-3
lines changed

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> {
444444
self.kind.successors()
445445
}
446446

447+
/// Return `Some` if all successors are identical.
448+
#[inline]
449+
pub fn identical_successor(&self) -> Option<BasicBlock> {
450+
let mut successors = self.successors();
451+
let first_succ = successors.next()?;
452+
if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
453+
}
454+
447455
#[inline]
448456
pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
449457
self.kind.successors_mut(f)

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
143143
// statements itself to avoid moving the (relatively) large statements twice.
144144
// We do not push the statements directly into the target block (`bb`) as that is slower
145145
// due to additional reallocations
146-
let mut merged_blocks = Vec::new();
146+
let mut merged_blocks: Vec<BasicBlock> = Vec::new();
147147
let mut outer_changed = false;
148148
loop {
149149
let mut changed = false;
@@ -158,8 +158,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
158158
let mut terminator =
159159
self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
160160

161-
terminator
162-
.successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed));
161+
let identical_succ = terminator.identical_successor();
162+
terminator.successors_mut(|successor| {
163+
self.collapse_goto_chain(successor, &mut changed);
164+
});
165+
if changed && let Some(identical_succ) = identical_succ {
166+
// Add debugging information from the goto chain only when all successors are identical,
167+
// otherwise, we may provide misleading debugging information within a branch.
168+
let mut succ_debuginfos =
169+
self.basic_blocks[identical_succ].after_last_stmt_debuginfos.clone();
170+
self.basic_blocks[bb].after_last_stmt_debuginfos.append(&mut succ_debuginfos);
171+
}
163172

164173
let mut inner_changed = true;
165174
merged_blocks.clear();
@@ -176,10 +185,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
176185
if statements_to_merge > 0 {
177186
let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
178187
statements.reserve(statements_to_merge);
188+
let mut parent_bb_last_debuginfos =
189+
std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
179190
for &from in &merged_blocks {
191+
if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
192+
stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos);
193+
}
180194
statements.append(&mut self.basic_blocks[from].statements);
195+
parent_bb_last_debuginfos =
196+
std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
181197
}
182198
self.basic_blocks[bb].statements = statements;
199+
self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
183200
}
184201

185202
self.basic_blocks[bb].terminator = Some(terminator);
@@ -229,11 +246,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
229246
let last = current;
230247
*changed |= *start != last;
231248
*start = last;
249+
let mut succ = last;
232250
while let Some((current, mut terminator)) = terminators.pop() {
233251
let Terminator { kind: TerminatorKind::Goto { ref mut target }, .. } = terminator
234252
else {
235253
unreachable!();
236254
};
255+
if *target != last {
256+
let mut succ_debuginfos =
257+
self.basic_blocks[succ].after_last_stmt_debuginfos.clone();
258+
self.basic_blocks[current].after_last_stmt_debuginfos.extend(&mut succ_debuginfos);
259+
}
260+
succ = current;
237261
*changed |= *target != last;
238262
*target = last;
239263
debug!("collapsing goto chain from {:?} to {:?}", current, target);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
-
1515
- bb1: {
1616
- // DBG: _3 = &((*_1).0: i32);
17+
- nop;
1718
- goto -> bb2;
1819
- }
1920
-
2021
- bb2: {
2122
// DBG: _4 = &((*_1).1: i64);
23+
- nop;
2224
_0 = copy ((*_1).2: i32);
2325
return;
2426
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
- bb1: {
1818
(*_2) = const true;
1919
// DBG: _3 = &((*_1).0: i32);
20+
- nop;
2021
- goto -> bb2;
2122
- }
2223
-
2324
- bb2: {
2425
// DBG: _4 = &((*_1).1: i64);
26+
- nop;
2527
_0 = copy ((*_1).2: i32);
2628
// DBG: _5 = &((*_1).2: i32);
29+
- nop;
2730
return;
2831
}
2932
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
-
1717
- bb1: {
1818
// DBG: _2 = &((*_1).0: i32);
19+
- nop;
1920
- goto -> bb2;
2021
- }
2122
-
2223
- bb2: {
2324
// DBG: _3 = &((*_1).1: i64);
25+
- nop;
2426
_0 = copy ((*_1).2: i32);
2527
// DBG: _4 = &((*_1).2: i32);
28+
- nop;
2629
return;
2730
}
2831
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
- // MIR for `preserve_debuginfo_identical_succs` before SimplifyCfg-final
2+
+ // MIR for `preserve_debuginfo_identical_succs` after SimplifyCfg-final
3+
4+
fn preserve_debuginfo_identical_succs(_1: &Foo, _2: bool) -> i32 {
5+
debug foo_a => _3;
6+
debug foo_b => _4;
7+
debug foo_c => _5;
8+
let mut _0: i32;
9+
let mut _3: &i32;
10+
let mut _4: &i64;
11+
let mut _5: &i32;
12+
13+
bb0: {
14+
- switchInt(copy _2) -> [1: bb1, otherwise: bb1];
15+
- }
16+
-
17+
- bb1: {
18+
// DBG: _3 = &((*_1).0: i32);
19+
- goto -> bb2;
20+
- }
21+
-
22+
- bb2: {
23+
// DBG: _4 = &((*_1).1: i64);
24+
_0 = copy ((*_1).2: i32);
25+
// DBG: _5 = &((*_1).2: i32);
26+
return;
27+
}
28+
}
29+
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//@ test-mir-pass: SimplifyCfg-final
2+
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination-initial
3+
4+
#![feature(core_intrinsics, custom_mir)]
5+
#![crate_type = "lib"]
6+
7+
use std::intrinsics::mir::*;
8+
9+
pub struct Foo {
10+
a: i32,
11+
b: i64,
12+
c: i32,
13+
}
14+
15+
// EMIT_MIR simplifycfg.drop_debuginfo.SimplifyCfg-final.diff
16+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
17+
pub fn drop_debuginfo(foo: &Foo, c: bool) -> i32 {
18+
// CHECK-LABEL: fn drop_debuginfo
19+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
20+
// CHECK: bb0: {
21+
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
22+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
23+
// CHECK-NEXT: return;
24+
mir! {
25+
let _foo_a: &i32;
26+
let _foo_b: &i64;
27+
debug foo_a => _foo_a;
28+
debug foo_b => _foo_b;
29+
{
30+
match c {
31+
true => tmp,
32+
_ => ret,
33+
}
34+
}
35+
tmp = {
36+
// Because we don't know if `c` is always true, we must drop this debuginfo.
37+
_foo_a = &(*foo).a;
38+
Goto(ret)
39+
}
40+
ret = {
41+
_foo_b = &(*foo).b;
42+
RET = (*foo).c;
43+
Return()
44+
}
45+
}
46+
}
47+
48+
// EMIT_MIR simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff
49+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
50+
pub fn preserve_debuginfo_1(foo: &Foo, v: &mut bool) -> i32 {
51+
// CHECK-LABEL: fn preserve_debuginfo_1
52+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
53+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
54+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
55+
// CHECK: bb0: {
56+
// CHECK-NEXT: (*_2) = const true;
57+
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
58+
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
59+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
60+
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
61+
// CHECK-NEXT: return;
62+
mir! {
63+
let _foo_a: &i32;
64+
let _foo_b: &i64;
65+
let _foo_c: &i32;
66+
debug foo_a => _foo_a;
67+
debug foo_b => _foo_b;
68+
debug foo_c => _foo_c;
69+
{
70+
Goto(tmp)
71+
}
72+
tmp = {
73+
*v = true;
74+
_foo_a = &(*foo).a;
75+
Goto(ret)
76+
}
77+
ret = {
78+
_foo_b = &(*foo).b;
79+
RET = (*foo).c;
80+
_foo_c = &(*foo).c;
81+
Return()
82+
}
83+
}
84+
}
85+
86+
// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff
87+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
88+
pub fn preserve_debuginfo_2(foo: &Foo) -> i32 {
89+
// CHECK-LABEL: fn preserve_debuginfo_2
90+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
91+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
92+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
93+
// CHECK: bb0: {
94+
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
95+
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
96+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
97+
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
98+
// CHECK-NEXT: return;
99+
mir! {
100+
let _foo_a: &i32;
101+
let _foo_b: &i64;
102+
let _foo_c: &i32;
103+
debug foo_a => _foo_a;
104+
debug foo_b => _foo_b;
105+
debug foo_c => _foo_c;
106+
{
107+
Goto(tmp)
108+
}
109+
tmp = {
110+
_foo_a = &(*foo).a;
111+
Goto(ret)
112+
}
113+
ret = {
114+
_foo_b = &(*foo).b;
115+
RET = (*foo).c;
116+
_foo_c = &(*foo).c;
117+
Return()
118+
}
119+
}
120+
}
121+
122+
// EMIT_MIR simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff
123+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
124+
pub fn preserve_debuginfo_identical_succs(foo: &Foo, c: bool) -> i32 {
125+
// CHECK-LABEL: fn preserve_debuginfo_identical_succs
126+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
127+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
128+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
129+
// CHECK: bb0: {
130+
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
131+
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
132+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
133+
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
134+
// CHECK-NEXT: return;
135+
mir! {
136+
let _foo_a: &i32;
137+
let _foo_b: &i64;
138+
let _foo_c: &i32;
139+
debug foo_a => _foo_a;
140+
debug foo_b => _foo_b;
141+
debug foo_c => _foo_c;
142+
{
143+
match c {
144+
true => tmp,
145+
_ => tmp,
146+
}
147+
}
148+
tmp = {
149+
_foo_a = &(*foo).a;
150+
Goto(ret)
151+
}
152+
ret = {
153+
_foo_b = &(*foo).b;
154+
RET = (*foo).c;
155+
_foo_c = &(*foo).c;
156+
Return()
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)