@@ -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