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 ;
1112use rustc:: middle:: const_val:: ConstVal ;
1213use rustc:: ty:: TyCtxt ;
1314use rustc:: mir:: repr:: * ;
1415use rustc:: mir:: transform:: { MirPass , MirSource , Pass } ;
1516use pretty;
17+ use std:: mem;
1618
19+ use super :: predecessor_map:: PredecessorMap ;
1720use super :: remove_dead_blocks:: RemoveDeadBlocks ;
1821
1922pub 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