@@ -20,6 +20,96 @@ mod iter_nodes;
2020mod node_flow;
2121mod union_find;
2222
23+ /// Ensures that each BCB node needing a counter has one, by creating physical
24+ /// counters or counter expressions for nodes as required.
25+ pub ( super ) fn make_bcb_counters (
26+ graph : & CoverageGraph ,
27+ bcb_needs_counter : & DenseBitSet < BasicCoverageBlock > ,
28+ ) -> CoverageCounters {
29+ let balanced_graph = BalancedFlowGraph :: for_graph ( graph, |n| !graph[ n] . is_out_summable ) ;
30+ let merged_graph = MergedNodeFlowGraph :: for_balanced_graph ( & balanced_graph) ;
31+
32+ let nodes = make_node_counter_priority_list ( graph, balanced_graph) ;
33+ let node_counters = merged_graph. make_node_counters ( & nodes) ;
34+
35+ transcribe_counters ( & node_counters, bcb_needs_counter)
36+ }
37+
38+ fn make_node_counter_priority_list (
39+ graph : & CoverageGraph ,
40+ balanced_graph : BalancedFlowGraph < & CoverageGraph > ,
41+ ) -> Vec < BasicCoverageBlock > {
42+ // A "reloop" node has exactly one out-edge, which jumps back to the top
43+ // of an enclosing loop. Reloop nodes are typically visited more times
44+ // than loop-exit nodes, so try to avoid giving them physical counters.
45+ let is_reloop_node = IndexVec :: from_fn_n (
46+ |node| match graph. successors [ node] . as_slice ( ) {
47+ & [ succ] => graph. dominates ( succ, node) ,
48+ _ => false ,
49+ } ,
50+ graph. num_nodes ( ) ,
51+ ) ;
52+
53+ let mut nodes = balanced_graph. iter_nodes ( ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
54+ // The first node is the sink, which must not get a physical counter.
55+ assert_eq ! ( nodes[ 0 ] , balanced_graph. sink) ;
56+ // Sort the real nodes, such that earlier (lesser) nodes take priority
57+ // in being given a counter expression instead of a physical counter.
58+ nodes[ 1 ..] . sort_by ( |& a, & b| {
59+ // Start with a dummy `Equal` to make the actual tests line up nicely.
60+ Ordering :: Equal
61+ // Prefer a physical counter for return/yield nodes.
62+ . then_with ( || Ord :: cmp ( & graph[ a] . is_out_summable , & graph[ b] . is_out_summable ) )
63+ // Prefer an expression for reloop nodes (see definition above).
64+ . then_with ( || Ord :: cmp ( & is_reloop_node[ a] , & is_reloop_node[ b] ) . reverse ( ) )
65+ // Otherwise, prefer a physical counter for dominating nodes.
66+ . then_with ( || graph. cmp_in_dominator_order ( a, b) . reverse ( ) )
67+ } ) ;
68+ nodes
69+ }
70+
71+ fn transcribe_counters (
72+ old : & NodeCounters < BasicCoverageBlock > ,
73+ bcb_needs_counter : & DenseBitSet < BasicCoverageBlock > ,
74+ ) -> CoverageCounters {
75+ let mut new = CoverageCounters :: with_num_bcbs ( bcb_needs_counter. domain_size ( ) ) ;
76+
77+ for bcb in bcb_needs_counter. iter ( ) {
78+ let ( mut pos, mut neg) : ( Vec < _ > , Vec < _ > ) =
79+ old. counter_expr ( bcb) . iter ( ) . partition_map ( |& CounterTerm { node, op } | match op {
80+ Op :: Add => Either :: Left ( node) ,
81+ Op :: Subtract => Either :: Right ( node) ,
82+ } ) ;
83+
84+ if pos. is_empty ( ) {
85+ // If we somehow end up with no positive terms, fall back to
86+ // creating a physical counter. There's no known way for this
87+ // to happen, but we can avoid an ICE if it does.
88+ debug_assert ! ( false , "{bcb:?} has no positive counter terms" ) ;
89+ pos = vec ! [ bcb] ;
90+ neg = vec ! [ ] ;
91+ }
92+
93+ pos. sort ( ) ;
94+ neg. sort ( ) ;
95+
96+ let mut new_counters_for_sites = |sites : Vec < BasicCoverageBlock > | {
97+ sites. into_iter ( ) . map ( |node| new. ensure_phys_counter ( node) ) . collect :: < Vec < _ > > ( )
98+ } ;
99+ let mut pos = new_counters_for_sites ( pos) ;
100+ let mut neg = new_counters_for_sites ( neg) ;
101+
102+ pos. sort ( ) ;
103+ neg. sort ( ) ;
104+
105+ let pos_counter = new. make_sum ( & pos) . expect ( "`pos` should not be empty" ) ;
106+ let new_counter = new. make_subtracted_sum ( pos_counter, & neg) ;
107+ new. set_node_counter ( bcb, new_counter) ;
108+ }
109+
110+ new
111+ }
112+
23113/// The coverage counter or counter expression associated with a particular
24114/// BCB node or BCB edge.
25115#[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
@@ -73,46 +163,6 @@ pub(super) struct CoverageCounters {
73163}
74164
75165impl CoverageCounters {
76- /// Ensures that each BCB node needing a counter has one, by creating physical
77- /// counters or counter expressions for nodes and edges as required.
78- pub ( super ) fn make_bcb_counters (
79- graph : & CoverageGraph ,
80- bcb_needs_counter : & DenseBitSet < BasicCoverageBlock > ,
81- ) -> Self {
82- let balanced_graph = BalancedFlowGraph :: for_graph ( graph, |n| !graph[ n] . is_out_summable ) ;
83- let merged_graph = MergedNodeFlowGraph :: for_balanced_graph ( & balanced_graph) ;
84-
85- // A "reloop" node has exactly one out-edge, which jumps back to the top
86- // of an enclosing loop. Reloop nodes are typically visited more times
87- // than loop-exit nodes, so try to avoid giving them physical counters.
88- let is_reloop_node = IndexVec :: from_fn_n (
89- |node| match graph. successors [ node] . as_slice ( ) {
90- & [ succ] => graph. dominates ( succ, node) ,
91- _ => false ,
92- } ,
93- graph. num_nodes ( ) ,
94- ) ;
95-
96- let mut nodes = balanced_graph. iter_nodes ( ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
97- // The first node is the sink, which must not get a physical counter.
98- assert_eq ! ( nodes[ 0 ] , balanced_graph. sink) ;
99- // Sort the real nodes, such that earlier (lesser) nodes take priority
100- // in being given a counter expression instead of a physical counter.
101- nodes[ 1 ..] . sort_by ( |& a, & b| {
102- // Start with a dummy `Equal` to make the actual tests line up nicely.
103- Ordering :: Equal
104- // Prefer a physical counter for return/yield nodes.
105- . then_with ( || Ord :: cmp ( & graph[ a] . is_out_summable , & graph[ b] . is_out_summable ) )
106- // Prefer an expression for reloop nodes (see definition above).
107- . then_with ( || Ord :: cmp ( & is_reloop_node[ a] , & is_reloop_node[ b] ) . reverse ( ) )
108- // Otherwise, prefer a physical counter for dominating nodes.
109- . then_with ( || graph. cmp_in_dominator_order ( a, b) . reverse ( ) )
110- } ) ;
111- let node_counters = merged_graph. make_node_counters ( & nodes) ;
112-
113- Transcriber :: new ( graph. num_nodes ( ) , node_counters) . transcribe_counters ( bcb_needs_counter)
114- }
115-
116166 fn with_num_bcbs ( num_bcbs : usize ) -> Self {
117167 Self {
118168 phys_counter_for_node : FxIndexMap :: default ( ) ,
@@ -216,56 +266,3 @@ impl CoverageCounters {
216266 expressions
217267 }
218268}
219-
220- struct Transcriber {
221- old : NodeCounters < BasicCoverageBlock > ,
222- new : CoverageCounters ,
223- }
224-
225- impl Transcriber {
226- fn new ( num_nodes : usize , old : NodeCounters < BasicCoverageBlock > ) -> Self {
227- Self { old, new : CoverageCounters :: with_num_bcbs ( num_nodes) }
228- }
229-
230- fn transcribe_counters (
231- mut self ,
232- bcb_needs_counter : & DenseBitSet < BasicCoverageBlock > ,
233- ) -> CoverageCounters {
234- for bcb in bcb_needs_counter. iter ( ) {
235- let ( mut pos, mut neg) : ( Vec < _ > , Vec < _ > ) =
236- self . old . counter_expr ( bcb) . iter ( ) . partition_map (
237- |& CounterTerm { node, op } | match op {
238- Op :: Add => Either :: Left ( node) ,
239- Op :: Subtract => Either :: Right ( node) ,
240- } ,
241- ) ;
242-
243- if pos. is_empty ( ) {
244- // If we somehow end up with no positive terms, fall back to
245- // creating a physical counter. There's no known way for this
246- // to happen, but we can avoid an ICE if it does.
247- debug_assert ! ( false , "{bcb:?} has no positive counter terms" ) ;
248- pos = vec ! [ bcb] ;
249- neg = vec ! [ ] ;
250- }
251-
252- pos. sort ( ) ;
253- neg. sort ( ) ;
254-
255- let mut new_counters_for_sites = |sites : Vec < BasicCoverageBlock > | {
256- sites. into_iter ( ) . map ( |node| self . new . ensure_phys_counter ( node) ) . collect :: < Vec < _ > > ( )
257- } ;
258- let mut pos = new_counters_for_sites ( pos) ;
259- let mut neg = new_counters_for_sites ( neg) ;
260-
261- pos. sort ( ) ;
262- neg. sort ( ) ;
263-
264- let pos_counter = self . new . make_sum ( & pos) . expect ( "`pos` should not be empty" ) ;
265- let new_counter = self . new . make_subtracted_sum ( pos_counter, & neg) ;
266- self . new . set_node_counter ( bcb, new_counter) ;
267- }
268-
269- self . new
270- }
271- }
0 commit comments