Skip to content

Commit 1ebb07e

Browse files
committed
[MIR] Enhance the SimplifyCfg pass to merge consecutive blocks
1 parent c049541 commit 1ebb07e

File tree

3 files changed

+188
-59
lines changed

3 files changed

+188
-59
lines changed

src/librustc_mir/transform/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod remove_dead_blocks;
1212
pub mod simplify_cfg;
1313
pub mod erase_regions;
1414
pub mod no_landing_pads;
15+
pub mod predecessor_map;
1516
pub mod type_check;
1617
pub mod break_critical_edges;
1718
pub mod promote_consts;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::mir::repr::*;
12+
use rustc::mir::visit::Visitor;
13+
14+
// A simple map to perform quick lookups of the predecessors of a BasicBlock.
15+
// Since BasicBlocks usually only have a small number of predecessors, we use a
16+
// simple vector. Also, if a block has the same target more than once, for
17+
// example in a switch, it will appear in the target's predecessor list multiple
18+
// times. This allows to update the map more easily when modifying the graph.
19+
pub struct PredecessorMap {
20+
map: Vec<Vec<BasicBlock>>,
21+
}
22+
23+
impl PredecessorMap {
24+
pub fn from_mir(mir: &Mir) -> PredecessorMap {
25+
let mut map = PredecessorMap {
26+
map: vec![Vec::new(); mir.basic_blocks.len()],
27+
};
28+
29+
PredecessorVisitor { predecessor_map: &mut map }.visit_mir(mir);
30+
31+
map
32+
}
33+
34+
pub fn predecessors(&self, block: BasicBlock) -> &[BasicBlock] {
35+
&self.map[block.index()]
36+
}
37+
38+
pub fn add_predecessor(&mut self, block: BasicBlock, predecessor: BasicBlock) {
39+
self.map[block.index()].push(predecessor);
40+
}
41+
42+
pub fn remove_predecessor(&mut self, block: BasicBlock, predecessor: BasicBlock) {
43+
let pos = self.map[block.index()].iter().position(|&p| p == predecessor).expect(
44+
&format!("{:?} is not registered as a predecessor of {:?}", predecessor, block));
45+
46+
self.map[block.index()].swap_remove(pos);
47+
}
48+
49+
pub fn replace_predecessor(&mut self, block: BasicBlock, old: BasicBlock, new: BasicBlock) {
50+
self.remove_predecessor(block, old);
51+
self.add_predecessor(block, new);
52+
}
53+
54+
pub fn replace_successor(&mut self, block: BasicBlock, old: BasicBlock, new: BasicBlock) {
55+
self.remove_predecessor(old, block);
56+
self.add_predecessor(new, block);
57+
}
58+
}
59+
60+
struct PredecessorVisitor<'a> {
61+
predecessor_map: &'a mut PredecessorMap,
62+
}
63+
64+
impl<'a, 'tcx> Visitor<'tcx> for PredecessorVisitor<'a> {
65+
fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) {
66+
self.predecessor_map.add_predecessor(target, source);
67+
}
68+
}

src/librustc_mir/transform/simplify_cfg.rs

+119-59
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
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

19+
use super::predecessor_map::PredecessorMap;
1720
use super::remove_dead_blocks::RemoveDeadBlocks;
1821

