@@ -33,6 +33,7 @@ use rustc_index::vec::{Idx, IndexVec};
3333use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
3434use rustc_middle:: mir:: * ;
3535use rustc_middle:: ty:: TyCtxt ;
36+ use smallvec:: SmallVec ;
3637use std:: borrow:: Cow ;
3738
3839pub struct SimplifyCfg {
@@ -172,9 +173,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
172173 }
173174 }
174175
175- // Collapse a goto chain starting from `start`
176- fn collapse_goto_chain ( & mut self , start : & mut BasicBlock , changed : & mut bool ) {
177- let mut terminator = match self . basic_blocks [ * start] {
176+ /// This function will return `None` if
177+ /// * the block has statements
178+ /// * the block has a terminator other than `goto`
179+ /// * the block has no terminator (meaning some other part of the current optimization stole it)
180+ fn take_terminator_if_simple_goto ( & mut self , bb : BasicBlock ) -> Option < Terminator < ' tcx > > {
181+ match self . basic_blocks [ bb] {
178182 BasicBlockData {
179183 ref statements,
180184 terminator :
@@ -183,32 +187,45 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
183187 } if statements. is_empty ( ) => terminator. take ( ) ,
184188 // if `terminator` is None, this means we are in a loop. In that
185189 // case, let all the loop collapse to its entry.
186- _ => return ,
187- } ;
188-
189- let target = match terminator {
190- Some ( Terminator { kind : TerminatorKind :: Goto { ref mut target } , .. } ) => {
191- self . collapse_goto_chain ( target, changed) ;
192- * target
193- }
194- _ => unreachable ! ( ) ,
195- } ;
196- self . basic_blocks [ * start] . terminator = terminator;
197-
198- debug ! ( "collapsing goto chain from {:?} to {:?}" , * start, target) ;
199-
200- * changed |= * start != target;
190+ _ => None ,
191+ }
192+ }
201193
202- if self . pred_count [ * start] == 1 {
203- // This is the last reference to *start, so the pred-count to
204- // to target is moved into the current block.
205- self . pred_count [ * start] = 0 ;
206- } else {
207- self . pred_count [ target] += 1 ;
208- self . pred_count [ * start] -= 1 ;
194+ /// Collapse a goto chain starting from `start`
195+ fn collapse_goto_chain ( & mut self , start : & mut BasicBlock , changed : & mut bool ) {
196+ // Using `SmallVec` here, because in some logs on libcore oli-obk saw many single-element
197+ // goto chains. We should probably benchmark different sizes.
198+ let mut terminators: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
199+ let mut current = * start;
200+ while let Some ( terminator) = self . take_terminator_if_simple_goto ( current) {
201+ let target = match terminator {
202+ Terminator { kind : TerminatorKind :: Goto { target } , .. } => target,
203+ _ => unreachable ! ( ) ,
204+ } ;
205+ terminators. push ( ( current, terminator) ) ;
206+ current = target;
209207 }
208+ let last = current;
209+ * start = last;
210+ while let Some ( ( current, mut terminator) ) = terminators. pop ( ) {
211+ let target = match terminator {
212+ Terminator { kind : TerminatorKind :: Goto { ref mut target } , .. } => target,
213+ _ => unreachable ! ( ) ,
214+ } ;
215+ * target = last;
216+ debug ! ( "collapsing goto chain from {:?} to {:?}" , current, target) ;
210217
211- * start = target;
218+ if self . pred_count [ current] == 1 {
219+ // This is the last reference to current, so the pred-count to
220+ // to target is moved into the current block.
221+ self . pred_count [ current] = 0 ;
222+ } else {
223+ self . pred_count [ * target] += 1 ;
224+ self . pred_count [ current] -= 1 ;
225+ }
226+ * changed = true ;
227+ self . basic_blocks [ current] . terminator = Some ( terminator) ;
228+ }
212229 }
213230
214231 // merge a block with 1 `goto` predecessor to its parent
0 commit comments