|
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