@@ -74,12 +74,11 @@ pub(super) struct CoverageCounters {
7474}
7575
7676impl CoverageCounters {
77- /// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
78- /// indirectly associated with coverage spans, and accumulates additional `Expression`s
79- /// representing intermediate values.
77+ /// Ensures that each BCB node needing a counter has one, by creating physical
78+ /// counters or counter expressions for nodes and edges as required.
8079 pub ( super ) fn make_bcb_counters (
8180 basic_coverage_blocks : & CoverageGraph ,
82- bcb_has_coverage_spans : impl Fn ( BasicCoverageBlock ) -> bool ,
81+ bcb_needs_counter : impl Fn ( BasicCoverageBlock ) -> bool ,
8382 ) -> Self {
8483 let num_bcbs = basic_coverage_blocks. num_nodes ( ) ;
8584
@@ -91,8 +90,7 @@ impl CoverageCounters {
9190 expressions_memo : FxHashMap :: default ( ) ,
9291 } ;
9392
94- MakeBcbCounters :: new ( & mut this, basic_coverage_blocks)
95- . make_bcb_counters ( bcb_has_coverage_spans) ;
93+ MakeBcbCounters :: new ( & mut this, basic_coverage_blocks) . make_bcb_counters ( bcb_needs_counter) ;
9694
9795 this
9896 }
@@ -241,10 +239,7 @@ impl CoverageCounters {
241239 }
242240}
243241
244- /// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
245- /// injected with coverage spans. `Expressions` have no runtime overhead, so if a viable expression
246- /// (adding or subtracting two other counters or expressions) can compute the same result as an
247- /// embedded counter, an `Expression` should be used.
242+ /// Helper struct that allows counter creation to inspect the BCB graph.
248243struct MakeBcbCounters < ' a > {
249244 coverage_counters : & ' a mut CoverageCounters ,
250245 basic_coverage_blocks : & ' a CoverageGraph ,
@@ -258,36 +253,21 @@ impl<'a> MakeBcbCounters<'a> {
258253 Self { coverage_counters, basic_coverage_blocks }
259254 }
260255
261- /// If two `BasicCoverageBlock`s branch from another `BasicCoverageBlock`, one of the branches
262- /// can be counted by `Expression` by subtracting the other branch from the branching
263- /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`.
264- /// One way to predict which branch executes the least is by considering loops. A loop is exited
265- /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost
266- /// always executed less than the branch that does not exit the loop.
267- fn make_bcb_counters ( & mut self , bcb_has_coverage_spans : impl Fn ( BasicCoverageBlock ) -> bool ) {
256+ fn make_bcb_counters ( & mut self , bcb_needs_counter : impl Fn ( BasicCoverageBlock ) -> bool ) {
268257 debug ! ( "make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock" ) ;
269258
270- // Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated
271- // coverage span, add a counter. If the `BasicCoverageBlock` branches, add a counter or
272- // expression to each branch `BasicCoverageBlock` (if the branch BCB has only one incoming
273- // edge) or edge from the branching BCB to the branch BCB (if the branch BCB has multiple
274- // incoming edges).
259+ // Traverse the coverage graph, ensuring that every node that needs a
260+ // coverage counter has one.
275261 //
276- // The `TraverseCoverageGraphWithLoops` traversal ensures that, when a loop is encountered,
277- // all `BasicCoverageBlock` nodes in the loop are visited before visiting any node outside
278- // the loop. The `traversal` state includes a `context_stack`, providing a way to know if
279- // the current BCB is in one or more nested loops or not .
262+ // The traversal tries to ensure that, when a loop is encountered, all
263+ // nodes within the loop are visited before visiting any nodes outside
264+ // the loop. It also keeps track of which loop(s) the traversal is
265+ // currently inside .
280266 let mut traversal = TraverseCoverageGraphWithLoops :: new ( self . basic_coverage_blocks ) ;
281267 while let Some ( bcb) = traversal. next ( ) {
282- if bcb_has_coverage_spans ( bcb) {
283- debug ! ( "{:?} has at least one coverage span. Get or make its counter" , bcb) ;
284- self . make_node_and_branch_counters ( & traversal, bcb) ;
285- } else {
286- debug ! (
287- "{:?} does not have any coverage spans. A counter will only be added if \
288- and when a covered BCB has an expression dependency.",
289- bcb,
290- ) ;
268+ let _span = debug_span ! ( "traversal" , ?bcb) . entered ( ) ;
269+ if bcb_needs_counter ( bcb) {
270+ self . make_node_counter_and_out_edge_counters ( & traversal, bcb) ;
291271 }
292272 }
293273
@@ -298,73 +278,66 @@ impl<'a> MakeBcbCounters<'a> {
298278 ) ;
299279 }
300280
301- fn make_node_and_branch_counters (
281+ /// Make sure the given node has a node counter, and then make sure each of
282+ /// its out-edges has an edge counter (if appropriate).
283+ #[ instrument( level = "debug" , skip( self , traversal) ) ]
284+ fn make_node_counter_and_out_edge_counters (
302285 & mut self ,
303286 traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
304287 from_bcb : BasicCoverageBlock ,
305288 ) {
306289 // First, ensure that this node has a counter of some kind.
307- // We might also use its term later to compute one of the branch counters.
308- let from_bcb_operand = self . get_or_make_counter_operand ( from_bcb) ;
290+ // We might also use that counter to compute one of the out-edge counters.
291+ let node_counter = self . get_or_make_node_counter ( from_bcb) ;
309292
310- let branch_target_bcbs = self . basic_coverage_blocks . successors [ from_bcb] . as_slice ( ) ;
293+ let successors = self . basic_coverage_blocks . successors [ from_bcb] . as_slice ( ) ;
311294
312295 // If this node doesn't have multiple out-edges, or all of its out-edges
313296 // already have counters, then we don't need to create edge counters.
314- let needs_branch_counters = branch_target_bcbs. len ( ) > 1
315- && branch_target_bcbs
316- . iter ( )
317- . any ( |& to_bcb| self . branch_has_no_counter ( from_bcb, to_bcb) ) ;
318- if !needs_branch_counters {
297+ let needs_out_edge_counters = successors. len ( ) > 1
298+ && successors. iter ( ) . any ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) ) ;
299+ if !needs_out_edge_counters {
319300 return ;
320301 }
321302
322- debug ! (
323- "{from_bcb:?} has some branch(es) without counters:\n {}" ,
324- branch_target_bcbs
325- . iter( )
326- . map( |& to_bcb| {
327- format!( "{from_bcb:?}->{to_bcb:?}: {:?}" , self . branch_counter( from_bcb, to_bcb) )
328- } )
329- . collect:: <Vec <_>>( )
330- . join( "\n " ) ,
331- ) ;
303+ if tracing:: enabled!( tracing:: Level :: DEBUG ) {
304+ let _span =
305+ debug_span ! ( "node has some out-edges without counters" , ?from_bcb) . entered ( ) ;
306+ for & to_bcb in successors {
307+ debug ! ( ?to_bcb, counter=?self . edge_counter( from_bcb, to_bcb) ) ;
308+ }
309+ }
332310
333- // Of the branch edges that don't have counters yet, one can be given an expression
334- // (computed from the other edges) instead of a dedicated counter.
335- let expression_to_bcb = self . choose_preferred_expression_branch ( traversal, from_bcb) ;
311+ // Of the out- edges that don't have counters yet, one can be given an expression
312+ // (computed from the other out- edges) instead of a dedicated counter.
313+ let expression_to_bcb = self . choose_out_edge_for_expression ( traversal, from_bcb) ;
336314
337- // For each branch arm other than the one that was chosen to get an expression,
315+ // For each out-edge other than the one that was chosen to get an expression,
338316 // ensure that it has a counter (existing counter/expression or a new counter),
339- // and accumulate the corresponding terms into a single sum term .
340- let sum_of_all_other_branches : BcbCounter = {
341- let _span = debug_span ! ( "sum_of_all_other_branches " , ?expression_to_bcb) . entered ( ) ;
342- branch_target_bcbs
317+ // and accumulate the corresponding counters into a single sum expression .
318+ let sum_of_all_other_out_edges : BcbCounter = {
319+ let _span = debug_span ! ( "sum_of_all_other_out_edges " , ?expression_to_bcb) . entered ( ) ;
320+ successors
343321 . iter ( )
344322 . copied ( )
345- // Skip the chosen branch , since we'll calculate it from the other branches .
323+ // Skip the chosen edge , since we'll calculate its count from this sum .
346324 . filter ( |& to_bcb| to_bcb != expression_to_bcb)
347325 . fold ( None , |accum, to_bcb| {
348326 let _span = debug_span ! ( "to_bcb" , ?accum, ?to_bcb) . entered ( ) ;
349- let branch_counter = self . get_or_make_edge_counter_operand ( from_bcb, to_bcb) ;
350- Some ( self . coverage_counters . make_sum_expression ( accum, branch_counter ) )
327+ let edge_counter = self . get_or_make_edge_counter ( from_bcb, to_bcb) ;
328+ Some ( self . coverage_counters . make_sum_expression ( accum, edge_counter ) )
351329 } )
352- . expect ( "there must be at least one other branch " )
330+ . expect ( "there must be at least one other out-edge " )
353331 } ;
354332
355- // For the branch that was chosen to get an expression, create that expression
356- // by taking the count of the node we're branching from, and subtracting the
357- // sum of all the other branches.
358- debug ! (
359- "Making an expression for the selected expression_branch: \
360- {expression_to_bcb:?} (expression_branch predecessors: {:?})",
361- self . bcb_predecessors( expression_to_bcb) ,
362- ) ;
333+ // Now create an expression for the chosen edge, by taking the counter
334+ // for its source node and subtracting the sum of its sibling out-edges.
363335 let expression = self . coverage_counters . make_expression (
364- from_bcb_operand ,
336+ node_counter ,
365337 Op :: Subtract ,
366- sum_of_all_other_branches ,
338+ sum_of_all_other_out_edges ,
367339 ) ;
340+
368341 debug ! ( "{expression_to_bcb:?} gets an expression: {expression:?}" ) ;
369342 if self . basic_coverage_blocks . bcb_has_multiple_in_edges ( expression_to_bcb) {
370343 self . coverage_counters . set_bcb_edge_counter ( from_bcb, expression_to_bcb, expression) ;
@@ -374,7 +347,7 @@ impl<'a> MakeBcbCounters<'a> {
374347 }
375348
376349 #[ instrument( level = "debug" , skip( self ) ) ]
377- fn get_or_make_counter_operand ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
350+ fn get_or_make_node_counter ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
378351 // If the BCB already has a counter, return it.
379352 if let Some ( counter_kind) = self . coverage_counters . bcb_counters [ bcb] {
380353 debug ! ( "{bcb:?} already has a counter: {counter_kind:?}" ) ;
@@ -411,7 +384,7 @@ impl<'a> MakeBcbCounters<'a> {
411384 . copied ( )
412385 . fold ( None , |accum, from_bcb| {
413386 let _span = debug_span ! ( "from_bcb" , ?accum, ?from_bcb) . entered ( ) ;
414- let edge_counter = self . get_or_make_edge_counter_operand ( from_bcb, bcb) ;
387+ let edge_counter = self . get_or_make_edge_counter ( from_bcb, bcb) ;
415388 Some ( self . coverage_counters . make_sum_expression ( accum, edge_counter) )
416389 } )
417390 . expect ( "there must be at least one in-edge" )
@@ -422,7 +395,7 @@ impl<'a> MakeBcbCounters<'a> {
422395 }
423396
424397 #[ instrument( level = "debug" , skip( self ) ) ]
425- fn get_or_make_edge_counter_operand (
398+ fn get_or_make_edge_counter (
426399 & mut self ,
427400 from_bcb : BasicCoverageBlock ,
428401 to_bcb : BasicCoverageBlock ,
@@ -431,13 +404,13 @@ impl<'a> MakeBcbCounters<'a> {
431404 // a node counter instead, since it will have the same value.
432405 if !self . basic_coverage_blocks . bcb_has_multiple_in_edges ( to_bcb) {
433406 assert_eq ! ( [ from_bcb] . as_slice( ) , self . basic_coverage_blocks. predecessors[ to_bcb] ) ;
434- return self . get_or_make_counter_operand ( to_bcb) ;
407+ return self . get_or_make_node_counter ( to_bcb) ;
435408 }
436409
437410 // If the source BCB has only one successor (assumed to be the given target), an edge
438411 // counter is unnecessary. Just get or make a counter for the source BCB.
439412 if self . bcb_successors ( from_bcb) . len ( ) == 1 {
440- return self . get_or_make_counter_operand ( from_bcb) ;
413+ return self . get_or_make_node_counter ( from_bcb) ;
441414 }
442415
443416 // If the edge already has a counter, return it.
@@ -455,82 +428,79 @@ impl<'a> MakeBcbCounters<'a> {
455428 self . coverage_counters . set_bcb_edge_counter ( from_bcb, to_bcb, counter_kind)
456429 }
457430
458- /// Select a branch for the expression, either the recommended `reloop_branch`, or if none was
459- /// found, select any branch.
460- fn choose_preferred_expression_branch (
431+ /// Choose one of the out-edges of `from_bcb` to receive an expression
432+ /// instead of a physical counter, and returns that edge's target node.
433+ ///
434+ /// - Precondition: The node must have at least one out-edge without a counter.
435+ /// - Postcondition: The selected edge does not have an edge counter.
436+ fn choose_out_edge_for_expression (
461437 & self ,
462438 traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
463439 from_bcb : BasicCoverageBlock ,
464440 ) -> BasicCoverageBlock {
465- let good_reloop_branch = self . find_good_reloop_branch ( traversal, from_bcb) ;
466- if let Some ( reloop_target) = good_reloop_branch {
467- assert ! ( self . branch_has_no_counter( from_bcb, reloop_target) ) ;
441+ if let Some ( reloop_target) = self . find_good_reloop_edge ( traversal, from_bcb) {
442+ assert ! ( self . edge_has_no_counter( from_bcb, reloop_target) ) ;
468443 debug ! ( "Selecting reloop target {reloop_target:?} to get an expression" ) ;
469- reloop_target
470- } else {
471- let & branch_without_counter = self
472- . bcb_successors ( from_bcb)
473- . iter ( )
474- . find ( |& & to_bcb| self . branch_has_no_counter ( from_bcb, to_bcb) )
475- . expect (
476- "needs_branch_counters was `true` so there should be at least one \
477- branch",
478- ) ;
479- debug ! (
480- "Selecting any branch={:?} that still needs a counter, to get the \
481- `Expression` because there was no `reloop_branch`, or it already had a \
482- counter",
483- branch_without_counter
484- ) ;
485- branch_without_counter
444+ return reloop_target;
486445 }
446+
447+ // We couldn't identify a "good" edge, so just choose any edge that
448+ // doesn't already have a counter.
449+ let arbitrary_target = self
450+ . bcb_successors ( from_bcb)
451+ . iter ( )
452+ . copied ( )
453+ . find ( |& to_bcb| self . edge_has_no_counter ( from_bcb, to_bcb) )
454+ . expect ( "precondition: at least one out-edge without a counter" ) ;
455+ debug ! ( ?arbitrary_target, "selecting arbitrary out-edge to get an expression" ) ;
456+ arbitrary_target
487457 }
488458
489- /// Tries to find a branch that leads back to the top of a loop, and that
490- /// doesn't already have a counter. Such branches are good candidates to
459+ /// Tries to find an edge that leads back to the top of a loop, and that
460+ /// doesn't already have a counter. Such edges are good candidates to
491461 /// be given an expression (instead of a physical counter), because they
492- /// will tend to be executed more times than a loop-exit branch .
493- fn find_good_reloop_branch (
462+ /// will tend to be executed more times than a loop-exit edge .
463+ fn find_good_reloop_edge (
494464 & self ,
495465 traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
496466 from_bcb : BasicCoverageBlock ,
497467 ) -> Option < BasicCoverageBlock > {
498- let branch_target_bcbs = self . bcb_successors ( from_bcb) ;
468+ let successors = self . bcb_successors ( from_bcb) ;
499469
500470 // Consider each loop on the current traversal context stack, top-down.
501471 for reloop_bcbs in traversal. reloop_bcbs_per_loop ( ) {
502- let mut all_branches_exit_this_loop = true ;
472+ let mut all_edges_exit_this_loop = true ;
503473
504- // Try to find a branch that doesn't exit this loop and doesn't
474+ // Try to find an out-edge that doesn't exit this loop and doesn't
505475 // already have a counter.
506- for & branch_target_bcb in branch_target_bcbs {
507- // A branch is a reloop branch if it dominates any BCB that has
508- // an edge back to the loop header. (Other branches are exits .)
509- let is_reloop_branch = reloop_bcbs. iter ( ) . any ( |& reloop_bcb| {
510- self . basic_coverage_blocks . dominates ( branch_target_bcb , reloop_bcb)
476+ for & target_bcb in successors {
477+ // An edge is a reloop edge if its target dominates any BCB that has
478+ // an edge back to the loop header. (Otherwise it's an exit edge .)
479+ let is_reloop_edge = reloop_bcbs. iter ( ) . any ( |& reloop_bcb| {
480+ self . basic_coverage_blocks . dominates ( target_bcb , reloop_bcb)
511481 } ) ;
512482
513- if is_reloop_branch {
514- all_branches_exit_this_loop = false ;
515- if self . branch_has_no_counter ( from_bcb, branch_target_bcb ) {
516- // We found a good branch to be given an expression.
517- return Some ( branch_target_bcb ) ;
483+ if is_reloop_edge {
484+ all_edges_exit_this_loop = false ;
485+ if self . edge_has_no_counter ( from_bcb, target_bcb ) {
486+ // We found a good out-edge to be given an expression.
487+ return Some ( target_bcb ) ;
518488 }
519- // Keep looking for another reloop branch without a counter.
489+ // Keep looking for another reloop edge without a counter.
520490 } else {
521- // This branch exits the loop.
491+ // This edge exits the loop.
522492 }
523493 }
524494
525- if !all_branches_exit_this_loop {
526- // We found one or more reloop branches , but all of them already
527- // have counters. Let the caller choose one of the exit branches .
528- debug ! ( "All reloop branches had counters; skip checking the other loops" ) ;
495+ if !all_edges_exit_this_loop {
496+ // We found one or more reloop edges , but all of them already
497+ // have counters. Let the caller choose one of the other edges .
498+ debug ! ( "All reloop edges had counters; skipping the other loops" ) ;
529499 return None ;
530500 }
531501
532- // All of the branches exit this loop, so keep looking for a good
533- // reloop branch for one of the outer loops.
502+ // All of the out-edges exit this loop, so keep looking for a good
503+ // reloop edge for one of the outer loops.
534504 }
535505
536506 None
@@ -547,15 +517,15 @@ impl<'a> MakeBcbCounters<'a> {
547517 }
548518
549519 #[ inline]
550- fn branch_has_no_counter (
520+ fn edge_has_no_counter (
551521 & self ,
552522 from_bcb : BasicCoverageBlock ,
553523 to_bcb : BasicCoverageBlock ,
554524 ) -> bool {
555- self . branch_counter ( from_bcb, to_bcb) . is_none ( )
525+ self . edge_counter ( from_bcb, to_bcb) . is_none ( )
556526 }
557527
558- fn branch_counter (
528+ fn edge_counter (
559529 & self ,
560530 from_bcb : BasicCoverageBlock ,
561531 to_bcb : BasicCoverageBlock ,
0 commit comments