simplifycfg: Preserve debuginfos when merging bbs
This commit is contained in:
parent
571412f819
commit
cc93132ae4
8 changed files with 322 additions and 3 deletions
|
|
@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> {
|
|||
self.kind.successors()
|
||||
}
|
||||
|
||||
/// Return `Some` if all successors are identical.
|
||||
#[inline]
|
||||
pub fn identical_successor(&self) -> Option<BasicBlock> {
|
||||
let mut successors = self.successors();
|
||||
let first_succ = successors.next()?;
|
||||
if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
|
||||
self.kind.successors_mut(f)
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
|||
// statements itself to avoid moving the (relatively) large statements twice.
|
||||
// We do not push the statements directly into the target block (`bb`) as that is slower
|
||||
// due to additional reallocations
|
||||
let mut merged_blocks = Vec::new();
|
||||
let mut merged_blocks: Vec<BasicBlock> = Vec::new();
|
||||
let mut outer_changed = false;
|
||||
loop {
|
||||
let mut changed = false;
|
||||
|
|
@ -159,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
|||
let mut terminator =
|
||||
self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
|
||||
|
||||
terminator
|
||||
.successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed));
|
||||
terminator.successors_mut(|successor| {
|
||||
self.collapse_goto_chain(successor, &mut changed);
|
||||
});
|
||||
|
||||
let mut inner_changed = true;
|
||||
merged_blocks.clear();
|
||||
|
|
@ -177,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
|||
if statements_to_merge > 0 {
|
||||
let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
|
||||
statements.reserve(statements_to_merge);
|
||||
let mut parent_bb_last_debuginfos =
|
||||
std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
|
||||
for &from in &merged_blocks {
|
||||
if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
|
||||
stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos);
|
||||
}
|
||||
statements.append(&mut self.basic_blocks[from].statements);
|
||||
parent_bb_last_debuginfos =
|
||||
std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
|
||||
}
|
||||
self.basic_blocks[bb].statements = statements;
|
||||
self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
|
||||
}
|
||||
|
||||
self.basic_blocks[bb].terminator = Some(terminator);
|
||||
|
|
@ -220,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
|||
// goto chains. We should probably benchmark different sizes.
|
||||
let mut terminators: SmallVec<[_; 1]> = Default::default();
|
||||
let mut current = *start;
|
||||
// If each successor has only one predecessor, it's a trivial goto chain.
|
||||
// We can move all debuginfos to the last basic block.
|
||||
let mut trivial_goto_chain = true;
|
||||
while let Some(terminator) = self.take_terminator_if_simple_goto(current) {
|
||||
let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else {
|
||||
unreachable!();
|
||||
};
|
||||
trivial_goto_chain &= self.pred_count[target] == 1;
|
||||
terminators.push((current, terminator));
|
||||
current = target;
|
||||
}
|
||||
|
|
@ -235,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
|||
else {
|
||||
unreachable!();
|
||||
};
|
||||
if trivial_goto_chain {
|
||||
let mut pred_debuginfos =
|
||||
std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos);
|
||||
let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut()
|
||||
{
|
||||
&mut stmt.debuginfos
|
||||
} else {
|
||||
&mut self.basic_blocks[last].after_last_stmt_debuginfos
|
||||
};
|
||||
debuginfos.prepend(&mut pred_debuginfos);
|
||||
}
|
||||
*changed |= *target != last;
|
||||
*target = last;
|
||||
debug!("collapsing goto chain from {:?} to {:?}", current, target);
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@
|
|||
-
|
||||
- bb1: {
|
||||
- // DBG: _3 = &((*_1).0: i32);
|
||||
- nop;
|
||||
- goto -> bb2;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
// DBG: _4 = &((*_1).1: i64);
|
||||
- nop;
|
||||
_0 = copy ((*_1).2: i32);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,16 @@
|
|||
- bb1: {
|
||||
(*_2) = const true;
|
||||
// DBG: _3 = &((*_1).0: i32);
|
||||
- nop;
|
||||
- goto -> bb2;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
// DBG: _4 = &((*_1).1: i64);
|
||||
- nop;
|
||||
_0 = copy ((*_1).2: i32);
|
||||
// DBG: _5 = &((*_1).2: i32);
|
||||
- nop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,16 @@
|
|||
-
|
||||
- bb1: {
|
||||
// DBG: _2 = &((*_1).0: i32);
|
||||
- nop;
|
||||
- goto -> bb2;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
// DBG: _3 = &((*_1).1: i64);
|
||||
- nop;
|
||||
_0 = copy ((*_1).2: i32);
|
||||
// DBG: _4 = &((*_1).2: i32);
|
||||
- nop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
- // MIR for `preserve_debuginfo_3` before SimplifyCfg-final
|
||||
+ // MIR for `preserve_debuginfo_3` after SimplifyCfg-final
|
||||
|
||||
fn preserve_debuginfo_3(_1: &Foo, _2: bool) -> i32 {
|
||||
debug foo_a => _3;
|
||||
debug foo_b => _4;
|
||||
debug foo_c => _5;
|
||||
let mut _0: i32;
|
||||
let mut _3: &i32;
|
||||
let mut _4: &i64;
|
||||
let mut _5: &i32;
|
||||
|
||||
bb0: {
|
||||
- switchInt(copy _2) -> [1: bb1, otherwise: bb2];
|
||||
+ switchInt(copy _2) -> [1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
- // DBG: _3 = &((*_1).0: i32);
|
||||
- nop;
|
||||
- goto -> bb3;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
// DBG: _4 = &((*_1).1: i64);
|
||||
- nop;
|
||||
_0 = copy ((*_1).2: i32);
|
||||
return;
|
||||
}
|
||||
|
||||
- bb3: {
|
||||
+ bb2: {
|
||||
+ // DBG: _3 = &((*_1).0: i32);
|
||||
// DBG: _5 = &((*_1).2: i32);
|
||||
- nop;
|
||||
_0 = copy ((*_1).0: i32);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
- // MIR for `preserve_debuginfo_identical_succs` before SimplifyCfg-final
|
||||
+ // MIR for `preserve_debuginfo_identical_succs` after SimplifyCfg-final
|
||||
|
||||
fn preserve_debuginfo_identical_succs(_1: &Foo, _2: bool) -> i32 {
|
||||
debug foo_a => _3;
|
||||
debug foo_b => _4;
|
||||
debug foo_c => _5;
|
||||
let mut _0: i32;
|
||||
let mut _3: &i32;
|
||||
let mut _4: &i64;
|
||||
let mut _5: &i32;
|
||||
|
||||
bb0: {
|
||||
- switchInt(copy _2) -> [1: bb1, otherwise: bb1];
|
||||
- }
|
||||
-
|
||||
- bb1: {
|
||||
// DBG: _3 = &((*_1).0: i32);
|
||||
- nop;
|
||||
- goto -> bb2;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
// DBG: _4 = &((*_1).1: i64);
|
||||
- nop;
|
||||
_0 = copy ((*_1).2: i32);
|
||||
// DBG: _5 = &((*_1).2: i32);
|
||||
- nop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
207
tests/mir-opt/debuginfo/simplifycfg.rs
Normal file
207
tests/mir-opt/debuginfo/simplifycfg.rs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
//@ test-mir-pass: SimplifyCfg-final
|
||||
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination-initial
|
||||
|
||||
#![feature(core_intrinsics, custom_mir)]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::intrinsics::mir::*;
|
||||
|
||||
pub struct Foo {
|
||||
a: i32,
|
||||
b: i64,
|
||||
c: i32,
|
||||
}
|
||||
|
||||
// EMIT_MIR simplifycfg.drop_debuginfo.SimplifyCfg-final.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn drop_debuginfo(foo: &Foo, c: bool) -> i32 {
|
||||
// CHECK-LABEL: fn drop_debuginfo
|
||||
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
|
||||
// CHECK-NEXT: return;
|
||||
mir! {
|
||||
let _foo_a: &i32;
|
||||
let _foo_b: &i64;
|
||||
debug foo_a => _foo_a;
|
||||
debug foo_b => _foo_b;
|
||||
{
|
||||
match c {
|
||||
true => tmp,
|
||||
_ => ret,
|
||||
}
|
||||
}
|
||||
tmp = {
|
||||
// Because we don't know if `c` is always true, we must drop this debuginfo.
|
||||
_foo_a = &(*foo).a;
|
||||
Goto(ret)
|
||||
}
|
||||
ret = {
|
||||
_foo_b = &(*foo).b;
|
||||
RET = (*foo).c;
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn preserve_debuginfo_1(foo: &Foo, v: &mut bool) -> i32 {
|
||||
// CHECK-LABEL: fn preserve_debuginfo_1
|
||||
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
|
||||
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
|
||||
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: (*_2) = const true;
|
||||
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
|
||||
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
|
||||
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
|
||||
// CHECK-NEXT: return;
|
||||
mir! {
|
||||
let _foo_a: &i32;
|
||||
let _foo_b: &i64;
|
||||
let _foo_c: &i32;
|
||||
debug foo_a => _foo_a;
|
||||
debug foo_b => _foo_b;
|
||||
debug foo_c => _foo_c;
|
||||
{
|
||||
Goto(tmp)
|
||||
}
|
||||
tmp = {
|
||||
*v = true;
|
||||
_foo_a = &(*foo).a;
|
||||
Goto(ret)
|
||||
}
|
||||
ret = {
|
||||
_foo_b = &(*foo).b;
|
||||
RET = (*foo).c;
|
||||
_foo_c = &(*foo).c;
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn preserve_debuginfo_2(foo: &Foo) -> i32 {
|
||||
// CHECK-LABEL: fn preserve_debuginfo_2
|
||||
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
|
||||
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
|
||||
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
|
||||
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
|
||||
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
|
||||
// CHECK-NEXT: return;
|
||||
mir! {
|
||||
let _foo_a: &i32;
|
||||
let _foo_b: &i64;
|
||||
let _foo_c: &i32;
|
||||
debug foo_a => _foo_a;
|
||||
debug foo_b => _foo_b;
|
||||
debug foo_c => _foo_c;
|
||||
{
|
||||
Goto(tmp)
|
||||
}
|
||||
tmp = {
|
||||
_foo_a = &(*foo).a;
|
||||
Goto(ret)
|
||||
}
|
||||
ret = {
|
||||
_foo_b = &(*foo).b;
|
||||
RET = (*foo).c;
|
||||
_foo_c = &(*foo).c;
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn preserve_debuginfo_3(foo: &Foo, c: bool) -> i32 {
|
||||
// CHECK-LABEL: fn preserve_debuginfo_3
|
||||
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
|
||||
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
|
||||
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: switchInt(copy _2) -> [1: bb2, otherwise: bb1];
|
||||
// CHECK: bb1: {
|
||||
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
|
||||
// CHECK-NEXT: return;
|
||||
// CHECK: bb2: {
|
||||
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
|
||||
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).0: i32);
|
||||
// CHECK-NEXT: return;
|
||||
mir! {
|
||||
let _foo_a: &i32;
|
||||
let _foo_b: &i64;
|
||||
let _foo_c: &i32;
|
||||
debug foo_a => _foo_a;
|
||||
debug foo_b => _foo_b;
|
||||
debug foo_c => _foo_c;
|
||||
{
|
||||
match c {
|
||||
true => tmp,
|
||||
_ => ret,
|
||||
}
|
||||
}
|
||||
tmp = {
|
||||
_foo_a = &(*foo).a;
|
||||
Goto(ret_1)
|
||||
}
|
||||
ret = {
|
||||
_foo_b = &(*foo).b;
|
||||
RET = (*foo).c;
|
||||
Return()
|
||||
}
|
||||
ret_1 = {
|
||||
_foo_c = &(*foo).c;
|
||||
RET = (*foo).a;
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn preserve_debuginfo_identical_succs(foo: &Foo, c: bool) -> i32 {
|
||||
// CHECK-LABEL: fn preserve_debuginfo_identical_succs
|
||||
// CHECK: debug foo_a => [[foo_a:_[0-9]+]];
|
||||
// CHECK: debug foo_b => [[foo_b:_[0-9]+]];
|
||||
// CHECK: debug foo_c => [[foo_c:_[0-9]+]];
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32)
|
||||
// CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64)
|
||||
// CHECK-NEXT: _0 = copy ((*_1).2: i32);
|
||||
// CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32)
|
||||
// CHECK-NEXT: return;
|
||||
mir! {
|
||||
let _foo_a: &i32;
|
||||
let _foo_b: &i64;
|
||||
let _foo_c: &i32;
|
||||
debug foo_a => _foo_a;
|
||||
debug foo_b => _foo_b;
|
||||
debug foo_c => _foo_c;
|
||||
{
|
||||
match c {
|
||||
true => tmp,
|
||||
_ => tmp,
|
||||
}
|
||||
}
|
||||
tmp = {
|
||||
_foo_a = &(*foo).a;
|
||||
Goto(ret)
|
||||
}
|
||||
ret = {
|
||||
_foo_b = &(*foo).b;
|
||||
RET = (*foo).c;
|
||||
_foo_c = &(*foo).c;
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue