@@ -63,12 +63,6 @@ pub(super) struct CoverageCounters {
63
63
64
64
/// Coverage counters/expressions that are associated with individual BCBs.
65
65
node_counters : IndexVec < BasicCoverageBlock , Option < BcbCounter > > ,
66
- /// Coverage counters/expressions that are associated with the control-flow
67
- /// edge between two BCBs.
68
- ///
69
- /// We currently don't iterate over this map, but if we do in the future,
70
- /// switch it back to `FxIndexMap` to avoid query stability hazards.
71
- edge_counters : FxHashMap < ( BasicCoverageBlock , BasicCoverageBlock ) , BcbCounter > ,
72
66
73
67
/// Table of expression data, associating each expression ID with its
74
68
/// corresponding operator (+ or -) and its LHS/RHS operands.
@@ -95,7 +89,6 @@ impl CoverageCounters {
95
89
Self {
96
90
counter_increment_sites : IndexVec :: new ( ) ,
97
91
node_counters : IndexVec :: from_elem_n ( None , num_bcbs) ,
98
- edge_counters : FxHashMap :: default ( ) ,
99
92
expressions : IndexVec :: new ( ) ,
100
93
expressions_memo : FxHashMap :: default ( ) ,
101
94
}
@@ -191,20 +184,6 @@ impl CoverageCounters {
191
184
counter
192
185
}
193
186
194
- fn set_edge_counter (
195
- & mut self ,
196
- from_bcb : BasicCoverageBlock ,
197
- to_bcb : BasicCoverageBlock ,
198
- counter : BcbCounter ,
199
- ) -> BcbCounter {
200
- let existing = self . edge_counters . insert ( ( from_bcb, to_bcb) , counter) ;
201
- assert ! (
202
- existing. is_none( ) ,
203
- "edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}"
204
- ) ;
205
- counter
206
- }
207
-
208
187
pub ( super ) fn term_for_bcb ( & self , bcb : BasicCoverageBlock ) -> Option < CovTerm > {
209
188
self . node_counters [ bcb] . map ( |counter| counter. as_term ( ) )
210
189
}
@@ -250,22 +229,53 @@ impl CoverageCounters {
250
229
}
251
230
}
252
231
232
+ /// Symbolic representation of the coverage counter to be used for a particular
233
+ /// node or edge in the coverage graph. The same site counter can be used for
234
+ /// multiple sites, if they have been determined to have the same count.
235
+ #[ derive( Clone , Copy , Debug ) ]
236
+ enum SiteCounter {
237
+ /// A physical counter at some node/edge.
238
+ Phys { site : Site } ,
239
+ /// A counter expression for a node that takes the sum of all its in-edge
240
+ /// counters.
241
+ NodeSumExpr { bcb : BasicCoverageBlock } ,
242
+ /// A counter expression for an edge that takes the counter of its source
243
+ /// node, and subtracts the counters of all its sibling out-edges.
244
+ EdgeDiffExpr { from_bcb : BasicCoverageBlock , to_bcb : BasicCoverageBlock } ,
245
+ }
246
+
247
+ /// Yields the graph successors of `from_bcb` that aren't `to_bcb`. This is
248
+ /// used when creating a counter expression for [`SiteCounter::EdgeDiffExpr`].
249
+ ///
250
+ /// For example, in this diagram the sibling out-edge targets of edge `AC` are
251
+ /// the nodes `B` and `D`.
252
+ ///
253
+ /// ```text
254
+ /// A
255
+ /// / | \
256
+ /// B C D
257
+ /// ```
258
+ fn sibling_out_edge_targets (
259
+ graph : & CoverageGraph ,
260
+ from_bcb : BasicCoverageBlock ,
261
+ to_bcb : BasicCoverageBlock ,
262
+ ) -> impl Iterator < Item = BasicCoverageBlock > + Captures < ' _ > {
263
+ graph. successors [ from_bcb] . iter ( ) . copied ( ) . filter ( move |& t| t != to_bcb)
264
+ }
265
+
253
266
/// Helper struct that allows counter creation to inspect the BCB graph, and
254
267
/// the set of nodes that need counters.
255
268
struct CountersBuilder < ' a > {
256
- counters : CoverageCounters ,
257
269
graph : & ' a CoverageGraph ,
258
270
bcb_needs_counter : & ' a BitSet < BasicCoverageBlock > ,
271
+
272
+ site_counters : FxHashMap < Site , SiteCounter > ,
259
273
}
260
274
261
275
impl < ' a > CountersBuilder < ' a > {
262
276
fn new ( graph : & ' a CoverageGraph , bcb_needs_counter : & ' a BitSet < BasicCoverageBlock > ) -> Self {
263
277
assert_eq ! ( graph. num_nodes( ) , bcb_needs_counter. domain_size( ) ) ;
264
- Self {
265
- counters : CoverageCounters :: with_num_bcbs ( graph. num_nodes ( ) ) ,
266
- graph,
267
- bcb_needs_counter,
268
- }
278
+ Self { graph, bcb_needs_counter, site_counters : FxHashMap :: default ( ) }
269
279
}
270
280
271
281
fn make_bcb_counters ( & mut self ) {
@@ -298,9 +308,7 @@ impl<'a> CountersBuilder<'a> {
298
308
fn make_node_counter_and_out_edge_counters ( & mut self , from_bcb : BasicCoverageBlock ) {
299
309
// First, ensure that this node has a counter of some kind.
300
310
// We might also use that counter to compute one of the out-edge counters.
301
- let node_counter = self . get_or_make_node_counter ( from_bcb) ;
302
-
303
- let successors = self . graph . successors [ from_bcb] . as_slice ( ) ;
311
+ self . get_or_make_node_counter ( from_bcb) ;
304
312
305
313
// If this node's out-edges won't sum to the node's counter,
306
314
// then there's no reason to create edge counters here.
@@ -311,11 +319,11 @@ impl<'a> CountersBuilder<'a> {
311
319
// When choosing which out-edge should be given a counter expression, ignore edges that
312
320
// already have counters, or could use the existing counter of their target node.
313
321
let out_edge_has_counter = |to_bcb| {
314
- if self . counters . edge_counters . contains_key ( & ( from_bcb, to_bcb) ) {
322
+ if self . site_counters . contains_key ( & Site :: Edge { from_bcb, to_bcb } ) {
315
323
return true ;
316
324
}
317
325
self . graph . sole_predecessor ( to_bcb) == Some ( from_bcb)
318
- && self . counters . node_counters [ to_bcb ] . is_some ( )
326
+ && self . site_counters . contains_key ( & Site :: Node { bcb : to_bcb } )
319
327
} ;
320
328
321
329
// Determine the set of out-edges that could benefit from being given an expression.
@@ -328,45 +336,41 @@ impl<'a> CountersBuilder<'a> {
328
336
329
337
// If there are out-edges without counters, choose one to be given an expression
330
338
// (computed from this node and the other out-edges) instead of a physical counter.
331
- let Some ( target_bcb ) = self . choose_out_edge_for_expression ( from_bcb, & candidate_successors)
339
+ let Some ( to_bcb ) = self . choose_out_edge_for_expression ( from_bcb, & candidate_successors)
332
340
else {
333
341
return ;
334
342
} ;
335
343
336
344
// For each out-edge other than the one that was chosen to get an expression,
337
345
// ensure that it has a counter (existing counter/expression or a new counter).
338
- let other_out_edge_counters = successors
339
- . iter ( )
340
- . copied ( )
341
- // Skip the chosen edge, since we'll calculate its count from this sum.
342
- . filter ( |& edge_target_bcb| edge_target_bcb != target_bcb)
343
- . map ( |to_bcb| self . get_or_make_edge_counter ( from_bcb, to_bcb) )
344
- . collect :: < Vec < _ > > ( ) ;
346
+ for target in sibling_out_edge_targets ( self . graph , from_bcb, to_bcb) {
347
+ self . get_or_make_edge_counter ( from_bcb, target) ;
348
+ }
345
349
346
350
// Now create an expression for the chosen edge, by taking the counter
347
351
// for its source node and subtracting the sum of its sibling out-edges.
348
- let expression = self . counters . make_subtracted_sum ( node_counter, & other_out_edge_counters) ;
349
-
350
- debug ! ( "{target_bcb:?} gets an expression: {expression:?}" ) ;
351
- self . counters . set_edge_counter ( from_bcb, target_bcb, expression) ;
352
+ let counter = SiteCounter :: EdgeDiffExpr { from_bcb, to_bcb } ;
353
+ self . site_counters . insert ( Site :: Edge { from_bcb, to_bcb } , counter) ;
352
354
}
353
355
354
356
#[ instrument( level = "debug" , skip( self ) ) ]
355
- fn get_or_make_node_counter ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
357
+ fn get_or_make_node_counter ( & mut self , bcb : BasicCoverageBlock ) -> SiteCounter {
356
358
// If the BCB already has a counter, return it.
357
- if let Some ( counter) = self . counters . node_counters [ bcb] {
359
+ if let Some ( & counter) = self . site_counters . get ( & Site :: Node { bcb } ) {
358
360
debug ! ( "{bcb:?} already has a counter: {counter:?}" ) ;
359
361
return counter;
360
362
}
361
363
362
364
let counter = self . make_node_counter_inner ( bcb) ;
363
- self . counters . set_node_counter ( bcb, counter)
365
+ self . site_counters . insert ( Site :: Node { bcb } , counter) ;
366
+ counter
364
367
}
365
368
366
- fn make_node_counter_inner ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
369
+ fn make_node_counter_inner ( & mut self , bcb : BasicCoverageBlock ) -> SiteCounter {
367
370
// If the node's sole in-edge already has a counter, use that.
368
371
if let Some ( sole_pred) = self . graph . sole_predecessor ( bcb)
369
- && let Some ( & edge_counter) = self . counters . edge_counters . get ( & ( sole_pred, bcb) )
372
+ && let Some ( & edge_counter) =
373
+ self . site_counters . get ( & Site :: Edge { from_bcb : sole_pred, to_bcb : bcb } )
370
374
{
371
375
return edge_counter;
372
376
}
@@ -380,20 +384,17 @@ impl<'a> CountersBuilder<'a> {
380
384
// leading to infinite recursion.
381
385
if predecessors. len ( ) <= 1 || predecessors. contains ( & bcb) {
382
386
debug ! ( ?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor" ) ;
383
- let counter = self . counters . make_phys_counter ( Site :: Node { bcb } ) ;
387
+ let counter = SiteCounter :: Phys { site : Site :: Node { bcb } } ;
384
388
debug ! ( ?bcb, ?counter, "node gets a physical counter" ) ;
385
389
return counter;
386
390
}
387
391
388
392
// A BCB with multiple incoming edges can compute its count by ensuring that counters
389
393
// exist for each of those edges, and then adding them up to get a total count.
390
- let in_edge_counters = predecessors
391
- . iter ( )
392
- . copied ( )
393
- . map ( |from_bcb| self . get_or_make_edge_counter ( from_bcb, bcb) )
394
- . collect :: < Vec < _ > > ( ) ;
395
- let sum_of_in_edges: BcbCounter =
396
- self . counters . make_sum ( & in_edge_counters) . expect ( "there must be at least one in-edge" ) ;
394
+ for & from_bcb in predecessors {
395
+ self . get_or_make_edge_counter ( from_bcb, bcb) ;
396
+ }
397
+ let sum_of_in_edges = SiteCounter :: NodeSumExpr { bcb } ;
397
398
398
399
debug ! ( "{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}" ) ;
399
400
sum_of_in_edges
@@ -404,22 +405,23 @@ impl<'a> CountersBuilder<'a> {
404
405
& mut self ,
405
406
from_bcb : BasicCoverageBlock ,
406
407
to_bcb : BasicCoverageBlock ,
407
- ) -> BcbCounter {
408
+ ) -> SiteCounter {
408
409
// If the edge already has a counter, return it.
409
- if let Some ( & counter) = self . counters . edge_counters . get ( & ( from_bcb, to_bcb) ) {
410
+ if let Some ( & counter) = self . site_counters . get ( & Site :: Edge { from_bcb, to_bcb } ) {
410
411
debug ! ( "Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}" ) ;
411
412
return counter;
412
413
}
413
414
414
415
let counter = self . make_edge_counter_inner ( from_bcb, to_bcb) ;
415
- self . counters . set_edge_counter ( from_bcb, to_bcb, counter)
416
+ self . site_counters . insert ( Site :: Edge { from_bcb, to_bcb } , counter) ;
417
+ counter
416
418
}
417
419
418
420
fn make_edge_counter_inner (
419
421
& mut self ,
420
422
from_bcb : BasicCoverageBlock ,
421
423
to_bcb : BasicCoverageBlock ,
422
- ) -> BcbCounter {
424
+ ) -> SiteCounter {
423
425
// If the target node has exactly one in-edge (i.e. this one), then just
424
426
// use the node's counter, since it will have the same value.
425
427
if let Some ( sole_pred) = self . graph . sole_predecessor ( to_bcb) {
@@ -437,7 +439,7 @@ impl<'a> CountersBuilder<'a> {
437
439
}
438
440
439
441
// Make a new counter to count this edge.
440
- let counter = self . counters . make_phys_counter ( Site :: Edge { from_bcb, to_bcb } ) ;
442
+ let counter = SiteCounter :: Phys { site : Site :: Edge { from_bcb, to_bcb } } ;
441
443
debug ! ( ?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter" ) ;
442
444
counter
443
445
}
@@ -525,14 +527,14 @@ impl<'a> Transcriber<'a> {
525
527
fn transcribe_counters ( mut self ) -> CoverageCounters {
526
528
for bcb in self . old . bcb_needs_counter . iter ( ) {
527
529
let site = Site :: Node { bcb } ;
528
- let Some ( old_counter ) = self . old . counters . node_counters [ bcb ] else { continue } ;
530
+ let site_counter = self . site_counter ( site ) ;
529
531
530
- // Resolve the old counter into flat lists of nodes/edges whose
532
+ // Resolve the site counter into flat lists of nodes/edges whose
531
533
// physical counts contribute to the counter for this node.
532
534
// Distinguish between counts that will be added vs subtracted.
533
535
let mut pos = vec ! [ ] ;
534
536
let mut neg = vec ! [ ] ;
535
- self . push_resolved_sites ( old_counter , & mut pos, & mut neg) ;
537
+ self . push_resolved_sites ( site_counter , & mut pos, & mut neg) ;
536
538
537
539
// Simplify by cancelling out sites that appear on both sides.
538
540
let ( mut pos, mut neg) = sort_and_cancel ( pos, neg) ;
@@ -566,22 +568,41 @@ impl<'a> Transcriber<'a> {
566
568
self . new
567
569
}
568
570
571
+ fn site_counter ( & self , site : Site ) -> SiteCounter {
572
+ self . old . site_counters . get ( & site) . copied ( ) . unwrap_or_else ( || {
573
+ // We should have already created all necessary site counters.
574
+ // But if we somehow didn't, avoid crashing in release builds,
575
+ // and just use an extra physical counter instead.
576
+ debug_assert ! ( false , "{site:?} should have a counter" ) ;
577
+ SiteCounter :: Phys { site }
578
+ } )
579
+ }
580
+
569
581
fn ensure_phys_counter ( & mut self , site : Site ) -> BcbCounter {
570
582
* self . phys_counter_for_site . entry ( site) . or_insert_with ( || self . new . make_phys_counter ( site) )
571
583
}
572
584
573
585
/// Resolves the given counter into flat lists of nodes/edges, whose counters
574
586
/// will then be added and subtracted to form a counter expression.
575
- fn push_resolved_sites ( & self , counter : BcbCounter , pos : & mut Vec < Site > , neg : & mut Vec < Site > ) {
587
+ fn push_resolved_sites ( & self , counter : SiteCounter , pos : & mut Vec < Site > , neg : & mut Vec < Site > ) {
576
588
match counter {
577
- BcbCounter :: Counter { id } => pos. push ( self . old . counters . counter_increment_sites [ id] ) ,
578
- BcbCounter :: Expression { id } => {
579
- let BcbExpression { lhs, op, rhs } = self . old . counters . expressions [ id] ;
580
- self . push_resolved_sites ( lhs, pos, neg) ;
581
- match op {
582
- Op :: Add => self . push_resolved_sites ( rhs, pos, neg) ,
589
+ SiteCounter :: Phys { site } => pos. push ( site) ,
590
+ SiteCounter :: NodeSumExpr { bcb } => {
591
+ for & from_bcb in & self . old . graph . predecessors [ bcb] {
592
+ let edge_counter = self . site_counter ( Site :: Edge { from_bcb, to_bcb : bcb } ) ;
593
+ self . push_resolved_sites ( edge_counter, pos, neg) ;
594
+ }
595
+ }
596
+ SiteCounter :: EdgeDiffExpr { from_bcb, to_bcb } => {
597
+ // First, add the count for `from_bcb`.
598
+ let node_counter = self . site_counter ( Site :: Node { bcb : from_bcb } ) ;
599
+ self . push_resolved_sites ( node_counter, pos, neg) ;
600
+
601
+ // Then subtract the counts for the other out-edges.
602
+ for target in sibling_out_edge_targets ( self . old . graph , from_bcb, to_bcb) {
603
+ let edge_counter = self . site_counter ( Site :: Edge { from_bcb, to_bcb : target } ) ;
583
604
// Swap `neg` and `pos` so that the counter is subtracted.
584
- Op :: Subtract => self . push_resolved_sites ( rhs , neg, pos) ,
605
+ self . push_resolved_sites ( edge_counter , neg, pos) ;
585
606
}
586
607
}
587
608
}
0 commit comments