@@ -321,25 +321,23 @@ impl<'a> MakeBcbCounters<'a> {
321
321
if !self . basic_coverage_blocks [ from_bcb] . is_out_summable {
322
322
return ;
323
323
}
324
- // If this node doesn't have multiple out-edges, or all of its out-edges
325
- // already have counters, then we don't need to create edge counters.
326
- let needs_out_edge_counters = successors. len ( ) > 1
327
- && successors. iter ( ) . any ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) ) ;
328
- if !needs_out_edge_counters {
329
- return ;
330
- }
331
324
332
- if tracing:: enabled!( tracing:: Level :: DEBUG ) {
333
- let _span =
334
- debug_span ! ( "node has some out-edges without counters" , ?from_bcb) . entered ( ) ;
335
- for & to_bcb in successors {
336
- debug ! ( ?to_bcb, counter=?self . edge_counter( from_bcb, to_bcb) ) ;
337
- }
338
- }
325
+ // Determine the set of out-edges that don't yet have edge counters.
326
+ let candidate_successors = self
327
+ . bcb_successors ( from_bcb)
328
+ . iter ( )
329
+ . copied ( )
330
+ . filter ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) )
331
+ . collect :: < Vec < _ > > ( ) ;
332
+ debug ! ( ?candidate_successors) ;
339
333
340
- // Of the out-edges that don't have counters yet, one can be given an expression
341
- // (computed from the other out-edges) instead of a dedicated counter.
342
- let expression_to_bcb = self . choose_out_edge_for_expression ( traversal, from_bcb) ;
334
+ // If there are out-edges without counters, choose one to be given an expression
335
+ // (computed from this node and the other out-edges) instead of a physical counter.
336
+ let Some ( expression_to_bcb) =
337
+ self . choose_out_edge_for_expression ( traversal, & candidate_successors)
338
+ else {
339
+ return ;
340
+ } ;
343
341
344
342
// For each out-edge other than the one that was chosen to get an expression,
345
343
// ensure that it has a counter (existing counter/expression or a new counter),
@@ -351,10 +349,11 @@ impl<'a> MakeBcbCounters<'a> {
351
349
. filter ( |& to_bcb| to_bcb != expression_to_bcb)
352
350
. map ( |to_bcb| self . get_or_make_edge_counter ( from_bcb, to_bcb) )
353
351
. collect :: < Vec < _ > > ( ) ;
354
- let sum_of_all_other_out_edges: BcbCounter = self
355
- . coverage_counters
356
- . make_sum ( & other_out_edge_counters)
357
- . expect ( "there must be at least one other out-edge" ) ;
352
+ let Some ( sum_of_all_other_out_edges) =
353
+ self . coverage_counters . make_sum ( & other_out_edge_counters)
354
+ else {
355
+ return ;
356
+ } ;
358
357
359
358
// Now create an expression for the chosen edge, by taking the counter
360
359
// for its source node and subtracting the sum of its sibling out-edges.
@@ -440,79 +439,59 @@ impl<'a> MakeBcbCounters<'a> {
440
439
self . coverage_counters . make_phys_edge_counter ( from_bcb, to_bcb)
441
440
}
442
441
443
- /// Choose one of the out-edges of `from_bcb` to receive an expression
444
- /// instead of a physical counter, and returns that edge's target node.
445
- ///
446
- /// - Precondition: The node must have at least one out-edge without a counter.
447
- /// - Postcondition: The selected edge does not have an edge counter.
442
+ /// Given a set of candidate out-edges (represented by their successor node),
443
+ /// choose one to be given a counter expression instead of a physical counter.
448
444
fn choose_out_edge_for_expression (
449
445
& self ,
450
446
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
451
- from_bcb : BasicCoverageBlock ,
452
- ) -> BasicCoverageBlock {
453
- if let Some ( reloop_target) = self . find_good_reloop_edge ( traversal, from_bcb) {
454
- assert ! ( self . edge_has_no_counter( from_bcb, reloop_target) ) ;
447
+ candidate_successors : & [ BasicCoverageBlock ] ,
448
+ ) -> Option < BasicCoverageBlock > {
449
+ // Try to find a candidate that leads back to the top of a loop,
450
+ // because reloop edges tend to be executed more times than loop-exit edges.
451
+ if let Some ( reloop_target) = self . find_good_reloop_edge ( traversal, & candidate_successors) {
455
452
debug ! ( "Selecting reloop target {reloop_target:?} to get an expression" ) ;
456
- return reloop_target;
453
+ return Some ( reloop_target) ;
457
454
}
458
455
459
- // We couldn't identify a "good" edge, so just choose any edge that
460
- // doesn't already have a counter.
461
- let arbitrary_target = self
462
- . bcb_successors ( from_bcb)
463
- . iter ( )
464
- . copied ( )
465
- . find ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) )
466
- . expect ( "precondition: at least one out-edge without a counter" ) ;
456
+ // We couldn't identify a "good" edge, so just choose an arbitrary one.
457
+ let arbitrary_target = candidate_successors. first ( ) . copied ( ) ?;
467
458
debug ! ( ?arbitrary_target, "selecting arbitrary out-edge to get an expression" ) ;
468
- arbitrary_target
459
+ Some ( arbitrary_target)
469
460
}
470
461
471
- /// Tries to find an edge that leads back to the top of a loop, and that
472
- /// doesn't already have a counter. Such edges are good candidates to
473
- /// be given an expression (instead of a physical counter), because they
474
- /// will tend to be executed more times than a loop-exit edge.
462
+ /// Given a set of candidate out-edges (represented by their successor node),
463
+ /// tries to find one that leads back to the top of a loop.
464
+ ///
465
+ /// Reloop edges are good candidates for counter expressions, because they
466
+ /// will tend to be executed more times than a loop-exit edge, so it's nice
467
+ /// for them to be able to avoid a physical counter increment.
475
468
fn find_good_reloop_edge (
476
469
& self ,
477
470
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
478
- from_bcb : BasicCoverageBlock ,
471
+ candidate_successors : & [ BasicCoverageBlock ] ,
479
472
) -> Option < BasicCoverageBlock > {
480
- let successors = self . bcb_successors ( from_bcb) ;
473
+ // If there are no candidates, avoid iterating over the loop stack.
474
+ if candidate_successors. is_empty ( ) {
475
+ return None ;
476
+ }
481
477
482
478
// Consider each loop on the current traversal context stack, top-down.
483
479
for reloop_bcbs in traversal. reloop_bcbs_per_loop ( ) {
484
- let mut all_edges_exit_this_loop = true ;
485
-
486
- // Try to find an out-edge that doesn't exit this loop and doesn't
487
- // already have a counter.
488
- for & target_bcb in successors {
480
+ // Try to find a candidate edge that doesn't exit this loop.
481
+ for & target_bcb in candidate_successors {
489
482
// An edge is a reloop edge if its target dominates any BCB that has
490
483
// an edge back to the loop header. (Otherwise it's an exit edge.)
491
484
let is_reloop_edge = reloop_bcbs. iter ( ) . any ( |& reloop_bcb| {
492
485
self . basic_coverage_blocks . dominates ( target_bcb, reloop_bcb)
493
486
} ) ;
494
-
495
487
if is_reloop_edge {
496
- all_edges_exit_this_loop = false ;
497
- if self . edge_has_no_counter ( from_bcb, target_bcb) {
498
- // We found a good out-edge to be given an expression.
499
- return Some ( target_bcb) ;
500
- }
501
- // Keep looking for another reloop edge without a counter.
502
- } else {
503
- // This edge exits the loop.
488
+ // We found a good out-edge to be given an expression.
489
+ return Some ( target_bcb) ;
504
490
}
505
491
}
506
492
507
- if !all_edges_exit_this_loop {
508
- // We found one or more reloop edges, but all of them already
509
- // have counters. Let the caller choose one of the other edges.
510
- debug ! ( "All reloop edges had counters; skipping the other loops" ) ;
511
- return None ;
512
- }
513
-
514
- // All of the out-edges exit this loop, so keep looking for a good
515
- // reloop edge for one of the outer loops.
493
+ // All of the candidate edges exit this loop, so keep looking
494
+ // for a good reloop edge for one of the outer loops.
516
495
}
517
496
518
497
None
0 commit comments