@@ -95,11 +95,33 @@ impl CoverageCounters {
9595 this
9696 }
9797
98- fn make_counter ( & mut self , site : CounterIncrementSite ) -> BcbCounter {
98+ /// Shared helper used by [`Self::make_phys_node_counter`] and
99+ /// [`Self::make_phys_edge_counter`]. Don't call this directly.
100+ fn make_counter_inner ( & mut self , site : CounterIncrementSite ) -> BcbCounter {
99101 let id = self . counter_increment_sites . push ( site) ;
100102 BcbCounter :: Counter { id }
101103 }
102104
105+ /// Creates a new physical counter attached a BCB node.
106+ /// The node must not already have a counter.
107+ fn make_phys_node_counter ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
108+ let counter = self . make_counter_inner ( CounterIncrementSite :: Node { bcb } ) ;
109+ debug ! ( ?bcb, ?counter, "node gets a physical counter" ) ;
110+ self . set_bcb_counter ( bcb, counter)
111+ }
112+
113+ /// Creates a new physical counter attached to a BCB edge.
114+ /// The edge must not already have a counter.
115+ fn make_phys_edge_counter (
116+ & mut self ,
117+ from_bcb : BasicCoverageBlock ,
118+ to_bcb : BasicCoverageBlock ,
119+ ) -> BcbCounter {
120+ let counter = self . make_counter_inner ( CounterIncrementSite :: Edge { from_bcb, to_bcb } ) ;
121+ debug ! ( ?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter" ) ;
122+ self . set_bcb_edge_counter ( from_bcb, to_bcb, counter)
123+ }
124+
103125 fn make_expression ( & mut self , lhs : BcbCounter , op : Op , rhs : BcbCounter ) -> BcbCounter {
104126 let new_expr = BcbExpression { lhs, op, rhs } ;
105127 * self
@@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> {
294316
295317 let successors = self . basic_coverage_blocks . successors [ from_bcb] . as_slice ( ) ;
296318
297- // If this node doesn't have multiple out-edges, or all of its out-edges
298- // already have counters, then we don't need to create edge counters.
299- let needs_out_edge_counters = successors. len ( ) > 1
300- && successors. iter ( ) . any ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) ) ;
301- if !needs_out_edge_counters {
319+ // If this node's out-edges won't sum to the node's counter,
320+ // then there's no reason to create edge counters here.
321+ if !self . basic_coverage_blocks [ from_bcb] . is_out_summable {
302322 return ;
303323 }
304324
305- if tracing :: enabled! ( tracing :: Level :: DEBUG ) {
306- let _span =
307- debug_span ! ( "node has some out-edges without counters" , ?from_bcb ) . entered ( ) ;
308- for & to_bcb in successors {
309- debug ! ( ? to_bcb, counter=? self . edge_counter ( from_bcb, to_bcb) ) ;
310- }
311- }
325+ // Determine the set of out-edges that don't yet have edge counters.
326+ let candidate_successors = self . basic_coverage_blocks . successors [ from_bcb ]
327+ . iter ( )
328+ . copied ( )
329+ . filter ( | & to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) )
330+ . collect :: < Vec < _ > > ( ) ;
331+ debug ! ( ?candidate_successors ) ;
312332
313- // Of the out-edges that don't have counters yet, one can be given an expression
314- // (computed from the other out-edges) instead of a dedicated counter.
315- let expression_to_bcb = self . choose_out_edge_for_expression ( traversal, from_bcb) ;
333+ // If there are out-edges without counters, choose one to be given an expression
334+ // (computed from this node and the other out-edges) instead of a physical counter.
335+ let Some ( expression_to_bcb) =
336+ self . choose_out_edge_for_expression ( traversal, & candidate_successors)
337+ else {
338+ return ;
339+ } ;
316340
317341 // For each out-edge other than the one that was chosen to get an expression,
318342 // ensure that it has a counter (existing counter/expression or a new counter),
@@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> {
324348 . filter ( |& to_bcb| to_bcb != expression_to_bcb)
325349 . map ( |to_bcb| self . get_or_make_edge_counter ( from_bcb, to_bcb) )
326350 . collect :: < Vec < _ > > ( ) ;
327- let sum_of_all_other_out_edges: BcbCounter = self
328- . coverage_counters
329- . make_sum ( & other_out_edge_counters)
330- . expect ( "there must be at least one other out-edge" ) ;
351+ let Some ( sum_of_all_other_out_edges) =
352+ self . coverage_counters . make_sum ( & other_out_edge_counters)
353+ else {
354+ return ;
355+ } ;
331356
332357 // Now create an expression for the chosen edge, by taking the counter
333358 // for its source node and subtracting the sum of its sibling out-edges.
@@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> {
338363 ) ;
339364
340365 debug ! ( "{expression_to_bcb:?} gets an expression: {expression:?}" ) ;
341- if self . basic_coverage_blocks . bcb_has_multiple_in_edges ( expression_to_bcb) {
342- self . coverage_counters . set_bcb_edge_counter ( from_bcb, expression_to_bcb, expression) ;
343- } else {
366+ if let Some ( sole_pred) = self . basic_coverage_blocks . sole_predecessor ( expression_to_bcb) {
367+ // This edge normally wouldn't get its own counter, so attach the expression
368+ // to its target node instead, so that `edge_has_no_counter` can see it.
369+ assert_eq ! ( sole_pred, from_bcb) ;
344370 self . coverage_counters . set_bcb_counter ( expression_to_bcb, expression) ;
371+ } else {
372+ self . coverage_counters . set_bcb_edge_counter ( from_bcb, expression_to_bcb, expression) ;
345373 }
346374 }
347375
@@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> {
353381 return counter_kind;
354382 }
355383
356- // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
357- // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the
358- // program results in a tight infinite loop, but it should still compile.
359- let one_path_to_target = !self . basic_coverage_blocks . bcb_has_multiple_in_edges ( bcb) ;
360- if one_path_to_target || self . bcb_predecessors ( bcb) . contains ( & bcb) {
361- let counter_kind =
362- self . coverage_counters . make_counter ( CounterIncrementSite :: Node { bcb } ) ;
363- if one_path_to_target {
364- debug ! ( "{bcb:?} gets a new counter: {counter_kind:?}" ) ;
365- } else {
366- debug ! (
367- "{bcb:?} has itself as its own predecessor. It can't be part of its own \
368- Expression sum, so it will get its own new counter: {counter_kind:?}. \
369- (Note, the compiled code will generate an infinite loop.)",
370- ) ;
371- }
372- return self . coverage_counters . set_bcb_counter ( bcb, counter_kind) ;
384+ let predecessors = self . basic_coverage_blocks . predecessors [ bcb] . as_slice ( ) ;
385+
386+ // Handle cases where we can't compute a node's count from its in-edges:
387+ // - START_BCB has no in-edges, so taking the sum would panic (or be wrong).
388+ // - For nodes with one in-edge, or that directly loop to themselves,
389+ // trying to get the in-edge counts would require this node's counter,
390+ // leading to infinite recursion.
391+ if predecessors. len ( ) <= 1 || predecessors. contains ( & bcb) {
392+ debug ! ( ?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor" ) ;
393+ return self . coverage_counters . make_phys_node_counter ( bcb) ;
373394 }
374395
375396 // A BCB with multiple incoming edges can compute its count by ensuring that counters
376397 // exist for each of those edges, and then adding them up to get a total count.
377- let in_edge_counters = self . basic_coverage_blocks . predecessors [ bcb ]
398+ let in_edge_counters = predecessors
378399 . iter ( )
379400 . copied ( )
380401 . map ( |from_bcb| self . get_or_make_edge_counter ( from_bcb, bcb) )
@@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> {
394415 from_bcb : BasicCoverageBlock ,
395416 to_bcb : BasicCoverageBlock ,
396417 ) -> BcbCounter {
397- // If the target BCB has only one in-edge (i.e. this one), then create
398- // a node counter instead, since it will have the same value.
399- if !self . basic_coverage_blocks . bcb_has_multiple_in_edges ( to_bcb) {
400- assert_eq ! ( [ from_bcb] . as_slice( ) , self . basic_coverage_blocks. predecessors[ to_bcb] ) ;
418+ // If the target node has exactly one in-edge (i.e. this one), then just
419+ // use the node's counter, since it will have the same value.
420+ if let Some ( sole_pred) = self . basic_coverage_blocks . sole_predecessor ( to_bcb) {
421+ assert_eq ! ( sole_pred, from_bcb) ;
422+ // This call must take care not to invoke `get_or_make_edge` for
423+ // this edge, since that would result in infinite recursion!
401424 return self . get_or_make_node_counter ( to_bcb) ;
402425 }
403426
404- // If the source BCB has only one successor (assumed to be the given target), an edge
405- // counter is unnecessary. Just get or make a counter for the source BCB.
406- if self . bcb_successors ( from_bcb) . len ( ) == 1 {
427+ // If the source node has exactly one out-edge (i.e. this one) and would have
428+ // the same execution count as that edge, then just use the node's counter.
429+ if let Some ( simple_succ) = self . basic_coverage_blocks . simple_successor ( from_bcb) {
430+ assert_eq ! ( simple_succ, to_bcb) ;
407431 return self . get_or_make_node_counter ( from_bcb) ;
408432 }
409433
@@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> {
416440 }
417441
418442 // Make a new counter to count this edge.
419- let counter_kind =
420- self . coverage_counters . make_counter ( CounterIncrementSite :: Edge { from_bcb, to_bcb } ) ;
421- debug ! ( "Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}" ) ;
422- self . coverage_counters . set_bcb_edge_counter ( from_bcb, to_bcb, counter_kind)
443+ self . coverage_counters . make_phys_edge_counter ( from_bcb, to_bcb)
423444 }
424445
425- /// Choose one of the out-edges of `from_bcb` to receive an expression
426- /// instead of a physical counter, and returns that edge's target node.
427- ///
428- /// - Precondition: The node must have at least one out-edge without a counter.
429- /// - Postcondition: The selected edge does not have an edge counter.
446+ /// Given a set of candidate out-edges (represented by their successor node),
447+ /// choose one to be given a counter expression instead of a physical counter.
430448 fn choose_out_edge_for_expression (
431449 & self ,
432450 traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
433- from_bcb : BasicCoverageBlock ,
434- ) -> BasicCoverageBlock {
435- if let Some ( reloop_target) = self . find_good_reloop_edge ( traversal, from_bcb) {
436- assert ! ( self . edge_has_no_counter( from_bcb, reloop_target) ) ;
451+ candidate_successors : & [ BasicCoverageBlock ] ,
452+ ) -> Option < BasicCoverageBlock > {
453+ // Try to find a candidate that leads back to the top of a loop,
454+ // because reloop edges tend to be executed more times than loop-exit edges.
455+ if let Some ( reloop_target) = self . find_good_reloop_edge ( traversal, & candidate_successors) {
437456 debug ! ( "Selecting reloop target {reloop_target:?} to get an expression" ) ;
438- return reloop_target;
457+ return Some ( reloop_target) ;
439458 }
440459
441- // We couldn't identify a "good" edge, so just choose any edge that
442- // doesn't already have a counter.
443- let arbitrary_target = self
444- . bcb_successors ( from_bcb)
445- . iter ( )
446- . copied ( )
447- . find ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) )
448- . expect ( "precondition: at least one out-edge without a counter" ) ;
460+ // We couldn't identify a "good" edge, so just choose an arbitrary one.
461+ let arbitrary_target = candidate_successors. first ( ) . copied ( ) ?;
449462 debug ! ( ?arbitrary_target, "selecting arbitrary out-edge to get an expression" ) ;
450- arbitrary_target
463+ Some ( arbitrary_target)
451464 }
452465
453- /// Tries to find an edge that leads back to the top of a loop, and that
454- /// doesn't already have a counter. Such edges are good candidates to
455- /// be given an expression (instead of a physical counter), because they
456- /// will tend to be executed more times than a loop-exit edge.
466+ /// Given a set of candidate out-edges (represented by their successor node),
467+ /// tries to find one that leads back to the top of a loop.
468+ ///
469+ /// Reloop edges are good candidates for counter expressions, because they
470+ /// will tend to be executed more times than a loop-exit edge, so it's nice
471+ /// for them to be able to avoid a physical counter increment.
457472 fn find_good_reloop_edge (
458473 & self ,
459474 traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
460- from_bcb : BasicCoverageBlock ,
475+ candidate_successors : & [ BasicCoverageBlock ] ,
461476 ) -> Option < BasicCoverageBlock > {
462- let successors = self . bcb_successors ( from_bcb) ;
477+ // If there are no candidates, avoid iterating over the loop stack.
478+ if candidate_successors. is_empty ( ) {
479+ return None ;
480+ }
463481
464482 // Consider each loop on the current traversal context stack, top-down.
465483 for reloop_bcbs in traversal. reloop_bcbs_per_loop ( ) {
466- let mut all_edges_exit_this_loop = true ;
467-
468- // Try to find an out-edge that doesn't exit this loop and doesn't
469- // already have a counter.
470- for & target_bcb in successors {
484+ // Try to find a candidate edge that doesn't exit this loop.
485+ for & target_bcb in candidate_successors {
471486 // An edge is a reloop edge if its target dominates any BCB that has
472487 // an edge back to the loop header. (Otherwise it's an exit edge.)
473488 let is_reloop_edge = reloop_bcbs. iter ( ) . any ( |& reloop_bcb| {
474489 self . basic_coverage_blocks . dominates ( target_bcb, reloop_bcb)
475490 } ) ;
476-
477491 if is_reloop_edge {
478- all_edges_exit_this_loop = false ;
479- if self . edge_has_no_counter ( from_bcb, target_bcb) {
480- // We found a good out-edge to be given an expression.
481- return Some ( target_bcb) ;
482- }
483- // Keep looking for another reloop edge without a counter.
484- } else {
485- // This edge exits the loop.
492+ // We found a good out-edge to be given an expression.
493+ return Some ( target_bcb) ;
486494 }
487495 }
488496
489- if !all_edges_exit_this_loop {
490- // We found one or more reloop edges, but all of them already
491- // have counters. Let the caller choose one of the other edges.
492- debug ! ( "All reloop edges had counters; skipping the other loops" ) ;
493- return None ;
494- }
495-
496- // All of the out-edges exit this loop, so keep looking for a good
497- // reloop edge for one of the outer loops.
497+ // All of the candidate edges exit this loop, so keep looking
498+ // for a good reloop edge for one of the outer loops.
498499 }
499500
500501 None
501502 }
502503
503- #[ inline]
504- fn bcb_predecessors ( & self , bcb : BasicCoverageBlock ) -> & [ BasicCoverageBlock ] {
505- & self . basic_coverage_blocks . predecessors [ bcb]
506- }
507-
508- #[ inline]
509- fn bcb_successors ( & self , bcb : BasicCoverageBlock ) -> & [ BasicCoverageBlock ] {
510- & self . basic_coverage_blocks . successors [ bcb]
511- }
512-
513504 #[ inline]
514505 fn edge_has_no_counter (
515506 & self ,
516507 from_bcb : BasicCoverageBlock ,
517508 to_bcb : BasicCoverageBlock ,
518509 ) -> bool {
519- self . edge_counter ( from_bcb, to_bcb) . is_none ( )
520- }
510+ let edge_counter =
511+ if let Some ( sole_pred) = self . basic_coverage_blocks . sole_predecessor ( to_bcb) {
512+ assert_eq ! ( sole_pred, from_bcb) ;
513+ self . coverage_counters . bcb_counters [ to_bcb]
514+ } else {
515+ self . coverage_counters . bcb_edge_counters . get ( & ( from_bcb, to_bcb) ) . copied ( )
516+ } ;
521517
522- fn edge_counter (
523- & self ,
524- from_bcb : BasicCoverageBlock ,
525- to_bcb : BasicCoverageBlock ,
526- ) -> Option < & BcbCounter > {
527- if self . basic_coverage_blocks . bcb_has_multiple_in_edges ( to_bcb) {
528- self . coverage_counters . bcb_edge_counters . get ( & ( from_bcb, to_bcb) )
529- } else {
530- self . coverage_counters . bcb_counters [ to_bcb] . as_ref ( )
531- }
518+ edge_counter. is_none ( )
532519 }
533520}
0 commit comments