Skip to content

Commit 494bc14

Browse files
committed
Rollup merge of rust-lang#33552 - dotdash:scfg, r=luqmana
[MIR] Enhance the SimplifyCfg pass to merge consecutive blocks Updated from rust-lang#30238, including the changes suggested by @Aatch.
2 parents 96b6e8f + 8ad6d27 commit 494bc14

File tree

1 file changed

+124
-59
lines changed

1 file changed

+124
-59
lines changed

src/librustc_mir/transform/simplify_cfg.rs

+124-59
Original file line numberDiff line numberDiff line change
@@ -8,73 +8,155 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use rustc_data_structures::bitvec::BitVector;
1112
use rustc::middle::const_val::ConstVal;
1213
use rustc::ty::TyCtxt;
1314
use rustc::mir::repr::*;
1415
use rustc::mir::transform::{MirPass, MirSource, Pass};
1516
use pretty;
17+
use std::mem;
1618

1719
use super::remove_dead_blocks::RemoveDeadBlocks;
1820

21+
use traversal;
22+
1923
pub struct SimplifyCfg;
2024

2125
impl SimplifyCfg {
2226
pub fn new() -> SimplifyCfg {
2327
SimplifyCfg
2428
}
29+
}
30+
31+
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
32+
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
33+
simplify_branches(mir);
34+
RemoveDeadBlocks.run_pass(tcx, src, mir);
35+
merge_consecutive_blocks(mir);
36+
RemoveDeadBlocks.run_pass(tcx, src, mir);
37+
pretty::dump_mir(tcx, "simplify_cfg", &0, src, mir, None);
38+
39+
// FIXME: Should probably be moved into some kind of pass manager
40+
mir.basic_blocks.shrink_to_fit();
41+
}
42+
}
43+
44+
impl Pass for SimplifyCfg {}
45+
46+
fn merge_consecutive_blocks(mir: &mut Mir) {
47+
// Build the precedecessor map for the MIR
48+
let mut pred_count = vec![0u32; mir.basic_blocks.len()];
49+
for (_, data) in traversal::preorder(mir) {
50+
if let Some(ref term) = data.terminator {
51+
for &tgt in term.successors().iter() {
52+
pred_count[tgt.index()] += 1;
53+
}
54+
}
55+
}
56+
57+
loop {
58+
let mut changed = false;
59+
let mut seen = BitVector::new(mir.basic_blocks.len());
60+
let mut worklist = vec![START_BLOCK];
61+
while let Some(bb) = worklist.pop() {
62+
// Temporarily take ownership of the terminator we're modifying to keep borrowck happy
63+
let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
64+
.expect("invalid terminator state");
65+
66+
// See if we can merge the target block into this one
67+
loop {
68+
let mut inner_change = false;
2569

26-
fn remove_goto_chains(&self, mir: &mut Mir) -> bool {
27-
// Find the target at the end of the jump chain, return None if there is a loop
28-
fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
29-
// Keep track of already seen blocks to detect loops
30-
let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
31-
32-
while mir.basic_block_data(target).statements.is_empty() {
33-
// NB -- terminator may have been swapped with `None`
34-
// below, in which case we have a cycle and just want
35-
// to stop
36-
if let Some(ref terminator) = mir.basic_block_data(target).terminator {
37-
match terminator.kind {
38-
TerminatorKind::Goto { target: next } => {
39-
if seen.contains(&next) {
40-
return None;
70+
if let TerminatorKind::Goto { target } = terminator.kind {
71+
// Don't bother trying to merge a block into itself
72+
if target == bb {
73+
break;
74+
}
75+
76+
let num_insts = mir.basic_block_data(target).statements.len();
77+
match mir.basic_block_data(target).terminator().kind {
78+
TerminatorKind::Goto { target: new_target } if num_insts == 0 => {
79+
inner_change = true;
80+
terminator.kind = TerminatorKind::Goto { target: new_target };
81+
pred_count[target.index()] -= 1;
82+
pred_count[new_target.index()] += 1;
83+
}
84+
_ if pred_count[target.index()] == 1 => {
85+
inner_change = true;
86+
let mut stmts = Vec::new();
87+
{
88+
let target_data = mir.basic_block_data_mut(target);
89+
mem::swap(&mut stmts, &mut target_data.statements);
90+
mem::swap(&mut terminator, target_data.terminator_mut());
4191
}
42-
seen.push(next);
43-
target = next;
92+
93+
mir.basic_block_data_mut(bb).statements.append(&mut stmts);
4494
}
45-
_ => break
95+
_ => {}
96+
};
97+
}
98+
99+
for target in terminator.successors_mut() {
100+
let new_target = match final_target(mir, *target) {
101+
Some(new_target) => new_target,
102+
None if mir.basic_block_data(bb).statements.is_empty() => bb,
103+
None => continue
104+
};
105+
if *target != new_target {
106+
inner_change = true;
107+
pred_count[target.index()] -= 1;
108+
pred_count[new_target.index()] += 1;
109+
*target = new_target;
46110
}
47-
} else {
48-
break
111+
}
112+
113+
changed |= inner_change;
114+
if !inner_change {
115+
break;
49116
}
50117
}
51118

52-
Some(target)
119+
mir.basic_block_data_mut(bb).terminator = Some(terminator);
120+
121+
for succ in mir.basic_block_data(bb).terminator().successors().iter() {
122+
if seen.insert(succ.index()) {
123+
worklist.push(*succ);
124+
}
125+
}
53126
}
54127

55-
let mut changed = false;
56-
for bb in mir.all_basic_blocks() {
57-
// Temporarily take ownership of the terminator we're modifying to keep borrowck happy
58-
let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
59-
.expect("invalid terminator state");
60-
61-
debug!("remove_goto_chains: bb={:?} terminator={:?}", bb, terminator);
62-
63-
for target in terminator.successors_mut() {
64-
let new_target = match final_target(mir, *target) {
65-
Some(new_target) => new_target,
66-
None if mir.basic_block_data(bb).statements.is_empty() => bb,
67-
None => continue
68-
};
69-
changed |= *target != new_target;
70-
*target = new_target;
128+
if !changed {
129+
break;
130+
}
131+
}
132+
}
133+
134+
// Find the target at the end of the jump chain, return None if there is a loop
135+
fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
136+
// Keep track of already seen blocks to detect loops
137+
let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
138+
139+
while mir.basic_block_data(target).statements.is_empty() {
140+
// NB -- terminator may have been swapped with `None` in
141+
// merge_consecutive_blocks, in which case we have a cycle and just want
142+
// to stop
143+
match mir.basic_block_data(target).terminator {
144+
Some(Terminator { kind: TerminatorKind::Goto { target: next }, .. }) => {
145+
if seen.contains(&next) {
146+
return None;
147+
}
148+
seen.push(next);
149+
target = next;
71150
}
72-
mir.basic_block_data_mut(bb).terminator = Some(terminator);
151+
_ => break
73152
}
74-
changed
75153
}
76154

77-
fn simplify_branches(&self, mir: &mut Mir) -> bool {
155+
Some(target)
156+
}
157+
158+
fn simplify_branches(mir: &mut Mir) {
159+
loop {
78160
let mut changed = false;
79161

80162
for bb in mir.all_basic_blocks() {
@@ -106,25 +188,8 @@ impl SimplifyCfg {
106188
}
107189
}
108190

109-
changed
110-
}
111-
}
112-
113-
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
114-
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
115-
src: MirSource, mir: &mut Mir<'tcx>) {
116-
let mut counter = 0;
117-
let mut changed = true;
118-
while changed {
119-
pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None);
120-
counter += 1;
121-
changed = self.simplify_branches(mir);
122-
changed |= self.remove_goto_chains(mir);
123-
RemoveDeadBlocks.run_pass(tcx, src, mir);
191+
if !changed {
192+
break;
124193
}
125-
// FIXME: Should probably be moved into some kind of pass manager
126-
mir.basic_blocks.shrink_to_fit();
127194
}
128195
}
129-
130-
impl Pass for SimplifyCfg {}

0 commit comments

Comments
 (0)