Skip to content

Commit 5e317fc

Browse files
committed
simplifycfg: Preserve debuginfos when merging bbs
1 parent 6b5117e commit 5e317fc

File tree

5 files changed

+249
-5
lines changed

5 files changed

+249
-5
lines changed

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 39 additions & 5 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;
@@ -157,9 +157,19 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
157157

158158
let mut terminator =
159159
self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
160-
161-
terminator
162-
.successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed));
160+
let mut debuginfos = if let Some(first_succ) = terminator.successors().next()
161+
&& terminator.successors().all(|s| s == first_succ)
162+
{
163+
Some(Vec::new())
164+
} else {
165+
None
166+
};
167+
terminator.successors_mut(|successor| {
168+
self.collapse_goto_chain(successor, &mut changed, debuginfos.as_mut())
169+
});
170+
if let Some(mut debuginfos) = debuginfos {
171+
self.basic_blocks[bb].after_last_stmt_debuginfos.append(&mut debuginfos);
172+
}
163173

164174
let mut inner_changed = true;
165175
merged_blocks.clear();
@@ -176,10 +186,19 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
176186
if statements_to_merge > 0 {
177187
let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
178188
statements.reserve(statements_to_merge);
189+
let mut parent_bb_last_debuginfos =
190+
std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
179191
for &from in &merged_blocks {
192+
if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
193+
parent_bb_last_debuginfos.append(&mut stmt.debuginfos);
194+
std::mem::swap(&mut parent_bb_last_debuginfos, &mut stmt.debuginfos);
195+
}
180196
statements.append(&mut self.basic_blocks[from].statements);
197+
parent_bb_last_debuginfos
198+
.append(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
181199
}
182200
self.basic_blocks[bb].statements = statements;
201+
self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
183202
}
184203

185204
self.basic_blocks[bb].terminator = Some(terminator);
@@ -214,7 +233,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
214233
}
215234

216235
/// Collapse a goto chain starting from `start`
217-
fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
236+
fn collapse_goto_chain(
237+
&mut self,
238+
start: &mut BasicBlock,
239+
changed: &mut bool,
240+
pred_debuginfos: Option<&mut Vec<StmtDebugInfo<'tcx>>>,
241+
) {
218242
// Using `SmallVec` here, because in some logs on libcore oli-obk saw many single-element
219243
// goto chains. We should probably benchmark different sizes.
220244
let mut terminators: SmallVec<[_; 1]> = Default::default();
@@ -228,11 +252,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
228252
}
229253
let last = current;
230254
*start = last;
255+
let mut debuginfos = Vec::new();
231256
while let Some((current, mut terminator)) = terminators.pop() {
232257
let Terminator { kind: TerminatorKind::Goto { ref mut target }, .. } = terminator
233258
else {
234259
unreachable!();
235260
};
261+
if *target != last {
262+
self.basic_blocks[current]
263+
.after_last_stmt_debuginfos
264+
.extend_from_slice(&debuginfos);
265+
}
266+
debuginfos.extend_from_slice(&self.basic_blocks[current].after_last_stmt_debuginfos);
236267
*changed |= *target != last;
237268
*target = last;
238269
debug!("collapsing goto chain from {:?} to {:?}", current, target);
@@ -247,6 +278,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
247278
}
248279
self.basic_blocks[current].terminator = Some(terminator);
249280
}
281+
if let Some(parent_debuginfos) = pred_debuginfos {
282+
parent_debuginfos.append(&mut debuginfos);
283+
}
250284
}
251285

252286
// merge a block with 1 `goto` predecessor to its parent
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: AssignRef(_3, ((*_1).0: i32))
17+
- goto -> bb2;
18+
- }
19+
-
20+
- bb2: {
21+
// DBG: AssignRef(_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: AssignRef(_3, ((*_1).0: i32))
20+
- goto -> bb2;
21+
- }
22+
-
23+
- bb2: {
24+
// DBG: AssignRef(_4, ((*_1).1: i64))
25+
_0 = copy ((*_1).2: i32);
26+
// DBG: AssignRef(_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: AssignRef(_2, ((*_1).0: i32))
19+
- goto -> bb2;
20+
- }
21+
-
22+
- bb2: {
23+
// DBG: AssignRef(_3, ((*_1).1: i64))
24+
_0 = copy ((*_1).2: i32);
25+
// DBG: AssignRef(_4, ((*_1).2: i32))
26+
return;
27+
}
28+
}
29+
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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: AssignRef([[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+
_foo_a = &(*foo).a;
37+
Goto(ret)
38+
}
39+
ret = {
40+
_foo_b = &(*foo).b;
41+
RET = (*foo).c;
42+
Return()
43+
}
44+
}
45+
}
46+
47+
// EMIT_MIR simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff
48+
#[custom_mir(dialect = "runtime")]
49+
pub fn preserve_debuginfo_1(foo: &Foo, v: &mut bool) -> i32 {
50+
// CHECK-LABEL: fn preserve_debuginfo_1
51+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
52+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
53+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
54+
// CHECK: bb0: {
55+
// CHECK-NEXT: (*_2) = const true;
56+
// CHECK-NEXT: DBG: AssignRef([[foo_a]], ((*_1).0: i32))
57+
// CHECK-NEXT: DBG: AssignRef([[foo_b]], ((*_1).1: i64))
58+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
59+
// CHECK-NEXT: DBG: AssignRef([[foo_c]], ((*_1).2: i32))
60+
// CHECK-NEXT: return;
61+
mir! {
62+
let _foo_a: &i32;
63+
let _foo_b: &i64;
64+
let _foo_c: &i32;
65+
debug foo_a => _foo_a;
66+
debug foo_b => _foo_b;
67+
debug foo_c => _foo_c;
68+
{
69+
match true {
70+
true => tmp,
71+
_ => ret,
72+
}
73+
}
74+
tmp = {
75+
*v = true;
76+
_foo_a = &(*foo).a;
77+
Goto(ret)
78+
}
79+
ret = {
80+
_foo_b = &(*foo).b;
81+
RET = (*foo).c;
82+
_foo_c = &(*foo).c;
83+
Return()
84+
}
85+
}
86+
}
87+
88+
// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff
89+
#[custom_mir(dialect = "runtime")]
90+
pub fn preserve_debuginfo_2(foo: &Foo) -> i32 {
91+
// CHECK-LABEL: fn preserve_debuginfo_2
92+
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
93+
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
94+
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
95+
// CHECK: bb0: {
96+
// CHECK-NEXT: DBG: AssignRef([[foo_a]], ((*_1).0: i32))
97+
// CHECK-NEXT: DBG: AssignRef([[foo_b]], ((*_1).1: i64))
98+
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
99+
// CHECK-NEXT: DBG: AssignRef([[foo_c]], ((*_1).2: i32))
100+
// CHECK-NEXT: return;
101+
mir! {
102+
let _foo_a: &i32;
103+
let _foo_b: &i64;
104+
let _foo_c: &i32;
105+
debug foo_a => _foo_a;
106+
debug foo_b => _foo_b;
107+
debug foo_c => _foo_c;
108+
{
109+
match true {
110+
true => tmp,
111+
_ => ret,
112+
}
113+
}
114+
tmp = {
115+
_foo_a = &(*foo).a;
116+
Goto(ret)
117+
}
118+
ret = {
119+
_foo_b = &(*foo).b;
120+
RET = (*foo).c;
121+
_foo_c = &(*foo).c;
122+
Return()
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)