1922
pub struct SimplifyCfg;
@@ -22,59 +25,133 @@ impl SimplifyCfg {
2225
pub fn new() -> SimplifyCfg {
2326
SimplifyCfg
2427
}
28+
}
29+
30+
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
31+
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
32+
simplify_branches(mir);
33+
RemoveDeadBlocks.run_pass(tcx, src, mir);
34+
merge_consecutive_blocks(mir);
35+
RemoveDeadBlocks.run_pass(tcx, src, mir);
36+
pretty::dump_mir(tcx, "simplify_cfg", &0, src, mir, None);
2537

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;
38+
// FIXME: Should probably be moved into some kind of pass manager
39+
mir.basic_blocks.shrink_to_fit();
40+
}
41+
}
42+
43+
impl Pass for SimplifyCfg {}
44+
45+
fn merge_consecutive_blocks(mir: &mut Mir) {
46+
let mut predecessor_map = PredecessorMap::from_mir(mir);
47+
48+
loop {
49+
let mut changed = false;
50+
let mut seen = BitVector::new(mir.basic_blocks.len());
51+
let mut worklist = vec![START_BLOCK];
52+
while let Some(bb) = worklist.pop() {
53+
// Temporarily take ownership of the terminator we're modifying to keep borrowck happy
54+
let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
55+
.expect("invalid terminator state");
56+
57+
// See if we can merge the target block into this one
58+
loop {
59+
let mut inner_change = false;
60+
61+
if let TerminatorKind::Goto { target } = terminator.kind {
62+
// Don't bother trying to merge a block into itself
63+
if target == bb {
64+
break;
65+
}
66+
67+
let num_preds = predecessor_map.predecessors(target).len();
68+
let num_insts = mir.basic_block_data(target).statements.len();
69+
match mir.basic_block_data(target).terminator().kind {
70+
_ if num_preds == 1 => {
71+
inner_change = true;
72+
let mut stmts = Vec::new();
73+
{
74+
let target_data = mir.basic_block_data_mut(target);
75+
mem::swap(&mut stmts, &mut target_data.statements);
76+
mem::swap(&mut terminator, target_data.terminator_mut());
77+
}
78+
79+
mir.basic_block_data_mut(bb).statements.append(&mut stmts);
80+
81+
predecessor_map.replace_predecessor(target, bb, target);
82+
for succ in terminator.successors().iter() {
83+
predecessor_map.replace_predecessor(*succ, target, bb);
4184
}
42-
seen.push(next);
43-
target = next;
4485
}
45-
_ => break
86+
TerminatorKind::Goto { target: new_target } if num_insts == 0 => {
87+
inner_change = true;
88+
terminator.kind = TerminatorKind::Goto { target: new_target };
89+
predecessor_map.replace_successor(bb, target, new_target);
90+
}
91+
_ => {}
92+
};
93+
}
94+
95+
for target in terminator.successors_mut() {
96+
let new_target = match final_target(mir, *target) {
97+
Some(new_target) => new_target,
98+
None if mir.basic_block_data(bb).statements.is_empty() => bb,
99+
None => continue
100+
};
101+
if *target != new_target {
102+
inner_change = true;
103+
predecessor_map.replace_successor(bb, *target, new_target);
104+
*target = new_target;
46105
}
47-
} else {
48-
break
106+
}
107+
108+
changed |= inner_change;
109+
if !inner_change {
110+
break;
49111
}
50112
}
51113

52-
Some(target)
114+
mir.basic_block_data_mut(bb).terminator = Some(terminator);
115+
116+
for succ in mir.basic_block_data(bb).terminator().successors().iter() {
117+
if seen.insert(succ.index()) {
118+
worklist.push(*succ);
119+
}
120+
}
53121
}
54122

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;
123+
if !changed {
124+
break;
125+
}
126+
}
127+
}
128+
129+
// Find the target at the end of the jump chain, return None if there is a loop
130+
fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
131+
// Keep track of already seen blocks to detect loops
132+
let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
133+
134+
while mir.basic_block_data(target).statements.is_empty() {
135+
// NB -- terminator may have been swapped with `None` in
136+
// merge_consecutive_blocks, in which case we have a cycle and just want
137+
// to stop
138+
match mir.basic_block_data(target).terminator {
139+
Some(Terminator { kind: TerminatorKind::Goto { target: next }, .. }) => {
140+
if seen.contains(&next) {
141+
return None;
142+
}
143+
seen.push(next);
144+
target = next;
71145
}
72-
mir.basic_block_data_mut(bb).terminator = Some(terminator);
146+
_ => break
73147
}
74-
changed
75148
}
76149

77-
fn simplify_branches(&self, mir: &mut Mir) -> bool {
150+
Some(target)
151+
}
152+
153+
fn simplify_branches(mir: &mut Mir) {
154+
loop {
78155
let mut changed = false;
79156

80157
for bb in mir.all_basic_blocks() {
@@ -106,25 +183,8 @@ impl SimplifyCfg {
106183
}
107184
}
108185

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);
186+
if !changed {
187+
break;
124188
}
125-
// FIXME: Should probably be moved into some kind of pass manager
126-
mir.basic_blocks.shrink_to_fit();
127189
}
128190
}
129-
130-
impl Pass for SimplifyCfg {}

0 commit comments

Comments
 (0)