@@ -6,6 +6,7 @@ use rustc_index::{IndexSlice, IndexVec};
6
6
use rustc_middle:: mir:: { self , BasicBlock , TerminatorKind } ;
7
7
8
8
use std:: cmp:: Ordering ;
9
+ use std:: collections:: VecDeque ;
9
10
use std:: ops:: { Index , IndexMut } ;
10
11
11
12
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
@@ -385,57 +386,72 @@ fn bcb_filtered_successors<'a, 'tcx>(
385
386
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
386
387
#[ derive( Debug ) ]
387
388
pub ( super ) struct TraversalContext {
388
- /// From one or more backedges returning to a loop header.
389
- pub loop_backedges : Option < ( Vec < BasicCoverageBlock > , BasicCoverageBlock ) > ,
390
-
391
- /// worklist, to be traversed, of CoverageGraph in the loop with the given loop
392
- /// backedges, such that the loop is the inner inner-most loop containing these
393
- /// CoverageGraph
394
- pub worklist : Vec < BasicCoverageBlock > ,
389
+ /// BCB with one or more incoming loop backedges, indicating which loop
390
+ /// this context is for.
391
+ ///
392
+ /// If `None`, this is the non-loop context for the function as a whole.
393
+ loop_header : Option < BasicCoverageBlock > ,
394
+
395
+ /// Worklist of BCBs to be processed in this context.
396
+ worklist : VecDeque < BasicCoverageBlock > ,
395
397
}
396
398
397
- pub ( super ) struct TraverseCoverageGraphWithLoops {
398
- pub backedges : IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > ,
399
- pub context_stack : Vec < TraversalContext > ,
399
+ pub ( super ) struct TraverseCoverageGraphWithLoops < ' a > {
400
+ basic_coverage_blocks : & ' a CoverageGraph ,
401
+
402
+ backedges : IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > ,
403
+ context_stack : Vec < TraversalContext > ,
400
404
visited : BitSet < BasicCoverageBlock > ,
401
405
}
402
406
403
- impl TraverseCoverageGraphWithLoops {
404
- pub fn new ( basic_coverage_blocks : & CoverageGraph ) -> Self {
405
- let start_bcb = basic_coverage_blocks. start_node ( ) ;
407
+ impl < ' a > TraverseCoverageGraphWithLoops < ' a > {
408
+ pub ( super ) fn new ( basic_coverage_blocks : & ' a CoverageGraph ) -> Self {
406
409
let backedges = find_loop_backedges ( basic_coverage_blocks) ;
407
- let context_stack =
408
- vec ! [ TraversalContext { loop_backedges: None , worklist: vec![ start_bcb] } ] ;
410
+
411
+ let worklist = VecDeque :: from ( [ basic_coverage_blocks. start_node ( ) ] ) ;
412
+ let context_stack = vec ! [ TraversalContext { loop_header: None , worklist } ] ;
413
+
409
414
// `context_stack` starts with a `TraversalContext` for the main function context (beginning
410
415
// with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
411
416
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
412
417
// exhausted.
413
418
let visited = BitSet :: new_empty ( basic_coverage_blocks. num_nodes ( ) ) ;
414
- Self { backedges, context_stack, visited }
419
+ Self { basic_coverage_blocks , backedges, context_stack, visited }
415
420
}
416
421
417
- pub fn next ( & mut self , basic_coverage_blocks : & CoverageGraph ) -> Option < BasicCoverageBlock > {
422
+ /// For each loop on the loop context stack (top-down), yields a list of BCBs
423
+ /// within that loop that have an outgoing edge back to the loop header.
424
+ pub ( super ) fn reloop_bcbs_per_loop ( & self ) -> impl Iterator < Item = & [ BasicCoverageBlock ] > {
425
+ self . context_stack
426
+ . iter ( )
427
+ . rev ( )
428
+ . filter_map ( |context| context. loop_header )
429
+ . map ( |header_bcb| self . backedges [ header_bcb] . as_slice ( ) )
430
+ }
431
+
432
+ pub ( super ) fn next ( & mut self ) -> Option < BasicCoverageBlock > {
418
433
debug ! (
419
434
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}" ,
420
435
self . context_stack. iter( ) . rev( ) . collect:: <Vec <_>>( )
421
436
) ;
422
437
423
438
while let Some ( context) = self . context_stack . last_mut ( ) {
424
- if let Some ( next_bcb ) = context. worklist . pop ( ) {
425
- if !self . visited . insert ( next_bcb ) {
426
- debug ! ( "Already visited: {:?}" , next_bcb ) ;
439
+ if let Some ( bcb ) = context. worklist . pop_front ( ) {
440
+ if !self . visited . insert ( bcb ) {
441
+ debug ! ( "Already visited: {bcb :?}" ) ;
427
442
continue ;
428
443
}
429
- debug ! ( "Visiting {:?}" , next_bcb) ;
430
- if self . backedges [ next_bcb] . len ( ) > 0 {
431
- debug ! ( "{:?} is a loop header! Start a new TraversalContext..." , next_bcb) ;
444
+ debug ! ( "Visiting {bcb:?}" ) ;
445
+
446
+ if self . backedges [ bcb] . len ( ) > 0 {
447
+ debug ! ( "{bcb:?} is a loop header! Start a new TraversalContext..." ) ;
432
448
self . context_stack . push ( TraversalContext {
433
- loop_backedges : Some ( ( self . backedges [ next_bcb ] . clone ( ) , next_bcb ) ) ,
434
- worklist : Vec :: new ( ) ,
449
+ loop_header : Some ( bcb ) ,
450
+ worklist : VecDeque :: new ( ) ,
435
451
} ) ;
436
452
}
437
- self . extend_worklist ( basic_coverage_blocks , next_bcb ) ;
438
- return Some ( next_bcb ) ;
453
+ self . add_successors_to_worklists ( bcb ) ;
454
+ return Some ( bcb ) ;
439
455
} else {
440
456
// Strip contexts with empty worklists from the top of the stack
441
457
self . context_stack . pop ( ) ;
@@ -445,13 +461,10 @@ impl TraverseCoverageGraphWithLoops {
445
461
None
446
462
}
447
463
448
- pub fn extend_worklist (
449
- & mut self ,
450
- basic_coverage_blocks : & CoverageGraph ,
451
- bcb : BasicCoverageBlock ,
452
- ) {
453
- let successors = & basic_coverage_blocks. successors [ bcb] ;
464
+ pub fn add_successors_to_worklists ( & mut self , bcb : BasicCoverageBlock ) {
465
+ let successors = & self . basic_coverage_blocks . successors [ bcb] ;
454
466
debug ! ( "{:?} has {} successors:" , bcb, successors. len( ) ) ;
467
+
455
468
for & successor in successors {
456
469
if successor == bcb {
457
470
debug ! (
@@ -460,56 +473,44 @@ impl TraverseCoverageGraphWithLoops {
460
473
bcb
461
474
) ;
462
475
// Don't re-add this successor to the worklist. We are already processing it.
476
+ // FIXME: This claims to skip just the self-successor, but it actually skips
477
+ // all other successors as well. Does that matter?
463
478
break ;
464
479
}
465
- for context in self . context_stack . iter_mut ( ) . rev ( ) {
466
- // Add successors of the current BCB to the appropriate context. Successors that
467
- // stay within a loop are added to the BCBs context worklist. Successors that
468
- // exit the loop (they are not dominated by the loop header) must be reachable
469
- // from other BCBs outside the loop, and they will be added to a different
470
- // worklist.
471
- //
472
- // Branching blocks (with more than one successor) must be processed before
473
- // blocks with only one successor, to prevent unnecessarily complicating
474
- // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
475
- // branching block would have given an `Expression` (or vice versa).
476
- let ( some_successor_to_add, some_loop_header) =
477
- if let Some ( ( _, loop_header) ) = context. loop_backedges {
478
- if basic_coverage_blocks. dominates ( loop_header, successor) {
479
- ( Some ( successor) , Some ( loop_header) )
480
- } else {
481
- ( None , None )
482
- }
483
- } else {
484
- ( Some ( successor) , None )
485
- } ;
486
- if let Some ( successor_to_add) = some_successor_to_add {
487
- if basic_coverage_blocks. successors [ successor_to_add] . len ( ) > 1 {
488
- debug ! (
489
- "{:?} successor is branching. Prioritize it at the beginning of \
490
- the {}",
491
- successor_to_add,
492
- if let Some ( loop_header) = some_loop_header {
493
- format!( "worklist for the loop headed by {loop_header:?}" )
494
- } else {
495
- String :: from( "non-loop worklist" )
496
- } ,
497
- ) ;
498
- context. worklist . insert ( 0 , successor_to_add) ;
499
- } else {
500
- debug ! (
501
- "{:?} successor is non-branching. Defer it to the end of the {}" ,
502
- successor_to_add,
503
- if let Some ( loop_header) = some_loop_header {
504
- format!( "worklist for the loop headed by {loop_header:?}" )
505
- } else {
506
- String :: from( "non-loop worklist" )
507
- } ,
508
- ) ;
509
- context. worklist . push ( successor_to_add) ;
480
+
481
+ // Add successors of the current BCB to the appropriate context. Successors that
482
+ // stay within a loop are added to the BCBs context worklist. Successors that
483
+ // exit the loop (they are not dominated by the loop header) must be reachable
484
+ // from other BCBs outside the loop, and they will be added to a different
485
+ // worklist.
486
+ //
487
+ // Branching blocks (with more than one successor) must be processed before
488
+ // blocks with only one successor, to prevent unnecessarily complicating
489
+ // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
490
+ // branching block would have given an `Expression` (or vice versa).
491
+
492
+ let context = self
493
+ . context_stack
494
+ . iter_mut ( )
495
+ . rev ( )
496
+ . find ( |context| match context. loop_header {
497
+ Some ( loop_header) => {
498
+ self . basic_coverage_blocks . dominates ( loop_header, successor)
510
499
}
511
- break ;
512
- }
500
+ None => true ,
501
+ } )
502
+ . unwrap_or_else ( || bug ! ( "should always fall back to the root non-loop context" ) ) ;
503
+ debug ! ( "adding to worklist for {:?}" , context. loop_header) ;
504
+
505
+ // FIXME: The code below had debug messages claiming to add items to a
506
+ // particular end of the worklist, but was confused about which end was
507
+ // which. The existing behaviour has been preserved for now, but it's
508
+ // unclear what the intended behaviour was.
509
+
510
+ if self . basic_coverage_blocks . successors [ successor] . len ( ) > 1 {
511
+ context. worklist . push_back ( successor) ;
512
+ } else {
513
+ context. worklist . push_front ( successor) ;
513
514
}
514
515
}
515
516
}
0 commit comments