Skip to content

Commit d848f1d

Browse files
committed
rewrite the predecessors code to create a reduced graph
The old code created a flat listing of "HIR -> WorkProduct" edges. While perfectly general, this could lead to a lot of repetition if the same HIR nodes affect many work-products. This is set to be a problem when we start to skip typeck, since we will be adding a lot more "work-product"-like nodes. The newer code uses an alternative strategy: it "reduces" the graph instead. Basically we walk the dep-graph and convert it to a DAG, where we only keep intermediate nodes if they are used by multiple work-products. This DAG does not contain the same set of nodes as the original graph, but it is guaranteed that (a) every output node is included in the graph and (b) the set of input nodes that can reach each output node is unchanged. (Input nodes are basically HIR nodes and foreign metadata; output nodes are nodes that have assocaited state which we will persist to disk in some way. These are assumed to be disjoint sets.)
1 parent 55f9712 commit d848f1d

File tree

14 files changed

+1095
-467
lines changed

14 files changed

+1095
-467
lines changed

src/librustc_incremental/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#![feature(staged_api)]
2424
#![feature(rand)]
2525
#![feature(core_intrinsics)]
26+
#![feature(conservative_impl_trait)]
27+
#![feature(field_init_shorthand)]
28+
#![feature(pub_restricted)]
2629

2730
extern crate graphviz;
2831
#[macro_use] extern crate rustc;

src/librustc_incremental/persist/dirty_clean.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
6767

6868
let _ignore = tcx.dep_graph.in_ignore();
6969
let dirty_inputs: FxHashSet<DepNode<DefId>> =
70-
dirty_inputs.iter()
71-
.filter_map(|d| retraced.map(d))
72-
.collect();
70+
dirty_inputs.keys()
71+
.filter_map(|d| retraced.map(d))
72+
.collect();
7373
let query = tcx.dep_graph.query();
7474
debug!("query-nodes: {:?}", query.nodes());
7575
let krate = tcx.hir.krate();

src/librustc_incremental/persist/load.rs

+78-77
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! Code to save/load the dep-graph from files.
1212
13-
use rustc::dep_graph::DepNode;
13+
use rustc::dep_graph::{DepNode, WorkProductId};
1414
use rustc::hir::def_id::DefId;
1515
use rustc::hir::svh::Svh;
1616
use rustc::session::Session;
@@ -19,6 +19,7 @@ use rustc_data_structures::fx::{FxHashSet, FxHashMap};
1919
use rustc_serialize::Decodable as RustcDecodable;
2020
use rustc_serialize::opaque::Decoder;
2121
use std::path::{Path};
22+
use std::sync::Arc;
2223

2324
use IncrementalHashesMap;
2425
use ich::Fingerprint;
@@ -30,7 +31,9 @@ use super::fs::*;
3031
use super::file_format;
3132
use super::work_product;
3233

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>>;
3437

3538
/// If we are in incremental mode, and a previous dep-graph exists,
3639
/// 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>,
152155
// Retrace the paths in the directory to find their current location (if any).
153156
let retraced = directory.retrace(tcx);
154157

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 {
196180
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+
}
205191
}
206192
}
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) {
216193
continue;
217194
}
218195

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+
}
225210
}
226211

227212
// Add in work-products that are still clean, and delete those that are
228213
// dirty.
229-
reconcile_work_products(tcx, work_products, &dirty_target_nodes);
214+
reconcile_work_products(tcx, work_products, &clean_work_products);
230215

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);
232217

233218
load_prev_metadata_hashes(tcx,
234219
&retraced,
@@ -238,13 +223,13 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
238223

239224
/// Computes which of the original set of def-ids are dirty. Stored in
240225
/// 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 {
246231
let mut hcx = HashContext::new(tcx, incremental_hashes_map);
247-
let mut dirty_nodes = FxHashSet();
232+
let mut dirty_nodes = FxHashMap();
248233

249234
for hash in serialized_hashes {
250235
if let Some(dep_node) = retraced.map(&hash.dep_node) {
@@ -277,21 +262,37 @@ fn dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
277262
hash.dep_node);
278263
}
279264

280-
dirty_nodes.insert(hash.dep_node.clone());
265+
dirty_nodes.insert(hash.dep_node.clone(), hash.dep_node.clone());
281266
}
282267

283268
dirty_nodes
284269
}
285270

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+
286287
/// Go through the list of work-products produced in the previous run.
287288
/// Delete any whose nodes have been found to be dirty or which are
288289
/// otherwise no longer applicable.
289290
fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
290291
work_products: Vec<SerializedWorkProduct>,
291-
dirty_target_nodes: &FxHashSet<DepNode<DefId>>) {
292+
clean_work_products: &FxHashSet<Arc<WorkProductId>>) {
292293
debug!("reconcile_work_products({:?})", work_products);
293294
for swp in work_products {
294-
if dirty_target_nodes.contains(&DepNode::WorkProduct(swp.id.clone())) {
295+
if !clean_work_products.contains(&swp.id) {
295296
debug!("reconcile_work_products: dep-node for {:?} is dirty", swp);
296297
delete_dirty_work_product(tcx, swp);
297298
} else {

0 commit comments

Comments
 (0)