1 // Copyright 2015 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.
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.
11 use rustc_data_structures::bitvec::BitVector;
12 use rustc::middle::const_val::ConstVal;
13 use rustc::ty::TyCtxt;
14 use rustc::mir::repr::*;
15 use rustc::mir::transform::{MirPass, MirSource, Pass};
19 use super::remove_dead_blocks::RemoveDeadBlocks;
23 pub struct SimplifyCfg;
26 pub fn new() -> SimplifyCfg {
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);
39 // FIXME: Should probably be moved into some kind of pass manager
40 mir.basic_blocks.shrink_to_fit();
44 impl Pass for SimplifyCfg {}
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;
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");
66 // See if we can merge the target block into this one
68 let mut inner_change = false;
70 if let TerminatorKind::Goto { target } = terminator.kind {
71 // Don't bother trying to merge a block into itself
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 => {
80 terminator.kind = TerminatorKind::Goto { target: new_target };
81 pred_count[target.index()] -= 1;
82 pred_count[new_target.index()] += 1;
84 _ if pred_count[target.index()] == 1 => {
86 let mut stmts = Vec::new();
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());
93 mir.basic_block_data_mut(bb).statements.append(&mut stmts);
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,
105 if *target != new_target {
107 pred_count[target.index()] -= 1;
108 pred_count[new_target.index()] += 1;
109 *target = new_target;
113 changed |= inner_change;
119 mir.basic_block_data_mut(bb).terminator = Some(terminator);
121 for succ in mir.basic_block_data(bb).terminator().successors().iter() {
122 if seen.insert(succ.index()) {
123 worklist.push(*succ);
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);
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
143 match mir.basic_block_data(target).terminator {
144 Some(Terminator { kind: TerminatorKind::Goto { target: next }, .. }) => {
145 if seen.contains(&next) {
158 fn simplify_branches(mir: &mut Mir) {
160 let mut changed = false;
162 for bb in mir.all_basic_blocks() {
163 let basic_block = mir.basic_block_data_mut(bb);
164 let mut terminator = basic_block.terminator_mut();
165 terminator.kind = match terminator.kind {
166 TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => {
168 TerminatorKind::Goto { target: targets.0 }
171 TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
172 literal: Literal::Value {
173 value: ConstVal::Bool(cond)
178 TerminatorKind::Goto { target: targets.0 }
180 TerminatorKind::Goto { target: targets.1 }
184 TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
185 literal: Literal::Value {
186 value: ConstVal::Bool(cond)
188 }), expected, .. } if cond == expected => {
190 TerminatorKind::Goto { target: target }
193 TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
195 TerminatorKind::Goto { target: targets[0] }