@@ -63,12 +63,6 @@ pub(super) struct CoverageCounters {
6363
6464 /// Coverage counters/expressions that are associated with individual BCBs.
6565 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 > ,
7266
7367 /// Table of expression data, associating each expression ID with its
7468 /// corresponding operator (+ or -) and its LHS/RHS operands.
@@ -95,7 +89,6 @@ impl CoverageCounters {
9589 Self {
9690 counter_increment_sites : IndexVec :: new ( ) ,
9791 node_counters : IndexVec :: from_elem_n ( None , num_bcbs) ,
98- edge_counters : FxHashMap :: default ( ) ,
9992 expressions : IndexVec :: new ( ) ,
10093 expressions_memo : FxHashMap :: default ( ) ,
10194 }
@@ -191,20 +184,6 @@ impl CoverageCounters {
191184 counter
192185 }
193186
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-
208187 pub ( super ) fn term_for_bcb ( & self , bcb : BasicCoverageBlock ) -> Option < CovTerm > {
209188 self . node_counters [ bcb] . map ( |counter| counter. as_term ( ) )
210189 }
@@ -250,22 +229,53 @@ impl CoverageCounters {
250229 }
251230}
252231
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+
253266/// Helper struct that allows counter creation to inspect the BCB graph, and
254267/// the set of nodes that need counters.
255268struct CountersBuilder < ' a > {
256- counters : CoverageCounters ,
257269 graph : & ' a CoverageGraph ,
258270 bcb_needs_counter : & ' a BitSet < BasicCoverageBlock > ,
271+
272+ site_counters : FxHashMap < Site , SiteCounter > ,
259273}
260274
261275impl < ' a > CountersBuilder < ' a > {
262276 fn new ( graph : & ' a CoverageGraph , bcb_needs_counter : & ' a BitSet < BasicCoverageBlock > ) -> Self {
263277 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 ( ) }
269279 }
270280
271281 fn make_bcb_counters ( & mut self ) {
@@ -298,9 +308,7 @@ impl<'a> CountersBuilder<'a> {
298308 fn make_node_counter_and_out_edge_counters ( & mut self , from_bcb : BasicCoverageBlock ) {
299309 // First, ensure that this node has a counter of some kind.
300310 // 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) ;
304312
305313 // If this node's out-edges won't sum to the node's counter,
306314 // then there's no reason to create edge counters here.
@@ -311,11 +319,11 @@ impl<'a> CountersBuilder<'a> {
311319 // When choosing which out-edge should be given a counter expression, ignore edges that
312320 // already have counters, or could use the existing counter of their target node.
313321 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 } ) {
315323 return true ;
316324 }
317325 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 } )
319327 } ;
320328
321329 // Determine the set of out-edges that could benefit from being given an expression.
@@ -328,45 +336,41 @@ impl<'a> CountersBuilder<'a> {
328336
329337 // If there are out-edges without counters, choose one to be given an expression
330338 // (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)
332340 else {
333341 return ;
334342 } ;
335343
336344 // For each out-edge other than the one that was chosen to get an expression,
337345 // 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+ }
345349
346350 // Now create an expression for the chosen edge, by taking the counter
347351 // 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) ;
352354 }
353355
354356 #[ 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 {
356358 // 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 } ) {
358360 debug ! ( "{bcb:?} already has a counter: {counter:?}" ) ;
359361 return counter;
360362 }
361363
362364 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
364367 }
365368
366- fn make_node_counter_inner ( & mut self , bcb : BasicCoverageBlock ) -> BcbCounter {
369+ fn make_node_counter_inner ( & mut self , bcb : BasicCoverageBlock ) -> SiteCounter {
367370 // If the node's sole in-edge already has a counter, use that.
368371 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 } )
370374 {
371375 return edge_counter;
372376 }
@@ -380,20 +384,17 @@ impl<'a> CountersBuilder<'a> {
380384 // leading to infinite recursion.
381385 if predecessors. len ( ) <= 1 || predecessors. contains ( & bcb) {
382386 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 } } ;
384388 debug ! ( ?bcb, ?counter, "node gets a physical counter" ) ;
385389 return counter;
386390 }
387391
388392 // A BCB with multiple incoming edges can compute its count by ensuring that counters
389393 // 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 } ;
397398
398399 debug ! ( "{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}" ) ;
399400 sum_of_in_edges
@@ -404,22 +405,23 @@ impl<'a> CountersBuilder<'a> {
404405 & mut self ,
405406 from_bcb : BasicCoverageBlock ,
406407 to_bcb : BasicCoverageBlock ,
407- ) -> BcbCounter {
408+ ) -> SiteCounter {
408409 // 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 } ) {
410411 debug ! ( "Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}" ) ;
411412 return counter;
412413 }
413414
414415 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
416418 }
417419
418420 fn make_edge_counter_inner (
419421 & mut self ,
420422 from_bcb : BasicCoverageBlock ,
421423 to_bcb : BasicCoverageBlock ,
422- ) -> BcbCounter {
424+ ) -> SiteCounter {
423425 // If the target node has exactly one in-edge (i.e. this one), then just
424426 // use the node's counter, since it will have the same value.
425427 if let Some ( sole_pred) = self . graph . sole_predecessor ( to_bcb) {
@@ -437,7 +439,7 @@ impl<'a> CountersBuilder<'a> {
437439 }
438440
439441 // 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 } } ;
441443 debug ! ( ?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter" ) ;
442444 counter
443445 }
@@ -525,14 +527,14 @@ impl<'a> Transcriber<'a> {
525527 fn transcribe_counters ( mut self ) -> CoverageCounters {
526528 for bcb in self . old . bcb_needs_counter . iter ( ) {
527529 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 ) ;
529531
530- // Resolve the old counter into flat lists of nodes/edges whose
532+ // Resolve the site counter into flat lists of nodes/edges whose
531533 // physical counts contribute to the counter for this node.
532534 // Distinguish between counts that will be added vs subtracted.
533535 let mut pos = vec ! [ ] ;
534536 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) ;
536538
537539 // Simplify by cancelling out sites that appear on both sides.
538540 let ( mut pos, mut neg) = sort_and_cancel ( pos, neg) ;
@@ -566,22 +568,41 @@ impl<'a> Transcriber<'a> {
566568 self . new
567569 }
568570
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+
569581 fn ensure_phys_counter ( & mut self , site : Site ) -> BcbCounter {
570582 * self . phys_counter_for_site . entry ( site) . or_insert_with ( || self . new . make_phys_counter ( site) )
571583 }
572584
573585 /// Resolves the given counter into flat lists of nodes/edges, whose counters
574586 /// 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 > ) {
576588 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 } ) ;
583604 // 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) ;
585606 }
586607 }
587608 }
0 commit comments