3434
3535use rustc_data_structures:: bitvec:: BitVector ;
3636use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
37- use rustc:: middle:: const_val:: ConstVal ;
3837use rustc:: ty:: TyCtxt ;
3938use rustc:: mir:: repr:: * ;
4039use rustc:: mir:: transform:: { MirPass , MirSource , Pass } ;
4140use rustc:: mir:: traversal;
42-
4341use std:: fmt;
44- use std:: mem;
4542
4643pub struct SimplifyCfg < ' a > { label : & ' a str }
4744
@@ -53,9 +50,7 @@ impl<'a> SimplifyCfg<'a> {
5350
5451impl < ' l , ' tcx > MirPass < ' tcx > for SimplifyCfg < ' l > {
5552 fn run_pass < ' a > ( & mut self , _tcx : TyCtxt < ' a , ' tcx , ' tcx > , _src : MirSource , mir : & mut Mir < ' tcx > ) {
56- simplify_branches ( mir) ;
57- remove_dead_blocks ( mir) ;
58- merge_consecutive_blocks ( mir) ;
53+ CfgSimplifier :: new ( mir) . simplify ( ) ;
5954 remove_dead_blocks ( mir) ;
6055
6156 // FIXME: Should probably be moved into some kind of pass manager
@@ -70,155 +65,155 @@ impl<'l> Pass for SimplifyCfg<'l> {
7065 }
7166}
7267
73- fn merge_consecutive_blocks ( mir : & mut Mir ) {
74- let mut pred_count: IndexVec < _ , _ > =
75- mir. predecessors ( ) . iter ( ) . map ( |ps| ps. len ( ) ) . collect ( ) ;
76-
77- loop {
78- let mut changed = false ;
79- let mut seen = BitVector :: new ( mir. basic_blocks ( ) . len ( ) ) ;
80- let mut worklist = vec ! [ START_BLOCK ] ;
81- while let Some ( bb) = worklist. pop ( ) {
82- // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
83- let mut terminator = mir[ bb] . terminator . take ( ) . expect ( "invalid terminator state" ) ;
84-
85- // See if we can merge the target block into this one
86- loop {
87- let mut inner_change = false ;
88-
89- if let TerminatorKind :: Goto { target } = terminator. kind {
90- // Don't bother trying to merge a block into itself
91- if target == bb {
92- break ;
93- }
94-
95- let num_insts = mir[ target] . statements . len ( ) ;
96- match mir[ target] . terminator ( ) . kind {
97- TerminatorKind :: Goto { target : new_target } if num_insts == 0 => {
98- inner_change = true ;
99- terminator. kind = TerminatorKind :: Goto { target : new_target } ;
100- pred_count[ target] -= 1 ;
101- pred_count[ new_target] += 1 ;
102- }
103- _ if pred_count[ target] == 1 => {
104- inner_change = true ;
105- let mut stmts = Vec :: new ( ) ;
106- {
107- let target_data = & mut mir[ target] ;
108- mem:: swap ( & mut stmts, & mut target_data. statements ) ;
109- mem:: swap ( & mut terminator, target_data. terminator_mut ( ) ) ;
110- }
111-
112- mir[ bb] . statements . append ( & mut stmts) ;
113- }
114- _ => { }
115- } ;
116- }
117-
118- for target in terminator. successors_mut ( ) {
119- let new_target = match final_target ( mir, * target) {
120- Some ( new_target) => new_target,
121- None if mir[ bb] . statements . is_empty ( ) => bb,
122- None => continue
123- } ;
124- if * target != new_target {
125- inner_change = true ;
126- pred_count[ * target] -= 1 ;
127- pred_count[ new_target] += 1 ;
128- * target = new_target;
129- }
130- }
131-
132- changed |= inner_change;
133- if !inner_change {
134- break ;
135- }
136- }
68+ pub struct CfgSimplifier < ' a , ' tcx : ' a > {
69+ basic_blocks : & ' a mut IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
70+ pred_count : IndexVec < BasicBlock , u32 >
71+ }
13772
138- mir[ bb] . terminator = Some ( terminator) ;
73+ impl < ' a , ' tcx : ' a > CfgSimplifier < ' a , ' tcx > {
74+ fn new ( mir : & ' a mut Mir < ' tcx > ) -> Self {
75+ let mut pred_count = IndexVec :: from_elem ( 0u32 , mir. basic_blocks ( ) ) ;
13976
140- for succ in mir[ bb] . terminator ( ) . successors ( ) . iter ( ) {
141- if seen. insert ( succ. index ( ) ) {
142- worklist. push ( * succ) ;
77+ // we can't use mir.predecessors() here because that counts
78+ // dead blocks, which we don't want to.
79+ for ( _, data) in traversal:: preorder ( mir) {
80+ if let Some ( ref term) = data. terminator {
81+ for & tgt in term. successors ( ) . iter ( ) {
82+ pred_count[ tgt] += 1 ;
14383 }
14484 }
14585 }
14686
147- if !changed {
148- break ;
87+ let basic_blocks = mir. basic_blocks_mut ( ) ;
88+
89+ CfgSimplifier {
90+ basic_blocks : basic_blocks,
91+ pred_count : pred_count
14992 }
15093 }
151- }
15294
153- // Find the target at the end of the jump chain, return None if there is a loop
154- fn final_target ( mir : & Mir , mut target : BasicBlock ) -> Option < BasicBlock > {
155- // Keep track of already seen blocks to detect loops
156- let mut seen: Vec < BasicBlock > = Vec :: with_capacity ( 8 ) ;
157-
158- while mir[ target] . statements . is_empty ( ) {
159- // NB -- terminator may have been swapped with `None` in
160- // merge_consecutive_blocks, in which case we have a cycle and just want
161- // to stop
162- match mir[ target] . terminator {
163- Some ( Terminator { kind : TerminatorKind :: Goto { target : next } , .. } ) => {
164- if seen. contains ( & next) {
165- return None ;
95+ fn simplify ( mut self ) {
96+ loop {
97+ let mut changed = false ;
98+
99+ for bb in ( 0 ..self . basic_blocks . len ( ) ) . map ( BasicBlock :: new) {
100+ if self . pred_count [ bb] == 0 {
101+ continue
102+ }
103+
104+ debug ! ( "simplifying {:?}" , bb) ;
105+
106+ let mut terminator = self . basic_blocks [ bb] . terminator . take ( )
107+ . expect ( "invalid terminator state" ) ;
108+
109+ for successor in terminator. successors_mut ( ) {
110+ self . collapse_goto_chain ( successor, & mut changed) ;
166111 }
167- seen. push ( next) ;
168- target = next;
112+
113+ let mut new_stmts = vec ! [ ] ;
114+ let mut inner_changed = true ;
115+ while inner_changed {
116+ inner_changed = false ;
117+ inner_changed |= self . simplify_branch ( & mut terminator) ;
118+ inner_changed |= self . merge_successor ( & mut new_stmts, & mut terminator) ;
119+ changed |= inner_changed;
120+ }
121+
122+ self . basic_blocks [ bb] . statements . extend ( new_stmts) ;
123+ self . basic_blocks [ bb] . terminator = Some ( terminator) ;
124+
125+ changed |= inner_changed;
169126 }
170- _ => break
127+
128+ if !changed { break }
171129 }
172130 }
173131
174- Some ( target)
175- }
132+ // Collapse a goto chain starting from `start`
133+ fn collapse_goto_chain ( & mut self , start : & mut BasicBlock , changed : & mut bool ) {
134+ let mut terminator = match self . basic_blocks [ * start] {
135+ BasicBlockData {
136+ ref statements,
137+ terminator : ref mut terminator @ Some ( Terminator {
138+ kind : TerminatorKind :: Goto { .. } , ..
139+ } ) , ..
140+ } if statements. is_empty ( ) => terminator. take ( ) ,
141+ // if `terminator` is None, this means we are in a loop. In that
142+ // case, let all the loop collapse to its entry.
143+ _ => return
144+ } ;
145+
146+ let target = match terminator {
147+ Some ( Terminator { kind : TerminatorKind :: Goto { ref mut target } , .. } ) => {
148+ self . collapse_goto_chain ( target, changed) ;
149+ * target
150+ }
151+ _ => unreachable ! ( )
152+ } ;
153+ self . basic_blocks [ * start] . terminator = terminator;
176154
177- fn simplify_branches ( mir : & mut Mir ) {
178- loop {
179- let mut changed = false ;
155+ debug ! ( "collapsing goto chain from {:?} to {:?}" , * start, target) ;
180156
181- for ( _, basic_block) in mir. basic_blocks_mut ( ) . iter_enumerated_mut ( ) {
182- let mut terminator = basic_block. terminator_mut ( ) ;
183- terminator. kind = match terminator. kind {
184- TerminatorKind :: If { ref targets, .. } if targets. 0 == targets. 1 => {
185- changed = true ;
186- TerminatorKind :: Goto { target : targets. 0 }
187- }
157+ * changed |= * start != target;
158+ self . pred_count [ target] += 1 ;
159+ self . pred_count [ * start] -= 1 ;
160+ * start = target;
161+ }
188162
189- TerminatorKind :: If { ref targets, cond : Operand :: Constant ( Constant {
190- literal : Literal :: Value {
191- value : ConstVal :: Bool ( cond)
192- } , ..
193- } ) } => {
194- changed = true ;
195- if cond {
196- TerminatorKind :: Goto { target : targets. 0 }
197- } else {
198- TerminatorKind :: Goto { target : targets. 1 }
199- }
200- }
163+ // merge a block with 1 `goto` predecessor to its parent
164+ fn merge_successor ( & mut self ,
165+ new_stmts : & mut Vec < Statement < ' tcx > > ,
166+ terminator : & mut Terminator < ' tcx > )
167+ -> bool
168+ {
169+ let target = match terminator. kind {
170+ TerminatorKind :: Goto { target }
171+ if self . pred_count [ target] == 1
172+ => target,
173+ _ => return false
174+ } ;
175+
176+ debug ! ( "merging block {:?} into {:?}" , target, terminator) ;
177+ * terminator = match self . basic_blocks [ target] . terminator . take ( ) {
178+ Some ( terminator) => terminator,
179+ None => {
180+ // unreachable loop - this should not be possible, as we
181+ // don't strand blocks, but handle it correctly.
182+ return false
183+ }
184+ } ;
185+ new_stmts. extend ( self . basic_blocks [ target] . statements . drain ( ..) ) ;
186+ self . pred_count [ target] = 0 ;
201187
202- TerminatorKind :: Assert { target, cond : Operand :: Constant ( Constant {
203- literal : Literal :: Value {
204- value : ConstVal :: Bool ( cond)
205- } , ..
206- } ) , expected, .. } if cond == expected => {
207- changed = true ;
208- TerminatorKind :: Goto { target : target }
209- }
188+ true
189+ }
210190
211- TerminatorKind :: SwitchInt { ref targets, .. } if targets. len ( ) == 1 => {
212- changed = true ;
213- TerminatorKind :: Goto { target : targets[ 0 ] }
191+ // turn a branch with all successors identical to a goto
192+ fn simplify_branch ( & mut self , terminator : & mut Terminator < ' tcx > ) -> bool {
193+ match terminator. kind {
194+ TerminatorKind :: If { .. } |
195+ TerminatorKind :: Switch { .. } |
196+ TerminatorKind :: SwitchInt { .. } => { } ,
197+ _ => return false
198+ } ;
199+
200+ let first_succ = {
201+ let successors = terminator. successors ( ) ;
202+ if let Some ( & first_succ) = terminator. successors ( ) . get ( 0 ) {
203+ if successors. iter ( ) . all ( |s| * s == first_succ) {
204+ self . pred_count [ first_succ] -= ( successors. len ( ) -1 ) as u32 ;
205+ first_succ
206+ } else {
207+ return false
214208 }
215- _ => continue
209+ } else {
210+ return false
216211 }
217- }
212+ } ;
218213
219- if !changed {
220- break ;
221- }
214+ debug ! ( "simplifying branch {:?}" , terminator ) ;
215+ terminator . kind = TerminatorKind :: Goto { target : first_succ } ;
216+ true
222217 }
223218}
224219
0 commit comments