Skip to content

Commit 2b38c4b

Browse files
committed
Extend DepGraph so it can track "work-products"
A work product right now is just a `.o` file. In the future it probably includes other kinds of files, such as `.bc` files saving the unoptimized LLVM IR. However, because WorkProductIds must be independent of DefIds, so that they don't need translation, this system may not be suitable *as is* for storing fine-grained information (such as the MIR for individual defs), as it was originally intended. We will want to refactor some for that.
1 parent cec262e commit 2b38c4b

File tree

3 files changed

+128
-9
lines changed

3 files changed

+128
-9
lines changed

src/librustc/dep_graph/dep_node.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use std::fmt::Debug;
12+
use std::sync::Arc;
1213

1314
macro_rules! try_opt {
1415
($e:expr) => (
@@ -45,6 +46,10 @@ pub enum DepNode<D: Clone + Debug> {
4546
// in an extern crate.
4647
MetaData(D),
4748

49+
// Represents some artifact that we save to disk. Note that these
50+
// do not have a def-id as part of their identifier.
51+
WorkProduct(Arc<WorkProductId>),
52+
4853
// Represents different phases in the compiler.
4954
CrateReader,
5055
CollectLanguageItems,
@@ -189,6 +194,11 @@ impl<D: Clone + Debug> DepNode<D> {
189194
TransCrate => Some(TransCrate),
190195
TransWriteMetadata => Some(TransWriteMetadata),
191196
LinkBinary => Some(LinkBinary),
197+
198+
// work product names do not need to be mapped, because
199+
// they are always absolute.
200+
WorkProduct(ref id) => Some(WorkProduct(id.clone())),
201+
192202
Hir(ref d) => op(d).map(Hir),
193203
MetaData(ref d) => op(d).map(MetaData),
194204
CollectItem(ref d) => op(d).map(CollectItem),
@@ -229,3 +239,14 @@ impl<D: Clone + Debug> DepNode<D> {
229239
}
230240
}
231241
}
242+
243+
/// A "work product" corresponds to a `.o` (or other) file that we
244+
/// save in between runs. These ids do not have a DefId but rather
245+
/// some independent path or string that persists between runs without
246+
/// the need to be mapped or unmapped. (This ensures we can serialize
247+
/// them even in the absence of a tcx.)
248+
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
249+
pub enum WorkProductId {
250+
PartitionObjectFile(String), // see (*TransPartition) below
251+
}
252+

src/librustc/dep_graph/graph.rs

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,64 @@
99
// except according to those terms.
1010

1111
use hir::def_id::DefId;
12+
use rustc_data_structures::fnv::FnvHashMap;
13+
use std::cell::{Ref, RefCell};
1214
use std::rc::Rc;
15+
use std::sync::Arc;
1316

14-
use super::dep_node::DepNode;
17+
use super::dep_node::{DepNode, WorkProductId};
1518
use super::query::DepGraphQuery;
1619
use super::raii;
1720
use super::thread::{DepGraphThreadData, DepMessage};
1821

1922
#[derive(Clone)]
2023
pub struct DepGraph {
21-
data: Rc<DepGraphThreadData>
24+
data: Rc<DepGraphData>
25+
}
26+
27+
struct DepGraphData {
28+
/// we send messages to the thread to let it build up the dep-graph
29+
/// from the current run
30+
thread: DepGraphThreadData,
31+
32+
/// when we load, there may be `.o` files, cached mir, or other such
33+
/// things available to us. If we find that they are not dirty, we
34+
/// load the path to the file storing those work-products here into
35+
/// this map. We can later look for and extract that data.
36+
previous_work_products: RefCell<FnvHashMap<Arc<WorkProductId>, WorkProduct>>,
37+
38+
/// work-products that we generate in this run
39+
work_products: RefCell<FnvHashMap<Arc<WorkProductId>, WorkProduct>>,
2240
}
2341

2442
impl DepGraph {
2543
pub fn new(enabled: bool) -> DepGraph {
2644
DepGraph {
27-
data: Rc::new(DepGraphThreadData::new(enabled))
45+
data: Rc::new(DepGraphData {
46+
thread: DepGraphThreadData::new(enabled),
47+
previous_work_products: RefCell::new(FnvHashMap()),
48+
work_products: RefCell::new(FnvHashMap())
49+
})
2850
}
2951
}
3052

3153
/// True if we are actually building a dep-graph. If this returns false,
3254
/// then the other methods on this `DepGraph` will have no net effect.
3355
#[inline]
3456
pub fn enabled(&self) -> bool {
35-
self.data.enabled()
57+
self.data.thread.enabled()
3658
}
3759

3860
pub fn query(&self) -> DepGraphQuery<DefId> {
39-
self.data.query()
61+
self.data.thread.query()
4062
}
4163

4264
pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> {
43-
raii::IgnoreTask::new(&self.data)
65+
raii::IgnoreTask::new(&self.data.thread)
4466
}
4567

4668
pub fn in_task<'graph>(&'graph self, key: DepNode<DefId>) -> raii::DepTask<'graph> {
47-
raii::DepTask::new(&self.data, key)
69+
raii::DepTask::new(&self.data.thread, key)
4870
}
4971

5072
pub fn with_ignore<OP,R>(&self, op: OP) -> R
@@ -62,10 +84,84 @@ impl DepGraph {
6284
}
6385

6486
pub fn read(&self, v: DepNode<DefId>) {
65-
self.data.enqueue(DepMessage::Read(v));
87+
self.data.thread.enqueue(DepMessage::Read(v));
6688
}
6789

6890
pub fn write(&self, v: DepNode<DefId>) {
69-
self.data.enqueue(DepMessage::Write(v));
91+
self.data.thread.enqueue(DepMessage::Write(v));
92+
}
93+
94+
/// Indicates that a previous work product exists for `v`. This is
95+
/// invoked during initial start-up based on what nodes are clean
96+
/// (and what files exist in the incr. directory).
97+
pub fn insert_previous_work_product(&self, v: &Arc<WorkProductId>, data: WorkProduct) {
98+
debug!("insert_previous_work_product({:?}, {:?})", v, data);
99+
self.data.previous_work_products.borrow_mut()
100+
.insert(v.clone(), data);
101+
}
102+
103+
/// Indicates that we created the given work-product in this run
104+
/// for `v`. This record will be preserved and loaded in the next
105+
/// run.
106+
pub fn insert_work_product(&self, v: &Arc<WorkProductId>, data: WorkProduct) {
107+
debug!("insert_work_product({:?}, {:?})", v, data);
108+
self.data.work_products.borrow_mut()
109+
.insert(v.clone(), data);
70110
}
111+
112+
/// Check whether a previous work product exists for `v` and, if
113+
/// so, return the path that leads to it. Used to skip doing work.
114+
pub fn previous_work_product(&self, v: &Arc<WorkProductId>) -> Option<WorkProduct> {
115+
self.data.previous_work_products.borrow()
116+
.get(v)
117+
.cloned()
118+
}
119+
120+
/// Access the map of work-products created during this run. Only
121+
/// used during saving of the dep-graph.
122+
pub fn work_products(&self) -> Ref<FnvHashMap<Arc<WorkProductId>, WorkProduct>> {
123+
self.data.work_products.borrow()
124+
}
125+
}
126+
127+
/// A "work product" is an intermediate result that we save into the
128+
/// incremental directory for later re-use. The primary example are
129+
/// the object files that we save for each partition at code
130+
/// generation time.
131+
///
132+
/// Each work product is associated with a dep-node, representing the
133+
/// process that produced the work-product. If that dep-node is found
134+
/// to be dirty when we load up, then we will delete the work-product
135+
/// at load time. If the work-product is found to be clean, the we
136+
/// will keep a record in the `previous_work_products` list.
137+
///
138+
/// In addition, work products have an associated hash. This hash is
139+
/// an extra hash that can be used to decide if the work-product from
140+
/// a previous compilation can be re-used (in addition to the dirty
141+
/// edges check).
142+
///
143+
/// As the primary example, consider the object files we generate for
144+
/// each partition. In the first run, we create partitions based on
145+
/// the symbols that need to be compiled. For each partition P, we
146+
/// hash the symbols in P and create a `WorkProduct` record associated
147+
/// with `DepNode::TransPartition(P)`; the hash is the set of symbols
148+
/// in P.
149+
///
150+
/// The next time we compile, if the `DepNode::TransPartition(P)` is
151+
/// judged to be clean (which means none of the things we read to
152+
/// generate the partition were found to be dirty), it will be loaded
153+
/// into previous work products. We will then regenerate the set of
154+
/// symbols in the partition P and hash them (note that new symbols
155+
/// may be added -- for example, new monomorphizations -- even if
156+
/// nothing in P changed!). We will compare that hash against the
157+
/// previous hash. If it matches up, we can reuse the object file.
158+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
159+
pub struct WorkProduct {
160+
/// extra hash used to decide if work-product is still suitable;
161+
/// note that this is *not* a hash of the work-product itself.
162+
/// See documentation on `WorkProduct` type for an example.
163+
pub input_hash: u64,
164+
165+
/// filename storing this work-product (found in the incr. comp. directory)
166+
pub file_name: String,
71167
}

src/librustc/dep_graph/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ mod visit;
2020

2121
pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
2222
pub use self::dep_node::DepNode;
23+
pub use self::dep_node::WorkProductId;
2324
pub use self::graph::DepGraph;
25+
pub use self::graph::WorkProduct;
2426
pub use self::query::DepGraphQuery;
2527
pub use self::visit::visit_all_items_in_krate;
2628
pub use self::raii::DepTask;

0 commit comments

Comments
 (0)