|
8 | 8 | // option. This file may not be copied, modified, or distributed |
9 | 9 | // except according to those terms. |
10 | 10 |
|
| 11 | +use rustc_data_structures::bitvec::BitVector; |
11 | 12 | use rustc::middle::const_val::ConstVal; |
12 | 13 | use rustc::ty::TyCtxt; |
13 | 14 | use rustc::mir::repr::*; |
14 | 15 | use rustc::mir::transform::{MirPass, MirSource, Pass}; |
15 | 16 | use pretty; |
| 17 | +use std::mem; |
16 | 18 |
|
17 | 19 | use super::remove_dead_blocks::RemoveDeadBlocks; |
18 | 20 |
|
| 21 | +use traversal; |
| 22 | + |
19 | 23 | pub struct SimplifyCfg; |
20 | 24 |
|
21 | 25 | impl SimplifyCfg { |
22 | 26 | pub fn new() -> SimplifyCfg { |
23 | 27 | SimplifyCfg |
24 | 28 | } |
| 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; |
25 | 69 |
|
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()); |
41 | 91 | } |
42 | | - seen.push(next); |
43 | | - target = next; |
| 92 | + |
| 93 | + mir.basic_block_data_mut(bb).statements.append(&mut stmts); |
44 | 94 | } |
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; |
46 | 110 | } |
47 | | - } else { |
48 | | - break |
| 111 | + } |
| 112 | + |
| 113 | + changed |= inner_change; |
| 114 | + if !inner_change { |
| 115 | + break; |
49 | 116 | } |
50 | 117 | } |
51 | 118 |
|
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 | + } |
53 | 126 | } |
54 | 127 |
|
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; |
71 | 150 | } |
72 | | - mir.basic_block_data_mut(bb).terminator = Some(terminator); |
| 151 | + _ => break |
73 | 152 | } |
74 | | - changed |
75 | 153 | } |
76 | 154 |
|
77 | | - fn simplify_branches(&self, mir: &mut Mir) -> bool { |
| 155 | + Some(target) |
| 156 | +} |
| 157 | + |
| 158 | +fn simplify_branches(mir: &mut Mir) { |
| 159 | + loop { |
78 | 160 | let mut changed = false; |
79 | 161 |
|
80 | 162 | for bb in mir.all_basic_blocks() { |
@@ -106,25 +188,8 @@ impl SimplifyCfg { |
106 | 188 | } |
107 | 189 | } |
108 | 190 |
|
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; |
124 | 193 | } |
125 | | - // FIXME: Should probably be moved into some kind of pass manager |
126 | | - mir.basic_blocks.shrink_to_fit(); |
127 | 194 | } |
128 | 195 | } |
129 | | - |
130 | | -impl Pass for SimplifyCfg {} |
|
0 commit comments