8
8
9
9
use rustc_data_structures:: graph;
10
10
use rustc_index:: bit_set:: DenseBitSet ;
11
- use rustc_index:: { Idx , IndexVec } ;
11
+ use rustc_index:: { Idx , IndexSlice , IndexVec } ;
12
12
use rustc_middle:: mir:: coverage:: Op ;
13
13
14
14
use crate :: coverage:: counters:: iter_nodes:: IterNodes ;
@@ -17,8 +17,8 @@ use crate::coverage::counters::union_find::UnionFind;
17
17
#[ cfg( test) ]
18
18
mod tests;
19
19
20
- /// View of some underlying graph, in which each node's successors have been
21
- /// merged into a single "supernode".
20
+ /// Data representing a view of some underlying graph, in which each node's
21
+ /// successors have been merged into a single "supernode".
22
22
///
23
23
/// The resulting supernodes have no obvious meaning on their own.
24
24
/// However, merging successor nodes means that a node's out-edges can all
@@ -29,7 +29,7 @@ mod tests;
29
29
/// in the merged graph, it becomes possible to analyze the original node flows
30
30
/// using techniques for analyzing edge flows.
31
31
#[ derive( Debug ) ]
32
- pub ( crate ) struct MergedNodeFlowGraph < Node : Idx > {
32
+ pub ( crate ) struct NodeFlowData < Node : Idx > {
33
33
/// Maps each node to the supernode that contains it, indicated by some
34
34
/// arbitrary "root" node that is part of that supernode.
35
35
supernodes : IndexVec < Node , Node > ,
@@ -41,66 +41,59 @@ pub(crate) struct MergedNodeFlowGraph<Node: Idx> {
41
41
succ_supernodes : IndexVec < Node , Node > ,
42
42
}
43
43
44
- impl < Node : Idx > MergedNodeFlowGraph < Node > {
45
- /// Creates a "merged" view of an underlying graph.
46
- ///
47
- /// The given graph is assumed to have [“balanced flow”](balanced-flow),
48
- /// though it does not necessarily have to be a `BalancedFlowGraph`.
49
- ///
50
- /// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`.
51
- pub ( crate ) fn for_balanced_graph < G > ( graph : G ) -> Self
52
- where
53
- G : graph:: DirectedGraph < Node = Node > + graph:: Successors ,
54
- {
55
- let mut supernodes = UnionFind :: < G :: Node > :: new ( graph. num_nodes ( ) ) ;
56
-
57
- // For each node, merge its successors into a single supernode, and
58
- // arbitrarily choose one of those successors to represent all of them.
59
- let successors = graph
60
- . iter_nodes ( )
61
- . map ( |node| {
62
- graph
63
- . successors ( node)
64
- . reduce ( |a, b| supernodes. unify ( a, b) )
65
- . expect ( "each node in a balanced graph must have at least one out-edge" )
66
- } )
67
- . collect :: < IndexVec < G :: Node , G :: Node > > ( ) ;
68
-
69
- // Now that unification is complete, take a snapshot of the supernode forest,
70
- // and resolve each arbitrarily-chosen successor to its canonical root.
71
- // (This avoids having to explicitly resolve them later.)
72
- let supernodes = supernodes. snapshot ( ) ;
73
- let succ_supernodes = successors. into_iter ( ) . map ( |succ| supernodes[ succ] ) . collect ( ) ;
74
-
75
- Self { supernodes, succ_supernodes }
76
- }
77
-
78
- fn num_nodes ( & self ) -> usize {
79
- self . succ_supernodes . len ( )
80
- }
44
+ /// Creates a "merged" view of an underlying graph.
45
+ ///
46
+ /// The given graph is assumed to have [“balanced flow”](balanced-flow),
47
+ /// though it does not necessarily have to be a `BalancedFlowGraph`.
48
+ ///
49
+ /// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`.
50
+ pub ( crate ) fn node_flow_data_for_balanced_graph < G > ( graph : G ) -> NodeFlowData < G :: Node >
51
+ where
52
+ G : graph:: Successors ,
53
+ {
54
+ let mut supernodes = UnionFind :: < G :: Node > :: new ( graph. num_nodes ( ) ) ;
55
+
56
+ // For each node, merge its successors into a single supernode, and
57
+ // arbitrarily choose one of those successors to represent all of them.
58
+ let successors = graph
59
+ . iter_nodes ( )
60
+ . map ( |node| {
61
+ graph
62
+ . successors ( node)
63
+ . reduce ( |a, b| supernodes. unify ( a, b) )
64
+ . expect ( "each node in a balanced graph must have at least one out-edge" )
65
+ } )
66
+ . collect :: < IndexVec < G :: Node , G :: Node > > ( ) ;
67
+
68
+ // Now that unification is complete, take a snapshot of the supernode forest,
69
+ // and resolve each arbitrarily-chosen successor to its canonical root.
70
+ // (This avoids having to explicitly resolve them later.)
71
+ let supernodes = supernodes. snapshot ( ) ;
72
+ let succ_supernodes = successors. into_iter ( ) . map ( |succ| supernodes[ succ] ) . collect ( ) ;
73
+
74
+ NodeFlowData { supernodes, succ_supernodes }
75
+ }
81
76
82
- fn is_supernode ( & self , node : Node ) -> bool {
83
- self . supernodes [ node] == node
77
+ /// Uses the graph information in `node_flow_data`, together with a given
78
+ /// permutation of all nodes in the graph, to create physical counters and
79
+ /// counter expressions for each node in the underlying graph.
80
+ ///
81
+ /// The given list must contain exactly one copy of each node in the
82
+ /// underlying balanced-flow graph. The order of nodes is used as a hint to
83
+ /// influence counter allocation:
84
+ /// - Earlier nodes are more likely to receive counter expressions.
85
+ /// - Later nodes are more likely to receive physical counters.
86
+ pub ( crate ) fn make_node_counters < Node : Idx > (
87
+ node_flow_data : & NodeFlowData < Node > ,
88
+ priority_list : & [ Node ] ,
89
+ ) -> NodeCounters < Node > {
90
+ let mut builder = SpantreeBuilder :: new ( node_flow_data) ;
91
+
92
+ for & node in priority_list {
93
+ builder. visit_node ( node) ;
84
94
}
85
95
86
- /// Using the information in this merged graph, together with a given
87
- /// permutation of all nodes in the graph, to create physical counters and
88
- /// counter expressions for each node in the underlying graph.
89
- ///
90
- /// The given list must contain exactly one copy of each node in the
91
- /// underlying balanced-flow graph. The order of nodes is used as a hint to
92
- /// influence counter allocation:
93
- /// - Earlier nodes are more likely to receive counter expressions.
94
- /// - Later nodes are more likely to receive physical counters.
95
- pub ( crate ) fn make_node_counters ( & self , all_nodes_permutation : & [ Node ] ) -> NodeCounters < Node > {
96
- let mut builder = SpantreeBuilder :: new ( self ) ;
97
-
98
- for & node in all_nodes_permutation {
99
- builder. visit_node ( node) ;
100
- }
101
-
102
- NodeCounters { counter_terms : builder. finish ( ) }
103
- }
96
+ NodeCounters { counter_terms : builder. finish ( ) }
104
97
}
105
98
106
99
/// End result of allocating physical counters and counter expressions for the
@@ -141,7 +134,9 @@ pub(crate) struct CounterTerm<Node> {
141
134
142
135
#[ derive( Debug ) ]
143
136
struct SpantreeBuilder < ' a , Node : Idx > {
144
- graph : & ' a MergedNodeFlowGraph < Node > ,
137
+ supernodes : & ' a IndexSlice < Node , Node > ,
138
+ succ_supernodes : & ' a IndexSlice < Node , Node > ,
139
+
145
140
is_unvisited : DenseBitSet < Node > ,
146
141
/// Links supernodes to each other, gradually forming a spanning tree of
147
142
/// the merged-flow graph.
@@ -157,22 +152,28 @@ struct SpantreeBuilder<'a, Node: Idx> {
157
152
}
158
153
159
154
impl < ' a , Node : Idx > SpantreeBuilder < ' a , Node > {
160
- fn new ( graph : & ' a MergedNodeFlowGraph < Node > ) -> Self {
161
- let num_nodes = graph. num_nodes ( ) ;
155
+ fn new ( node_flow_data : & ' a NodeFlowData < Node > ) -> Self {
156
+ let NodeFlowData { supernodes, succ_supernodes } = node_flow_data;
157
+ let num_nodes = supernodes. len ( ) ;
162
158
Self {
163
- graph,
159
+ supernodes,
160
+ succ_supernodes,
164
161
is_unvisited : DenseBitSet :: new_filled ( num_nodes) ,
165
162
span_edges : IndexVec :: from_fn_n ( |_| None , num_nodes) ,
166
163
yank_buffer : vec ! [ ] ,
167
164
counter_terms : IndexVec :: from_fn_n ( |_| vec ! [ ] , num_nodes) ,
168
165
}
169
166
}
170
167
168
+ fn is_supernode ( & self , node : Node ) -> bool {
169
+ self . supernodes [ node] == node
170
+ }
171
+
171
172
/// Given a supernode, finds the supernode that is the "root" of its
172
173
/// spantree component. Two nodes that have the same spantree root are
173
174
/// connected in the spantree.
174
175
fn spantree_root ( & self , this : Node ) -> Node {
175
- debug_assert ! ( self . graph . is_supernode( this) ) ;
176
+ debug_assert ! ( self . is_supernode( this) ) ;
176
177
177
178
match self . span_edges [ this] {
178
179
None => this,
@@ -183,7 +184,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
183
184
/// Rotates edges in the spantree so that `this` is the root of its
184
185
/// spantree component.
185
186
fn yank_to_spantree_root ( & mut self , this : Node ) {
186
- debug_assert ! ( self . graph . is_supernode( this) ) ;
187
+ debug_assert ! ( self . is_supernode( this) ) ;
187
188
188
189
// The rotation is done iteratively, by first traversing from `this` to
189
190
// its root and storing the path in a buffer, and then traversing the
@@ -225,12 +226,12 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
225
226
226
227
// Get the supernode containing `this`, and make it the root of its
227
228
// component of the spantree.
228
- let this_supernode = self . graph . supernodes [ this] ;
229
+ let this_supernode = self . supernodes [ this] ;
229
230
self . yank_to_spantree_root ( this_supernode) ;
230
231
231
232
// Get the supernode containing all of this's successors.
232
- let succ_supernode = self . graph . succ_supernodes [ this] ;
233
- debug_assert ! ( self . graph . is_supernode( succ_supernode) ) ;
233
+ let succ_supernode = self . succ_supernodes [ this] ;
234
+ debug_assert ! ( self . is_supernode( succ_supernode) ) ;
234
235
235
236
// If two supernodes are already connected in the spantree, they will
236
237
// have the same spantree root. (Each supernode is connected to itself.)
@@ -279,18 +280,19 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
279
280
/// Asserts that all nodes have been visited, and returns the computed
280
281
/// counter expressions (made up of physical counters) for each node.
281
282
fn finish ( self ) -> IndexVec < Node , Vec < CounterTerm < Node > > > {
282
- let Self { graph , is_unvisited , span_edges, yank_buffer : _ , counter_terms } = self ;
283
+ let Self { ref span_edges, ref is_unvisited , ref counter_terms, .. } = self ;
283
284
assert ! ( is_unvisited. is_empty( ) , "some nodes were never visited: {is_unvisited:?}" ) ;
284
285
debug_assert ! (
285
286
span_edges
286
287
. iter_enumerated( )
287
- . all( |( node, span_edge) | { span_edge. is_some( ) <= graph . is_supernode( node) } ) ,
288
+ . all( |( node, span_edge) | { span_edge. is_some( ) <= self . is_supernode( node) } ) ,
288
289
"only supernodes can have a span edge" ,
289
290
) ;
290
291
debug_assert ! (
291
292
counter_terms. iter( ) . all( |terms| !terms. is_empty( ) ) ,
292
293
"after visiting all nodes, every node should have at least one term" ,
293
294
) ;
294
- counter_terms
295
+
296
+ self . counter_terms
295
297
}
296
298
}
0 commit comments