]>
git.proxmox.com Git - rustc.git/blob - src/librustc_incremental/persist/load.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Code to save/load the dep-graph from files.
13 use calculate_svh
::SvhCalculate
;
15 use rbml
::opaque
::Decoder
;
16 use rustc
::dep_graph
::DepNode
;
17 use rustc
::hir
::def_id
::DefId
;
19 use rustc_data_structures
::fnv
::FnvHashSet
;
20 use rustc_serialize
::Decodable
as RustcDecodable
;
26 use super::directory
::*;
27 use super::dirty_clean
;
30 type DirtyNodes
= FnvHashSet
<DepNode
<DefId
>>;
32 type CleanEdges
= Vec
<(DepNode
<DefId
>, DepNode
<DefId
>)>;
34 /// If we are in incremental mode, and a previous dep-graph exists,
35 /// then load up those nodes/edges that are still valid into the
36 /// dep-graph for this session. (This is assumed to be running very
37 /// early in compilation, before we've really done any work, but
38 /// actually it doesn't matter all that much.) See `README.md` for
39 /// more general overview.
40 pub fn load_dep_graph
<'tcx
>(tcx
: &ty
::TyCtxt
<'tcx
>) {
41 let _ignore
= tcx
.dep_graph
.in_ignore();
43 if let Some(dep_graph
) = dep_graph_path(tcx
) {
44 // FIXME(#32754) lock file?
45 load_dep_graph_if_exists(tcx
, &dep_graph
);
46 dirty_clean
::check_dirty_clean_annotations(tcx
);
50 pub fn load_dep_graph_if_exists
<'tcx
>(tcx
: &ty
::TyCtxt
<'tcx
>, path
: &Path
) {
55 let mut data
= vec
![];
58 .and_then(|mut file
| file
.read_to_end(&mut data
))
63 &format
!("could not load dep-graph from `{}`: {}",
64 path
.display(), err
));
69 match decode_dep_graph(tcx
, &data
) {
72 bug
!("decoding error in dep-graph from `{}`: {}", path
.display(), err
);
77 pub fn decode_dep_graph
<'tcx
>(tcx
: &ty
::TyCtxt
<'tcx
>, data
: &[u8])
80 // Deserialize the directory and dep-graph.
81 let mut decoder
= Decoder
::new(data
, 0);
82 let directory
= try
!(DefIdDirectory
::decode(&mut decoder
));
83 let serialized_dep_graph
= try
!(SerializedDepGraph
::decode(&mut decoder
));
85 debug
!("decode_dep_graph: directory = {:#?}", directory
);
86 debug
!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph
);
88 // Retrace the paths in the directory to find their current location (if any).
89 let retraced
= directory
.retrace(tcx
);
91 debug
!("decode_dep_graph: retraced = {:#?}", retraced
);
93 // Compute the set of Hir nodes whose data has changed.
95 initial_dirty_nodes(tcx
, &serialized_dep_graph
.hashes
, &retraced
);
97 debug
!("decode_dep_graph: initial dirty_nodes = {:#?}", dirty_nodes
);
99 // Find all DepNodes reachable from that core set. This loop
100 // iterates repeatedly over the list of edges whose source is not
101 // known to be dirty (`clean_edges`). If it finds an edge whose
102 // source is dirty, it removes it from that list and adds the
103 // target to `dirty_nodes`. It stops when it reaches a fixed
105 let clean_edges
= compute_clean_edges(&serialized_dep_graph
.edges
,
109 // Add synthetic `foo->foo` edges for each clean node `foo` that
110 // we had before. This is sort of a hack to create clean nodes in
111 // the graph, since the existence of a node is a signal that the
112 // work it represents need not be repeated.
114 serialized_dep_graph
.nodes
116 .filter_map(|&node
| retraced
.map(node
))
117 .filter(|node
| !dirty_nodes
.contains(node
))
118 .map(|node
| (node
, node
));
120 // Add nodes and edges that are not dirty into our main graph.
121 let dep_graph
= tcx
.dep_graph
.clone();
122 for (source
, target
) in clean_edges
.into_iter().chain(clean_nodes
) {
123 let _task
= dep_graph
.in_task(target
);
124 dep_graph
.read(source
);
126 debug
!("decode_dep_graph: clean edge: {:?} -> {:?}", source
, target
);
132 fn initial_dirty_nodes
<'tcx
>(tcx
: &ty
::TyCtxt
<'tcx
>,
133 hashed_items
: &[SerializedHash
],
134 retraced
: &RetracedDefIdDirectory
)
136 let mut items_removed
= false;
137 let mut dirty_nodes
= FnvHashSet();
138 for hashed_item
in hashed_items
{
139 match retraced
.def_id(hashed_item
.index
) {
141 // FIXME(#32753) -- should we use a distinct hash here
142 let current_hash
= tcx
.calculate_item_hash(def_id
);
143 debug
!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
144 def_id
, current_hash
, hashed_item
.hash
);
145 if current_hash
!= hashed_item
.hash
{
146 dirty_nodes
.insert(DepNode
::Hir(def_id
));
150 items_removed
= true;
155 // If any of the items in the krate have changed, then we consider
156 // the meta-node `Krate` to be dirty, since that means something
157 // which (potentially) read the contents of every single item.
158 if items_removed
|| !dirty_nodes
.is_empty() {
159 dirty_nodes
.insert(DepNode
::Krate
);
165 fn compute_clean_edges(serialized_edges
: &[(SerializedEdge
)],
166 retraced
: &RetracedDefIdDirectory
,
167 dirty_nodes
: &mut DirtyNodes
)
169 // Build up an initial list of edges. Include an edge (source,
170 // target) if neither node has been removed. If the source has
171 // been removed, add target to the list of dirty nodes.
172 let mut clean_edges
= Vec
::with_capacity(serialized_edges
.len());
173 for &(serialized_source
, serialized_target
) in serialized_edges
{
174 if let Some(target
) = retraced
.map(serialized_target
) {
175 if let Some(source
) = retraced
.map(serialized_source
) {
176 clean_edges
.push((source
, target
))
178 // source removed, target must be dirty
179 dirty_nodes
.insert(target
);
182 // target removed, ignore the edge
186 debug
!("compute_clean_edges: dirty_nodes={:#?}", dirty_nodes
);
188 // Propagate dirty marks by iterating repeatedly over
189 // `clean_edges`. If we find an edge `(source, target)` where
190 // `source` is dirty, add `target` to the list of dirty nodes and
191 // remove it. Keep doing this until we find no more dirty nodes.
192 let mut previous_size
= 0;
193 while dirty_nodes
.len() > previous_size
{
194 debug
!("compute_clean_edges: previous_size={}", previous_size
);
195 previous_size
= dirty_nodes
.len();
197 while i
< clean_edges
.len() {
198 if dirty_nodes
.contains(&clean_edges
[i
].0) {
199 let (source
, target
) = clean_edges
.swap_remove(i
);
200 debug
!("compute_clean_edges: dirty source {:?} -> {:?}",
202 dirty_nodes
.insert(target
);
203 } else if dirty_nodes
.contains(&clean_edges
[i
].1) {
204 let (source
, target
) = clean_edges
.swap_remove(i
);
205 debug
!("compute_clean_edges: dirty target {:?} -> {:?}",