@@ -95,11 +95,33 @@ impl CoverageCounters {
95
95
this
96
96
}
97
97
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 {
99
101
let id = self . counter_increment_sites . push ( site) ;
100
102
BcbCounter :: Counter { id }
101
103
}
102
104
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
+
103
125
fn make_expression ( & mut self , lhs : BcbCounter , op : Op , rhs : BcbCounter ) -> BcbCounter {
104
126
let new_expr = BcbExpression { lhs, op, rhs } ;
105
127
* self
@@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> {
294
316
295
317
let successors = self . basic_coverage_blocks . successors [ from_bcb] . as_slice ( ) ;
296
318
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 {
302
322
return ;
303
323
}
304
324
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 ) ;
312
332
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
+ } ;
316
340
317
341
// For each out-edge other than the one that was chosen to get an expression,
318
342
// ensure that it has a counter (existing counter/expression or a new counter),
@@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> {
324
348
. filter ( |& to_bcb| to_bcb != expression_to_bcb)
325
349
. map ( |to_bcb| self . get_or_make_edge_counter ( from_bcb, to_bcb) )
326
350
. 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
+ } ;
331
356
332
357
// Now create an expression for the chosen edge, by taking the counter
333
358
// for its source node and subtracting the sum of its sibling out-edges.
@@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> {
338
363
) ;
339
364
340
365
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) ;
344
370
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) ;
345
373
}
346
374
}
347
375
@@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> {
353
381
return counter_kind;
354
382
}
355
383
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) ;
373
394
}
374
395
375
396
// A BCB with multiple incoming edges can compute its count by ensuring that counters
376
397
// 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
378
399
. iter ( )
379
400
. copied ( )
380
401
. map ( |from_bcb| self . get_or_make_edge_counter ( from_bcb, bcb) )
@@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> {
394
415
from_bcb : BasicCoverageBlock ,
395
416
to_bcb : BasicCoverageBlock ,
396
417
) -> 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!
401
424
return self . get_or_make_node_counter ( to_bcb) ;
402
425
}
403
426
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) ;
407
431
return self . get_or_make_node_counter ( from_bcb) ;
408
432
}
409
433
@@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> {
416
440
}
417
441
418
442
// 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)
423
444
}
424
445
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.
430
448
fn choose_out_edge_for_expression (
431
449
& self ,
432
450
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) {
437
456
debug ! ( "Selecting reloop target {reloop_target:?} to get an expression" ) ;
438
- return reloop_target;
457
+ return Some ( reloop_target) ;
439
458
}
440
459
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 ( ) ?;
449
462
debug ! ( ?arbitrary_target, "selecting arbitrary out-edge to get an expression" ) ;
450
- arbitrary_target
463
+ Some ( arbitrary_target)
451
464
}
452
465
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.
457
472
fn find_good_reloop_edge (
458
473
& self ,
459
474
traversal : & TraverseCoverageGraphWithLoops < ' _ > ,
460
- from_bcb : BasicCoverageBlock ,
475
+ candidate_successors : & [ BasicCoverageBlock ] ,
461
476
) -> 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
+ }
463
481
464
482
// Consider each loop on the current traversal context stack, top-down.
465
483
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 {
471
486
// An edge is a reloop edge if its target dominates any BCB that has
472
487
// an edge back to the loop header. (Otherwise it's an exit edge.)
473
488
let is_reloop_edge = reloop_bcbs. iter ( ) . any ( |& reloop_bcb| {
474
489
self . basic_coverage_blocks . dominates ( target_bcb, reloop_bcb)
475
490
} ) ;
476
-
477
491
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) ;
486
494
}
487
495
}
488
496
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.
498
499
}
499
500
500
501
None
501
502
}
502
503
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
-
513
504
#[ inline]
514
505
fn edge_has_no_counter (
515
506
& self ,
516
507
from_bcb : BasicCoverageBlock ,
517
508
to_bcb : BasicCoverageBlock ,
518
509
) -> 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
+ } ;
521
517
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 ( )
532
519
}
533
520
}
0 commit comments