@@ -74,12 +74,11 @@ pub(super) struct CoverageCounters {
74
74
}
75
75
76
76
impl 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.
80
79
pub ( super ) fn make_bcb_counters (
81
80
basic_coverage_blocks : & CoverageGraph ,
82
- bcb_has_coverage_spans : impl Fn ( BasicCoverageBlock ) -> bool ,
81
+ bcb_needs_counter : impl Fn ( BasicCoverageBlock ) -> bool ,
83
82
) -> Self {
84
83
let num_bcbs = basic_coverage_blocks. num_nodes ( ) ;
85
84
@@ -91,8 +90,7 @@ impl CoverageCounters {
91
90
expressions_memo : FxHashMap :: default ( ) ,
92
91
} ;
93
92
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) ;
96
94
97
95
this
98
96
}
@@ -241,10 +239,7 @@ impl CoverageCounters {
241
239
}
242
240
}
243
241
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.
248
243
struct MakeBcbCounters < ' a > {
249
244
coverage_counters : & ' a mut CoverageCounters ,
250
245
basic_coverage_blocks : & ' a CoverageGraph ,
@@ -258,36 +253,21 @@ impl<'a> MakeBcbCounters<'a> {
258
253
Self { coverage_counters, basic_coverage_blocks }
259
254
}
260
255
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 ) {
268
257
debug ! ( "make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock" ) ;
269
258
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.
275
261
//
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 .
280
266
let mut traversal = TraverseCoverageGraphWithLoops :: new ( self . basic_coverage_blocks ) ;
281
267
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) ;
291
271
}
292
272
}
293
273
@@ -298,73 +278,66 @@ impl<'a> MakeBcbCounters<'a> {
298
278
) ;
299
279
}
300
280
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 (
302
285
& mut self ,
303
286
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
304
287
from_bcb : BasicCoverageBlock ,
305
288
) {
306
289
// 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) ;
309
292
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 ( ) ;
311
294
312
295
// If this node doesn't have multiple out-edges, or all of its out-edges
313
296
// 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 {
319
300
return ;
320
301
}
321
302
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
+ }
332
310
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) ;
336
314
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,
338
316
// 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
343
321
. iter ( )
344
322
. 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 .
346
324
. filter ( |& to_bcb| to_bcb != expression_to_bcb)
347
325
. fold ( None , |accum, to_bcb| {
348
326
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 ) )
351
329
} )
352
- . expect ( "there must be at least one other branch " )
330
+ . expect ( "there must be at least one other out-edge " )
353
331
} ;
354
332
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.
363
335
let expression = self . coverage_counters . make_expression (
364
- from_bcb_operand ,
336
+ node_counter ,
365
337
Op :: Subtract ,
366
- sum_of_all_other_branches ,
338
+ sum_of_all_other_out_edges ,
367
339
) ;
340
+
368
341
debug ! ( "{expression_to_bcb:?} gets an expression: {expression:?}" ) ;
369
342
if self . basic_coverage_blocks . bcb_has_multiple_in_edges ( expression_to_bcb) {
370
343
self . coverage_counters . set_bcb_edge_counter ( from_bcb, expression_to_bcb, expression) ;
@@ -374,7 +347,7 @@ impl<'a> MakeBcbCounters<'a> {
374
347
}
375
348
376
349
#[ 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 {
378
351
// If the BCB already has a counter, return it.
379
352
if let Some ( counter_kind) = self . coverage_counters . bcb_counters [ bcb] {
380
353
debug ! ( "{bcb:?} already has a counter: {counter_kind:?}" ) ;
@@ -411,7 +384,7 @@ impl<'a> MakeBcbCounters<'a> {
411
384
. copied ( )
412
385
. fold ( None , |accum, from_bcb| {
413
386
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) ;
415
388
Some ( self . coverage_counters . make_sum_expression ( accum, edge_counter) )
416
389
} )
417
390
. expect ( "there must be at least one in-edge" )
@@ -422,7 +395,7 @@ impl<'a> MakeBcbCounters<'a> {
422
395
}
423
396
424
397
#[ instrument( level = "debug" , skip( self ) ) ]
425
- fn get_or_make_edge_counter_operand (
398
+ fn get_or_make_edge_counter (
426
399
& mut self ,
427
400
from_bcb : BasicCoverageBlock ,
428
401
to_bcb : BasicCoverageBlock ,
@@ -431,13 +404,13 @@ impl<'a> MakeBcbCounters<'a> {
431
404
// a node counter instead, since it will have the same value.
432
405
if !self . basic_coverage_blocks . bcb_has_multiple_in_edges ( to_bcb) {
433
406
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) ;
435
408
}
436
409
437
410
// If the source BCB has only one successor (assumed to be the given target), an edge
438
411
// counter is unnecessary. Just get or make a counter for the source BCB.
439
412
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) ;
441
414
}
442
415
443
416
// If the edge already has a counter, return it.
@@ -455,82 +428,79 @@ impl<'a> MakeBcbCounters<'a> {
455
428
self . coverage_counters . set_bcb_edge_counter ( from_bcb, to_bcb, counter_kind)
456
429
}
457
430
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 (
461
437
& self ,
462
438
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
463
439
from_bcb : BasicCoverageBlock ,
464
440
) -> 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) ) ;
468
443
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;
486
445
}
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
487
457
}
488
458
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
491
461
/// 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 (
494
464
& self ,
495
465
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
496
466
from_bcb : BasicCoverageBlock ,
497
467
) -> Option < BasicCoverageBlock > {
498
- let branch_target_bcbs = self . bcb_successors ( from_bcb) ;
468
+ let successors = self . bcb_successors ( from_bcb) ;
499
469
500
470
// Consider each loop on the current traversal context stack, top-down.
501
471
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 ;
503
473
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
505
475
// 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)
511
481
} ) ;
512
482
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 ) ;
518
488
}
519
- // Keep looking for another reloop branch without a counter.
489
+ // Keep looking for another reloop edge without a counter.
520
490
} else {
521
- // This branch exits the loop.
491
+ // This edge exits the loop.
522
492
}
523
493
}
524
494
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" ) ;
529
499
return None ;
530
500
}
531
501
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.
534
504
}
535
505
536
506
None
@@ -547,15 +517,15 @@ impl<'a> MakeBcbCounters<'a> {
547
517
}
548
518
549
519
#[ inline]
550
- fn branch_has_no_counter (
520
+ fn edge_has_no_counter (
551
521
& self ,
552
522
from_bcb : BasicCoverageBlock ,
553
523
to_bcb : BasicCoverageBlock ,
554
524
) -> bool {
555
- self . branch_counter ( from_bcb, to_bcb) . is_none ( )
525
+ self . edge_counter ( from_bcb, to_bcb) . is_none ( )
556
526
}
557
527
558
- fn branch_counter (
528
+ fn edge_counter (
559
529
& self ,
560
530
from_bcb : BasicCoverageBlock ,
561
531
to_bcb : BasicCoverageBlock ,
0 commit comments