@@ -33,6 +33,7 @@ use rustc_index::vec::{Idx, IndexVec};
33
33
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
34
34
use rustc_middle:: mir:: * ;
35
35
use rustc_middle:: ty:: TyCtxt ;
36
+ use smallvec:: SmallVec ;
36
37
use std:: borrow:: Cow ;
37
38
38
39
pub struct SimplifyCfg {
@@ -172,9 +173,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
172
173
}
173
174
}
174
175
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] {
178
182
BasicBlockData {
179
183
ref statements,
180
184
terminator :
@@ -183,32 +187,45 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
183
187
} if statements. is_empty ( ) => terminator. take ( ) ,
184
188
// if `terminator` is None, this means we are in a loop. In that
185
189
// 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
+ }
201
193
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;
209
207
}
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) ;
210
217
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
+ }
212
229
}
213
230
214
231
// merge a block with 1 `goto` predecessor to its parent
0 commit comments