Skip to content

Commit 261895b

Browse files
dotdashgereeter
authored andcommitted
[MIR] Enhance the SimplifyCfg pass to merge consecutive blocks
1 parent aad4e56 commit 261895b

File tree

2 files changed

+120
-28
lines changed

2 files changed

+120
-28
lines changed

src/librustc_mir/transform/simplify_cfg.rs

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,7 @@ impl SimplifyCfg {
2121
SimplifyCfg
2222
}
2323

24-
fn remove_dead_blocks(&self, mir: &mut Mir) {
25-
let mut seen = vec![false; mir.basic_blocks.len()];
26-
27-
// These blocks are always required.
28-
seen[START_BLOCK.index()] = true;
29-
seen[END_BLOCK.index()] = true;
30-
seen[DIVERGE_BLOCK.index()] = true;
31-
32-
let mut worklist = vec![START_BLOCK];
33-
while let Some(bb) = worklist.pop() {
34-
for succ in mir.basic_block_data(bb).terminator.successors() {
35-
if !seen[succ.index()] {
36-
seen[succ.index()] = true;
37-
worklist.push(*succ);
38-
}
39-
}
40-
}
41-
42-
util::retain_basic_blocks(mir, &seen);
43-
}
44-
45-
fn remove_goto_chains(&self, mir: &mut Mir) -> bool {
24+
fn merge_consecutive_blocks(&self, mir: &mut Mir) -> bool {
4625

4726
// Find the target at the end of the jump chain, return None if there is a loop
4827
fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
@@ -65,25 +44,71 @@ impl SimplifyCfg {
6544
Some(target)
6645
}
6746

47+
let mut predecessor_map = util::build_predecessor_map(mir);
48+
6849
let mut changed = false;
69-
for bb in mir.all_basic_blocks() {
50+
let mut seen = vec![false; mir.basic_blocks.len()];
51+
let mut worklist = vec![START_BLOCK];
52+
while let Some(bb) = worklist.pop() {
7053
// Temporarily swap out the terminator we're modifying to keep borrowck happy
7154
let mut terminator = Terminator::Diverge;
7255
mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
7356

57+
// Shortcut chains of empty blocks that just jump from one to the next
7458
for target in terminator.successors_mut() {
7559
let new_target = match final_target(mir, *target) {
7660
Some(new_target) => new_target,
7761
None if mir.basic_block_data(bb).statements.is_empty() => bb,
7862
None => continue
7963
};
80-
changed |= *target != new_target;
81-
*target = new_target;
64+
65+
if *target != new_target {
66+
changed = true;
67+
predecessor_map.remove_predecessor(*target, bb);
68+
predecessor_map.add_predecessor(new_target, bb);
69+
*target = new_target;
70+
}
8271
}
8372

84-
mir.basic_block_data_mut(bb).terminator = terminator;
73+
// See if we can merge the target block into this one
74+
match terminator {
75+
Terminator::Goto { target } if target.index() > DIVERGE_BLOCK.index() &&
76+
predecessor_map.predecessors(target).len() == 1 => {
77+
changed = true;
78+
let mut other_data = BasicBlockData {
79+
statements: Vec::new(),
80+
terminator: Terminator::Goto { target: target}
81+
};
82+
mem::swap(&mut other_data, mir.basic_block_data_mut(target));
83+
84+
predecessor_map.replace_predecessor(target, bb, target);
85+
for succ in other_data.terminator.successors() {
86+
predecessor_map.replace_predecessor(*succ, target, bb);
87+
}
88+
89+
let data = mir.basic_block_data_mut(bb);
90+
data.statements.append(&mut other_data.statements);
91+
mem::swap(&mut data.terminator, &mut other_data.terminator);
92+
}
93+
_ => mir.basic_block_data_mut(bb).terminator = terminator
94+
}
95+
96+
for succ in mir.basic_block_data(bb).terminator.successors() {
97+
if !seen[succ.index()] {
98+
seen[succ.index()] = true;
99+
worklist.push(*succ);
100+
}
101+
}
85102
}
86103

104+
// These blocks must be retained, so mark them seen even if we didn't see them
105+
seen[START_BLOCK.index()] = true;
106+
seen[END_BLOCK.index()] = true;
107+
seen[DIVERGE_BLOCK.index()] = true;
108+
109+
// Now get rid of all the blocks we never saw
110+
util::retain_basic_blocks(mir, &seen);
111+
87112
changed
88113
}
89114

@@ -125,8 +150,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
125150
let mut changed = true;
126151
while changed {
127152
changed = self.simplify_branches(mir);
128-
changed |= self.remove_goto_chains(mir);
129-
self.remove_dead_blocks(mir);
153+
changed |= self.merge_consecutive_blocks(mir);
130154
}
131155

132156
// FIXME: Should probably be moved into some kind of pass manager

src/librustc_mir/transform/util.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use rustc::mir::repr::*;
12+
use rustc::mir::visit::*;
1213

1314
/// Update basic block ids in all terminators using the given replacements,
1415
/// useful e.g. after removal of several basic blocks to update all terminators
@@ -49,3 +50,70 @@ pub fn retain_basic_blocks(mir: &mut Mir, keep: &[bool]) {
4950

5051
update_basic_block_ids(mir, &replacements);
5152
}
53+
54+
// A simple map to perform quick lookups of the predecessors of a BasicBlock.
55+
// Since BasicBlocks usually only have a small number of predecessors, we use a
56+
// simple vector. Also, if a block has the same target more than once, for
57+
// example in a switch, it will appear in the target's predecessor list multiple
58+
// times. This allows to update the map more easily when modifying the graph.
59+
pub struct PredecessorMap {
60+
map: Vec<Vec<BasicBlock>>,
61+
}
62+
63+
impl PredecessorMap {
64+
pub fn new(num_blocks: usize) -> PredecessorMap {
65+
PredecessorMap {
66+
map: vec![Vec::new(); num_blocks],
67+
}
68+
}
69+
70+
pub fn predecessors(&self, block: BasicBlock) -> &[BasicBlock] {
71+
&self.map[block.index()]
72+
}
73+
74+
pub fn add_predecessor(&mut self, block: BasicBlock, predecessor: BasicBlock) {
75+
self.map[block.index()].push(predecessor);
76+
}
77+
78+
pub fn remove_predecessor(&mut self, block: BasicBlock, predecessor: BasicBlock) {
79+
let pos = self.map[block.index()].iter().position(|&p| p == predecessor).expect(
80+
&format!("{:?} is not registered as a predecessor of {:?}", predecessor, block));
81+
82+
self.map[block.index()].swap_remove(pos);
83+
}
84+
85+
pub fn replace_predecessor(&mut self, block: BasicBlock, old: BasicBlock, new: BasicBlock) {
86+
self.remove_predecessor(block, old);
87+
self.add_predecessor(block, new);
88+
}
89+
}
90+
91+
struct PredecessorVisitor {
92+
predecessor_map: PredecessorMap,
93+
}
94+
95+
impl PredecessorVisitor {
96+
fn new(num_blocks: usize) -> PredecessorVisitor {
97+
PredecessorVisitor {
98+
predecessor_map: PredecessorMap::new(num_blocks),
99+
}
100+
}
101+
}
102+
103+
impl<'tcx> Visitor<'tcx> for PredecessorVisitor {
104+
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
105+
self.predecessor_map = PredecessorMap::new(mir.basic_blocks.len());
106+
self.super_mir(mir);
107+
}
108+
109+
fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) {
110+
self.predecessor_map.add_predecessor(target, source);
111+
}
112+
}
113+
114+
pub fn build_predecessor_map(mir: &Mir) -> PredecessorMap {
115+
let mut v = PredecessorVisitor::new(mir.basic_blocks.len());
116+
v.visit_mir(mir);
117+
118+
v.predecessor_map
119+
}

0 commit comments

Comments
 (0)