10
10
11
11
//! Code to save/load the dep-graph from files.
12
12
13
- use rustc:: dep_graph:: DepNode ;
13
+ use rustc:: dep_graph:: { DepNode , WorkProductId } ;
14
14
use rustc:: hir:: def_id:: DefId ;
15
15
use rustc:: hir:: svh:: Svh ;
16
16
use rustc:: session:: Session ;
@@ -19,6 +19,7 @@ use rustc_data_structures::fx::{FxHashSet, FxHashMap};
19
19
use rustc_serialize:: Decodable as RustcDecodable ;
20
20
use rustc_serialize:: opaque:: Decoder ;
21
21
use std:: path:: { Path } ;
22
+ use std:: sync:: Arc ;
22
23
23
24
use IncrementalHashesMap ;
24
25
use ich:: Fingerprint ;
@@ -30,7 +31,9 @@ use super::fs::*;
30
31
use super :: file_format;
31
32
use super :: work_product;
32
33
33
- pub type DirtyNodes = FxHashSet < DepNode < DefPathIndex > > ;
34
+ // The key is a dirty node. The value is **some** base-input that we
35
+ // can blame it on.
36
+ pub type DirtyNodes = FxHashMap < DepNode < DefPathIndex > , DepNode < DefPathIndex > > ;
34
37
35
38
/// If we are in incremental mode, and a previous dep-graph exists,
36
39
/// then load up those nodes/edges that are still valid into the
@@ -152,83 +155,65 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
152
155
// Retrace the paths in the directory to find their current location (if any).
153
156
let retraced = directory. retrace ( tcx) ;
154
157
155
- // Compute the set of Hir nodes whose data has changed or which
156
- // have been removed. These are "raw" source nodes, which means
157
- // that they still use the original `DefPathIndex` values from the
158
- // encoding, rather than having been retraced to a `DefId`. The
159
- // reason for this is that this way we can include nodes that have
160
- // been removed (which no longer have a `DefId` in the current
161
- // compilation).
162
- let dirty_raw_source_nodes = dirty_nodes ( tcx,
163
- incremental_hashes_map,
164
- & serialized_dep_graph. hashes ,
165
- & retraced) ;
166
-
167
- // Create a list of (raw-source-node ->
168
- // retracted-target-node) edges. In the process of retracing the
169
- // target nodes, we may discover some of them def-paths no longer exist,
170
- // in which case there is no need to mark the corresopnding nodes as dirty
171
- // (they are just not present). So this list may be smaller than the original.
172
- //
173
- // Note though that in the common case the target nodes are
174
- // `DepNode::WorkProduct` instances, and those don't have a
175
- // def-id, so they will never be considered to not exist. Instead,
176
- // we do a secondary hashing step (later, in trans) when we know
177
- // the set of symbols that go into a work-product: if any symbols
178
- // have been removed (or added) the hash will be different and
179
- // we'll ignore the work-product then.
180
- let retraced_edges: Vec < _ > =
181
- serialized_dep_graph. edges . iter ( )
182
- . filter_map ( |& ( ref raw_source_node, ref raw_target_node) | {
183
- retraced. map ( raw_target_node)
184
- . map ( |target_node| ( raw_source_node, target_node) )
185
- } )
186
- . collect ( ) ;
187
-
188
- // Compute which work-products have an input that has changed or
189
- // been removed. Put the dirty ones into a set.
190
- let mut dirty_target_nodes = FxHashSet ( ) ;
191
- for & ( raw_source_node, ref target_node) in & retraced_edges {
192
- if dirty_raw_source_nodes. contains ( raw_source_node) {
193
- if !dirty_target_nodes. contains ( target_node) {
194
- dirty_target_nodes. insert ( target_node. clone ( ) ) ;
195
-
158
+ // Compute the set of nodes from the old graph where some input
159
+ // has changed or been removed. These are "raw" source nodes,
160
+ // which means that they still use the original `DefPathIndex`
161
+ // values from the encoding, rather than having been retraced to a
162
+ // `DefId`. The reason for this is that this way we can include
163
+ // nodes that have been removed (which no longer have a `DefId` in
164
+ // the current compilation).
165
+ let dirty_raw_nodes = initial_dirty_nodes ( tcx,
166
+ incremental_hashes_map,
167
+ & serialized_dep_graph. hashes ,
168
+ & retraced) ;
169
+ let dirty_raw_nodes = transitive_dirty_nodes ( & serialized_dep_graph. edges , dirty_raw_nodes) ;
170
+
171
+ // Recreate the edges in the graph that are still clean.
172
+ let mut clean_work_products = FxHashSet ( ) ;
173
+ let mut dirty_work_products = FxHashSet ( ) ; // incomplete; just used to suppress debug output
174
+ for edge in & serialized_dep_graph. edges {
175
+ // If the target is dirty, skip the edge. If this is an edge
176
+ // that targets a work-product, we can print the blame
177
+ // information now.
178
+ if let Some ( blame) = dirty_raw_nodes. get ( & edge. 1 ) {
179
+ if let DepNode :: WorkProduct ( ref wp) = edge. 1 {
196
180
if tcx. sess . opts . debugging_opts . incremental_info {
197
- // It'd be nice to pretty-print these paths better than just
198
- // using the `Debug` impls, but wev.
199
- println ! ( "incremental: module {:?} is dirty because {:?} \
200
- changed or was removed",
201
- target_node,
202
- raw_source_node. map_def( |& index| {
203
- Some ( directory. def_path_string( tcx, index) )
204
- } ) . unwrap( ) ) ;
181
+ if dirty_work_products. insert ( wp. clone ( ) ) {
182
+ // It'd be nice to pretty-print these paths better than just
183
+ // using the `Debug` impls, but wev.
184
+ println ! ( "incremental: module {:?} is dirty because {:?} \
185
+ changed or was removed",
186
+ wp,
187
+ blame. map_def( |& index| {
188
+ Some ( directory. def_path_string( tcx, index) )
189
+ } ) . unwrap( ) ) ;
190
+ }
205
191
}
206
192
}
207
- }
208
- }
209
-
210
- // For work-products that are still clean, add their deps into the
211
- // graph. This is needed because later we will have to save this
212
- // back out again!
213
- let dep_graph = tcx. dep_graph . clone ( ) ;
214
- for ( raw_source_node, target_node) in retraced_edges {
215
- if dirty_target_nodes. contains ( & target_node) {
216
193
continue ;
217
194
}
218
195
219
- let source_node = retraced. map ( raw_source_node) . unwrap ( ) ;
220
-
221
- debug ! ( "decode_dep_graph: clean edge: {:?} -> {:?}" , source_node, target_node) ;
222
-
223
- let _task = dep_graph. in_task ( target_node) ;
224
- dep_graph. read ( source_node) ;
196
+ // If the source is dirty, the target will be dirty.
197
+ assert ! ( !dirty_raw_nodes. contains_key( & edge. 0 ) ) ;
198
+
199
+ // Retrace the source -> target edges to def-ids and then
200
+ // create an edge in the graph. Retracing may yield none if
201
+ // some of the data happens to have been removed; this ought
202
+ // to be impossible unless it is dirty, so we can unwrap.
203
+ let source_node = retraced. map ( & edge. 0 ) . unwrap ( ) ;
204
+ let target_node = retraced. map ( & edge. 1 ) . unwrap ( ) ;
205
+ let _task = tcx. dep_graph . in_task ( target_node) ;
206
+ tcx. dep_graph . read ( source_node) ;
207
+ if let DepNode :: WorkProduct ( ref wp) = edge. 1 {
208
+ clean_work_products. insert ( wp. clone ( ) ) ;
209
+ }
225
210
}
226
211
227
212
// Add in work-products that are still clean, and delete those that are
228
213
// dirty.
229
- reconcile_work_products ( tcx, work_products, & dirty_target_nodes ) ;
214
+ reconcile_work_products ( tcx, work_products, & clean_work_products ) ;
230
215
231
- dirty_clean:: check_dirty_clean_annotations ( tcx, & dirty_raw_source_nodes , & retraced) ;
216
+ dirty_clean:: check_dirty_clean_annotations ( tcx, & dirty_raw_nodes , & retraced) ;
232
217
233
218
load_prev_metadata_hashes ( tcx,
234
219
& retraced,
@@ -238,13 +223,13 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
238
223
239
224
/// Computes which of the original set of def-ids are dirty. Stored in
240
225
/// a bit vector where the index is the DefPathIndex.
241
- fn dirty_nodes < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
242
- incremental_hashes_map : & IncrementalHashesMap ,
243
- serialized_hashes : & [ SerializedHash ] ,
244
- retraced : & RetracedDefIdDirectory )
245
- -> DirtyNodes {
226
+ fn initial_dirty_nodes < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
227
+ incremental_hashes_map : & IncrementalHashesMap ,
228
+ serialized_hashes : & [ SerializedHash ] ,
229
+ retraced : & RetracedDefIdDirectory )
230
+ -> DirtyNodes {
246
231
let mut hcx = HashContext :: new ( tcx, incremental_hashes_map) ;
247
- let mut dirty_nodes = FxHashSet ( ) ;
232
+ let mut dirty_nodes = FxHashMap ( ) ;
248
233
249
234
for hash in serialized_hashes {
250
235
if let Some ( dep_node) = retraced. map ( & hash. dep_node ) {
@@ -277,21 +262,37 @@ fn dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
277
262
hash. dep_node) ;
278
263
}
279
264
280
- dirty_nodes. insert ( hash. dep_node . clone ( ) ) ;
265
+ dirty_nodes. insert ( hash. dep_node . clone ( ) , hash . dep_node . clone ( ) ) ;
281
266
}
282
267
283
268
dirty_nodes
284
269
}
285
270
271
+ fn transitive_dirty_nodes ( edges : & [ SerializedEdge ] ,
272
+ mut dirty_nodes : DirtyNodes )
273
+ -> DirtyNodes
274
+ {
275
+ let mut len = 0 ;
276
+ while len != dirty_nodes. len ( ) {
277
+ len = dirty_nodes. len ( ) ;
278
+ for edge in edges {
279
+ if let Some ( n) = dirty_nodes. get ( & edge. 0 ) . cloned ( ) {
280
+ dirty_nodes. insert ( edge. 1 . clone ( ) , n) ;
281
+ }
282
+ }
283
+ }
284
+ dirty_nodes
285
+ }
286
+
286
287
/// Go through the list of work-products produced in the previous run.
287
288
/// Delete any whose nodes have been found to be dirty or which are
288
289
/// otherwise no longer applicable.
289
290
fn reconcile_work_products < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
290
291
work_products : Vec < SerializedWorkProduct > ,
291
- dirty_target_nodes : & FxHashSet < DepNode < DefId > > ) {
292
+ clean_work_products : & FxHashSet < Arc < WorkProductId > > ) {
292
293
debug ! ( "reconcile_work_products({:?})" , work_products) ;
293
294
for swp in work_products {
294
- if dirty_target_nodes . contains ( & DepNode :: WorkProduct ( swp. id . clone ( ) ) ) {
295
+ if !clean_work_products . contains ( & swp. id ) {
295
296
debug ! ( "reconcile_work_products: dep-node for {:?} is dirty" , swp) ;
296
297
delete_dirty_work_product ( tcx, swp) ;
297
298
} else {
0 commit comments