Skip to content

Commit 910cac7

Browse files
committed
simplifycfg: Preserve debuginfos when merging bbs
1 parent 6d2c2e5 commit 910cac7

File tree

6 files changed

+264
-4
lines changed

6 files changed

+264
-4
lines changed

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,21 @@ 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 Some(first_succ) = successors.next() else {
452+
return None;
453+
};
454+
while let Some(succ) = successors.next() {
455+
if first_succ != succ {
456+
return None;
457+
}
458+
}
459+
Some(first_succ)
460+
}
461+
447462
#[inline]
448463
pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
449464
self.kind.successors_mut(f)

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 38 additions & 4 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,21 @@ 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 mut debuginfos = Vec::new();
162+
if let Some(mut unique_succ) = terminator.identical_successor() {
163+
self.collapse_goto_chain(&mut unique_succ, &mut changed, &mut debuginfos);
164+
terminator.successors_mut(|successor| {
165+
*successor = unique_succ;
166+
});
167+
// Add debugging information from the goto chain only when all successors are identical,
168+
// otherwise, we may provide misleading debugging information within a branch.
169+
self.basic_blocks[bb].after_last_stmt_debuginfos.append(&mut debuginfos);
170+
} else {
171+
terminator.successors_mut(|successor| {
172+
debuginfos.clear();
173+
self.collapse_goto_chain(successor, &mut changed, &mut debuginfos)
174+
});
175+
}
163176

164177
let mut inner_changed = true;
165178
merged_blocks.clear();
@@ -176,10 +189,19 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
176189
if statements_to_merge > 0 {
177190
let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
178191
statements.reserve(statements_to_merge);
192+
let mut parent_bb_last_debuginfos =
193+
std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
179194
for &from in &merged_blocks {
195+
if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
196+
parent_bb_last_debuginfos.append(&mut stmt.debuginfos);
197+
std::mem::swap(&mut parent_bb_last_debuginfos, &mut stmt.debuginfos);
198+
}
180199
statements.append(&mut self.basic_blocks[from].statements);
200+
parent_bb_last_debuginfos
201+
.append(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
181202
}
182203
self.basic_blocks[bb].statements = statements;
204+
self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
183205
}
184206

185207
self.basic_blocks[bb].terminator = Some(terminator);
@@ -214,7 +236,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
214236
}
215237

216238
/// Collapse a goto chain starting from `start`
217-
fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
239+
fn collapse_goto_chain(
240+
&mut self,
241+
start: &mut BasicBlock,
242+
changed: &mut bool,
243+
pred_debuginfos: &mut Vec<StmtDebugInfo<'tcx>>,
244+
) {
218245
// Using `SmallVec` here, because in some logs on libcore oli-obk saw many single-element
219246
// goto chains. We should probably benchmark different sizes.
220247
let mut terminators: SmallVec<[_; 1]> = Default::default();
@@ -233,6 +260,13 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
233260
else {
234261
unreachable!();
235262
};
263+
if *target != last {
264+
self.basic_blocks[current]
265+
.after_last_stmt_debuginfos
266+
.extend_from_slice(pred_debuginfos);
267+
}
268+
pred_debuginfos
269+
.extend_from_slice(&self.basic_blocks[current].after_last_stmt_debuginfos);
236270
*changed |= *target != last;
237271
*target = last;
238272
debug!("collapsing goto chain from {:?} to {:?}", current, target);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- // MIR for `drop_debuginfo` before SimplifyCfg-final
2+
+ // MIR for `drop_debuginfo` after SimplifyCfg-final
3+
4+
fn drop_debuginfo(_1: &Foo, _2: bool) -> i32 {
5+
debug foo_a => _3;
6+
debug foo_b => _4;
7+
let mut _0: i32;
8+
let mut _3: &i32;
9+
let mut _4: &i64;
10+
11+
bb0: {
12+
- switchInt(copy _2) -> [1: bb1, otherwise: bb2];
13+
- }
14+
-
15+
- bb1: {
16+
- // DBG: _3 = &((*_1).0: i32)
17+
- goto -> bb2;
18+
- }
19+
-
20+
- bb2: {
21+
// DBG: _4 = &((*_1).1: i64)
22+
_0 = copy ((*_1).2: i32);
23+
return;
24+
}
25+
}
26+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- // MIR for `preserve_debuginfo_1` before SimplifyCfg-final
2+
+ // MIR for `preserve_debuginfo_1` after SimplifyCfg-final
3+
4+
fn preserve_debuginfo_1(_1: &Foo, _2: &mut 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+
- goto -> bb1;
15+
- }
16+
-
17+
- bb1: {
18+
(*_2) = const true;
19+
// DBG: _3 = &((*_1).0: i32)
20+
- goto -> bb2;
21+
- }
22+
-
23+
- bb2: {
24+
// DBG: _4 = &((*_1).1: i64)
25+
_0 = copy ((*_1).2: i32);
26+
// DBG: _5 = &((*_1).2: i32)
27+
return;
28+
}
29+
}
30+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
- // MIR for `preserve_debuginfo_2` before SimplifyCfg-final
2+
+ // MIR for `preserve_debuginfo_2` after SimplifyCfg-final
3+
4+
fn preserve_debuginfo_2(_1: &Foo) -> i32 {
5+
debug foo_a => _2;
6+
debug foo_b => _3;
7+
debug foo_c => _4;
8+
let mut _0: i32;
9+
let mut _2: &i32;
10+
let mut _3: &i64;
11+
let mut _4: &i32;
12+
13+
bb0: {
14+
- goto -> bb1;
15+
- }
16+
-
17+
- bb1: {
18+
// DBG: _2 = &((*_1).0: i32)
19+
- goto -> bb2;
20+
- }
21+
-
22+
- bb2: {
23+
// DBG: _3 = &((*_1).1: i64)
24+
_0 = copy ((*_1).2: i32);
25+
// DBG: _4 = &((*_1).2: i32)
26+
return;
27+
}
28+
}
29+
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//@ test-mir-pass: SimplifyCfg-final
2+
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination-initial,+SimplifyConstCondition-final
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")]
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")]
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+
match true {
71+
true => tmp,
72+
_ => ret,
73+
}
74+
}
75+
tmp = {
76+
*v = true;
77+
_foo_a = &(*foo).a;
78+
Goto(ret)
79+
}
80+
ret = {
81+
_foo_b = &(*foo).b;
82+
RET = (*foo).c;
83+
_foo_c = &(*foo).c;
84+
Return()
85+
}
86+
}
87+
}
88+
89+
// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff
90+
#[custom_mir(dialect = "runtime")]
91+
pub fn preserve_debuginfo_2(foo: &Foo) -> i32 {
92+
// CHECK-LABEL: fn preserve_debuginfo_2
93+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
94+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
95+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
96+
// CHECK: bb0: {
97+
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
98+
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
99+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
100+
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
101+
// CHECK-NEXT: return;
102+
mir! {
103+
let _foo_a: &i32;
104+
let _foo_b: &i64;
105+
let _foo_c: &i32;
106+
debug foo_a => _foo_a;
107+
debug foo_b => _foo_b;
108+
debug foo_c => _foo_c;
109+
{
110+
match true {
111+
true => tmp,
112+
_ => ret,
113+
}
114+
}
115+
tmp = {
116+
_foo_a = &(*foo).a;
117+
Goto(ret)
118+
}
119+
ret = {
120+
_foo_b = &(*foo).b;
121+
RET = (*foo).c;
122+
_foo_c = &(*foo).c;
123+
Return()
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)