@@ -6,6 +6,7 @@ use rustc_index::{IndexSlice, IndexVec};
66use rustc_middle:: mir:: { self , BasicBlock , TerminatorKind } ;
77
88use std:: cmp:: Ordering ;
9+ use std:: collections:: VecDeque ;
910use std:: ops:: { Index , IndexMut } ;
1011
1112/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
@@ -385,57 +386,72 @@ fn bcb_filtered_successors<'a, 'tcx>(
385386/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
386387#[ derive( Debug ) ]
387388pub ( 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 > ,
395397}
396398
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 > ,
400404 visited : BitSet < BasicCoverageBlock > ,
401405}
402406
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 {
406409 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+
409414 // `context_stack` starts with a `TraversalContext` for the main function context (beginning
410415 // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
411416 // of the stack as loops are entered, and popped off of the stack when a loop's worklist is
412417 // exhausted.
413418 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 }
415420 }
416421
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 > {
418433 debug ! (
419434 "TraverseCoverageGraphWithLoops::next - context_stack: {:?}" ,
420435 self . context_stack. iter( ) . rev( ) . collect:: <Vec <_>>( )
421436 ) ;
422437
423438 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 :?}" ) ;
427442 continue ;
428443 }
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..." ) ;
432448 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 ( ) ,
435451 } ) ;
436452 }
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 ) ;
439455 } else {
440456 // Strip contexts with empty worklists from the top of the stack
441457 self . context_stack . pop ( ) ;
@@ -445,13 +461,10 @@ impl TraverseCoverageGraphWithLoops {
445461 None
446462 }
447463
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] ;
454466 debug ! ( "{:?} has {} successors:" , bcb, successors. len( ) ) ;
467+
455468 for & successor in successors {
456469 if successor == bcb {
457470 debug ! (
@@ -460,56 +473,44 @@ impl TraverseCoverageGraphWithLoops {
460473 bcb
461474 ) ;
462475 // 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?
463478 break ;
464479 }
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)
510499 }
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) ;
513514 }
514515 }
515516 }
0 commit comments