11
11
use rustc:: dep_graph:: { DepGraphQuery , DepNode } ;
12
12
use rustc:: hir:: def_id:: DefId ;
13
13
use rustc_data_structures:: fx:: FxHashMap ;
14
- use rustc_data_structures:: graph:: { DepthFirstTraversal , INCOMING , NodeIndex } ;
14
+ use rustc_data_structures:: bitvec:: BitVector ;
15
+ use rustc_data_structures:: graph:: { NodeIndex , Graph } ;
15
16
16
17
use super :: hash:: * ;
17
18
use ich:: Fingerprint ;
@@ -33,11 +34,21 @@ pub struct Predecessors<'query> {
33
34
impl < ' q > Predecessors < ' q > {
34
35
pub fn new ( query : & ' q DepGraphQuery < DefId > , hcx : & mut HashContext ) -> Self {
35
36
// Find nodes for which we want to know the full set of preds
36
- let mut dfs = DepthFirstTraversal :: new ( & query. graph , INCOMING ) ;
37
- let all_nodes = query. graph . all_nodes ( ) ;
38
37
let tcx = hcx. tcx ;
38
+ let node_count = query. graph . len_nodes ( ) ;
39
39
40
- let inputs: FxHashMap < _ , _ > = all_nodes. iter ( )
40
+ // Set up some data structures the cache predecessor search needs:
41
+ let mut visit_counts: Vec < u32 > = Vec :: new ( ) ;
42
+ let mut node_cache: Vec < Option < Box < [ u32 ] > > > = Vec :: new ( ) ;
43
+ visit_counts. resize ( node_count, 0 ) ;
44
+ node_cache. resize ( node_count, None ) ;
45
+ let mut dfs_workspace1 = DfsWorkspace :: new ( node_count) ;
46
+ let mut dfs_workspace2 = DfsWorkspace :: new ( node_count) ;
47
+
48
+ let inputs: FxHashMap < _ , _ > = query
49
+ . graph
50
+ . all_nodes ( )
51
+ . iter ( )
41
52
. enumerate ( )
42
53
. filter ( |& ( _, node) | match node. data {
43
54
DepNode :: WorkProduct ( _) => true ,
@@ -51,11 +62,18 @@ impl<'q> Predecessors<'q> {
51
62
_ => false ,
52
63
} )
53
64
. map ( |( node_index, node) | {
54
- dfs. reset ( NodeIndex ( node_index) ) ;
55
- let inputs: Vec < _ > = dfs. by_ref ( )
56
- . map ( |i| & all_nodes[ i. node_id ( ) ] . data )
57
- . filter ( |d| HashContext :: is_hashable ( d) )
58
- . collect ( ) ;
65
+ find_roots ( & query. graph ,
66
+ node_index as u32 ,
67
+ & mut visit_counts,
68
+ & mut node_cache[ ..] ,
69
+ HashContext :: is_hashable,
70
+ & mut dfs_workspace1,
71
+ Some ( & mut dfs_workspace2) ) ;
72
+
73
+ let inputs: Vec < _ > = dfs_workspace1. output . nodes . iter ( ) . map ( |& i| {
74
+ query. graph . node_data ( NodeIndex ( i as usize ) )
75
+ } ) . collect ( ) ;
76
+
59
77
( & node. data , inputs)
60
78
} )
61
79
. collect ( ) ;
@@ -72,3 +90,273 @@ impl<'q> Predecessors<'q> {
72
90
}
73
91
}
74
92
}
93
+
94
+ const CACHING_THRESHOLD : u32 = 60 ;
95
+
96
+ // Starting at `start_node`, this function finds this node's "roots", that is,
97
+ // anything that is hashable, in the dep-graph. It uses a simple depth-first
98
+ // search to achieve that. However, since some sub-graphs are traversed over
99
+ // and over again, the function also some caching built into it: Each time it
100
+ // visits a node it increases a counter for that node. If a node has been
101
+ // visited more often than CACHING_THRESHOLD, the function will allocate a
102
+ // cache entry in the `cache` array. This cache entry contains a flat list of
103
+ // all roots reachable from the given node. The next time the node is visited,
104
+ // the search can just add the contents of this array to the output instead of
105
+ // recursing further.
106
+ //
107
+ // The function takes two `DfsWorkspace` arguments. These contains some data
108
+ // structures that would be expensive to re-allocate all the time, so they are
109
+ // allocated once up-front. There are two of them because building a cache entry
110
+ // requires a recursive invocation of this function. Two are enough though,
111
+ // since function never recurses more than once.
112
+ fn find_roots < T , F > ( graph : & Graph < T , ( ) > ,
113
+ start_node : u32 ,
114
+ visit_counts : & mut [ u32 ] ,
115
+ cache : & mut [ Option < Box < [ u32 ] > > ] ,
116
+ is_root : F ,
117
+ workspace : & mut DfsWorkspace ,
118
+ mut sub_workspace : Option < & mut DfsWorkspace > )
119
+ where F : Copy + Fn ( & T ) -> bool ,
120
+ T : :: std:: fmt:: Debug ,
121
+ {
122
+ workspace. visited . clear ( ) ;
123
+ workspace. output . clear ( ) ;
124
+ workspace. stack . clear ( ) ;
125
+ workspace. stack . push ( start_node) ;
126
+
127
+ loop {
128
+ let node = match workspace. stack . pop ( ) {
129
+ Some ( node) => node,
130
+ None => return ,
131
+ } ;
132
+
133
+ if !workspace. visited . insert ( node as usize ) {
134
+ continue
135
+ }
136
+
137
+ if is_root ( graph. node_data ( NodeIndex ( node as usize ) ) ) {
138
+ // If this is a root, just add it to the output.
139
+ workspace. output . insert ( node) ;
140
+ } else {
141
+ if let Some ( ref cached) = cache[ node as usize ] {
142
+ for & n in & cached[ ..] {
143
+ workspace. output . insert ( n) ;
144
+ }
145
+ // No need to recurse further from this node
146
+ continue
147
+ }
148
+
149
+ visit_counts[ node as usize ] += 1 ;
150
+
151
+ // If this node has been visited often enough to be cached ...
152
+ if visit_counts[ node as usize ] > CACHING_THRESHOLD {
153
+ // ... we are actually allowed to cache something, do so:
154
+ if let Some ( ref mut sub_workspace) = sub_workspace {
155
+ // Note that the following recursive invocation does never
156
+ // write to the cache (since we pass None as sub_workspace).
157
+ // This is intentional: The graph we are working with
158
+ // contains cycles and this prevent us from simply building
159
+ // our caches recursively on-demand.
160
+ // However, we can just do a regular, non-caching DFS to
161
+ // yield the set of roots and cache that.
162
+ find_roots ( graph,
163
+ node,
164
+ visit_counts,
165
+ cache,
166
+ is_root,
167
+ sub_workspace,
168
+ None ) ;
169
+
170
+ for & n in & sub_workspace. output . nodes {
171
+ workspace. output . insert ( n) ;
172
+ }
173
+
174
+ cache[ node as usize ] = Some ( sub_workspace. output
175
+ . nodes
176
+ . clone ( )
177
+ . into_boxed_slice ( ) ) ;
178
+ // No need to recurse further from this node
179
+ continue
180
+ }
181
+ }
182
+
183
+ for pred in graph. predecessor_nodes ( NodeIndex ( node as usize ) ) {
184
+ workspace. stack . push ( pred. node_id ( ) as u32 ) ;
185
+ }
186
+ }
187
+ }
188
+ }
189
+
190
+ struct DfsWorkspace {
191
+ stack : Vec < u32 > ,
192
+ visited : BitVector ,
193
+ output : NodeIndexSet ,
194
+ }
195
+
196
+ impl DfsWorkspace {
197
+ fn new ( total_node_count : usize ) -> DfsWorkspace {
198
+ DfsWorkspace {
199
+ stack : Vec :: new ( ) ,
200
+ visited : BitVector :: new ( total_node_count) ,
201
+ output : NodeIndexSet :: new ( total_node_count) ,
202
+ }
203
+ }
204
+ }
205
+
206
+ struct NodeIndexSet {
207
+ bitset : BitVector ,
208
+ nodes : Vec < u32 > ,
209
+ }
210
+
211
+ impl NodeIndexSet {
212
+ fn new ( total_node_count : usize ) -> NodeIndexSet {
213
+ NodeIndexSet {
214
+ bitset : BitVector :: new ( total_node_count) ,
215
+ nodes : Vec :: new ( ) ,
216
+ }
217
+ }
218
+
219
+ #[ inline]
220
+ fn clear ( & mut self ) {
221
+ self . bitset . clear ( ) ;
222
+ self . nodes . clear ( ) ;
223
+ }
224
+
225
+ #[ inline]
226
+ fn insert ( & mut self , node : u32 ) {
227
+ if self . bitset . insert ( node as usize ) {
228
+ self . nodes . push ( node)
229
+ }
230
+ }
231
+ }
232
+
233
+ #[ test]
234
+ fn test_cached_dfs_acyclic ( ) {
235
+
236
+ // 0 1 2
237
+ // | \ /
238
+ // 3---+ |
239
+ // | | |
240
+ // | | |
241
+ // 4 5 6
242
+ // \ / \ / \
243
+ // | | |
244
+ // 7 8 9
245
+
246
+ let mut g: Graph < bool , ( ) > = Graph :: new ( ) ;
247
+ g. add_node ( false ) ;
248
+ g. add_node ( false ) ;
249
+ g. add_node ( false ) ;
250
+ g. add_node ( false ) ;
251
+ g. add_node ( false ) ;
252
+ g. add_node ( false ) ;
253
+ g. add_node ( false ) ;
254
+ g. add_node ( true ) ;
255
+ g. add_node ( true ) ;
256
+ g. add_node ( true ) ;
257
+
258
+ g. add_edge ( NodeIndex ( 3 ) , NodeIndex ( 0 ) , ( ) ) ;
259
+ g. add_edge ( NodeIndex ( 4 ) , NodeIndex ( 3 ) , ( ) ) ;
260
+ g. add_edge ( NodeIndex ( 7 ) , NodeIndex ( 4 ) , ( ) ) ;
261
+ g. add_edge ( NodeIndex ( 5 ) , NodeIndex ( 3 ) , ( ) ) ;
262
+ g. add_edge ( NodeIndex ( 7 ) , NodeIndex ( 5 ) , ( ) ) ;
263
+ g. add_edge ( NodeIndex ( 8 ) , NodeIndex ( 5 ) , ( ) ) ;
264
+ g. add_edge ( NodeIndex ( 8 ) , NodeIndex ( 6 ) , ( ) ) ;
265
+ g. add_edge ( NodeIndex ( 9 ) , NodeIndex ( 6 ) , ( ) ) ;
266
+ g. add_edge ( NodeIndex ( 6 ) , NodeIndex ( 1 ) , ( ) ) ;
267
+ g. add_edge ( NodeIndex ( 6 ) , NodeIndex ( 2 ) , ( ) ) ;
268
+
269
+ let mut ws1 = DfsWorkspace :: new ( g. len_nodes ( ) ) ;
270
+ let mut ws2 = DfsWorkspace :: new ( g. len_nodes ( ) ) ;
271
+ let mut visit_counts: Vec < _ > = g. all_nodes ( ) . iter ( ) . map ( |_| 0u32 ) . collect ( ) ;
272
+ let mut cache: Vec < Option < Box < [ u32 ] > > > = g. all_nodes ( ) . iter ( ) . map ( |_| None ) . collect ( ) ;
273
+
274
+ fn is_root ( x : & bool ) -> bool { * x }
275
+
276
+ for _ in 0 .. CACHING_THRESHOLD + 1 {
277
+ find_roots ( & g, 5 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
278
+ ws1. output . nodes . sort ( ) ;
279
+ assert_eq ! ( ws1. output. nodes, vec![ 7 , 8 ] ) ;
280
+
281
+ find_roots ( & g, 6 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
282
+ ws1. output . nodes . sort ( ) ;
283
+ assert_eq ! ( ws1. output. nodes, vec![ 8 , 9 ] ) ;
284
+
285
+ find_roots ( & g, 0 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
286
+ ws1. output . nodes . sort ( ) ;
287
+ assert_eq ! ( ws1. output. nodes, vec![ 7 , 8 ] ) ;
288
+
289
+ find_roots ( & g, 1 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
290
+ ws1. output . nodes . sort ( ) ;
291
+ assert_eq ! ( ws1. output. nodes, vec![ 8 , 9 ] ) ;
292
+
293
+ find_roots ( & g, 2 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
294
+ ws1. output . nodes . sort ( ) ;
295
+ assert_eq ! ( ws1. output. nodes, vec![ 8 , 9 ] ) ;
296
+
297
+ find_roots ( & g, 3 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
298
+ ws1. output . nodes . sort ( ) ;
299
+ assert_eq ! ( ws1. output. nodes, vec![ 7 , 8 ] ) ;
300
+
301
+ find_roots ( & g, 4 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
302
+ ws1. output . nodes . sort ( ) ;
303
+ assert_eq ! ( ws1. output. nodes, vec![ 7 ] ) ;
304
+ }
305
+ }
306
+
307
+ #[ test]
308
+ fn test_cached_dfs_cyclic ( ) {
309
+
310
+ // 0 1 <---- 2 3
311
+ // ^ | ^ ^
312
+ // | v | |
313
+ // 4 ----> 5 ----> 6 ----> 7
314
+ // ^ ^ ^ ^
315
+ // | | | |
316
+ // 8 9 10 11
317
+
318
+
319
+ let mut g: Graph < bool , ( ) > = Graph :: new ( ) ;
320
+ g. add_node ( false ) ;
321
+ g. add_node ( false ) ;
322
+ g. add_node ( false ) ;
323
+ g. add_node ( false ) ;
324
+ g. add_node ( false ) ;
325
+ g. add_node ( false ) ;
326
+ g. add_node ( false ) ;
327
+ g. add_node ( false ) ;
328
+ g. add_node ( true ) ;
329
+ g. add_node ( true ) ;
330
+ g. add_node ( true ) ;
331
+ g. add_node ( true ) ;
332
+
333
+ g. add_edge ( NodeIndex ( 4 ) , NodeIndex ( 0 ) , ( ) ) ;
334
+ g. add_edge ( NodeIndex ( 8 ) , NodeIndex ( 4 ) , ( ) ) ;
335
+ g. add_edge ( NodeIndex ( 4 ) , NodeIndex ( 5 ) , ( ) ) ;
336
+ g. add_edge ( NodeIndex ( 1 ) , NodeIndex ( 5 ) , ( ) ) ;
337
+ g. add_edge ( NodeIndex ( 9 ) , NodeIndex ( 5 ) , ( ) ) ;
338
+ g. add_edge ( NodeIndex ( 5 ) , NodeIndex ( 6 ) , ( ) ) ;
339
+ g. add_edge ( NodeIndex ( 6 ) , NodeIndex ( 2 ) , ( ) ) ;
340
+ g. add_edge ( NodeIndex ( 2 ) , NodeIndex ( 1 ) , ( ) ) ;
341
+ g. add_edge ( NodeIndex ( 10 ) , NodeIndex ( 6 ) , ( ) ) ;
342
+ g. add_edge ( NodeIndex ( 6 ) , NodeIndex ( 7 ) , ( ) ) ;
343
+ g. add_edge ( NodeIndex ( 11 ) , NodeIndex ( 7 ) , ( ) ) ;
344
+ g. add_edge ( NodeIndex ( 7 ) , NodeIndex ( 3 ) , ( ) ) ;
345
+
346
+ let mut ws1 = DfsWorkspace :: new ( g. len_nodes ( ) ) ;
347
+ let mut ws2 = DfsWorkspace :: new ( g. len_nodes ( ) ) ;
348
+ let mut visit_counts: Vec < _ > = g. all_nodes ( ) . iter ( ) . map ( |_| 0u32 ) . collect ( ) ;
349
+ let mut cache: Vec < Option < Box < [ u32 ] > > > = g. all_nodes ( ) . iter ( ) . map ( |_| None ) . collect ( ) ;
350
+
351
+ fn is_root ( x : & bool ) -> bool { * x }
352
+
353
+ for _ in 0 .. CACHING_THRESHOLD + 1 {
354
+ find_roots ( & g, 2 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
355
+ ws1. output . nodes . sort ( ) ;
356
+ assert_eq ! ( ws1. output. nodes, vec![ 8 , 9 , 10 ] ) ;
357
+
358
+ find_roots ( & g, 3 , & mut visit_counts, & mut cache[ ..] , is_root, & mut ws1, Some ( & mut ws2) ) ;
359
+ ws1. output . nodes . sort ( ) ;
360
+ assert_eq ! ( ws1. output. nodes, vec![ 8 , 9 , 10 , 11 ] ) ;
361
+ }
362
+ }
0 commit comments