]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/load.rs
0ac1018462ee7a7eb06a2e6cc2b75a01eacda4db
[rust.git] / 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.
4 //
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.
10
11 //! Code to save/load the dep-graph from files.
12
13 use rbml::Error;
14 use rbml::opaque::Decoder;
15 use rustc::dep_graph::DepNode;
16 use rustc::hir::def_id::DefId;
17 use rustc::ty::TyCtxt;
18 use rustc_data_structures::fnv::FnvHashSet;
19 use rustc_serialize::Decodable as RustcDecodable;
20 use std::io::Read;
21 use std::fs::File;
22 use std::path::Path;
23
24 use super::data::*;
25 use super::directory::*;
26 use super::dirty_clean;
27 use super::hash::*;
28 use super::util::*;
29
30 type DirtyNodes = FnvHashSet<DepNode<DefId>>;
31
32 type CleanEdges = Vec<(DepNode<DefId>, DepNode<DefId>)>;
33
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<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
41     let _ignore = tcx.dep_graph.in_ignore();
42
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);
47     }
48 }
49
50 pub fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, path: &Path) {
51     if !path.exists() {
52         return;
53     }
54
55     let mut data = vec![];
56     match
57         File::open(path)
58         .and_then(|mut file| file.read_to_end(&mut data))
59     {
60         Ok(_) => { }
61         Err(err) => {
62             tcx.sess.err(
63                 &format!("could not load dep-graph from `{}`: {}",
64                          path.display(), err));
65             return;
66         }
67     }
68
69     match decode_dep_graph(tcx, &data) {
70         Ok(dirty) => dirty,
71         Err(err) => {
72             bug!("decoding error in dep-graph from `{}`: {}", path.display(), err);
73         }
74     }
75 }
76
77 pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
78                                   data: &[u8])
79                                   -> Result<(), Error>
80 {
81     // Deserialize the directory and dep-graph.
82     let mut decoder = Decoder::new(data, 0);
83     let directory = try!(DefIdDirectory::decode(&mut decoder));
84     let serialized_dep_graph = try!(SerializedDepGraph::decode(&mut decoder));
85
86     debug!("decode_dep_graph: directory = {:#?}", directory);
87     debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph);
88
89     // Retrace the paths in the directory to find their current location (if any).
90     let retraced = directory.retrace(tcx);
91
92     debug!("decode_dep_graph: retraced = {:#?}", retraced);
93
94     // Compute the set of Hir nodes whose data has changed.
95     let mut dirty_nodes =
96         initial_dirty_nodes(tcx, &serialized_dep_graph.hashes, &retraced);
97
98     debug!("decode_dep_graph: initial dirty_nodes = {:#?}", dirty_nodes);
99
100     // Find all DepNodes reachable from that core set. This loop
101     // iterates repeatedly over the list of edges whose source is not
102     // known to be dirty (`clean_edges`). If it finds an edge whose
103     // source is dirty, it removes it from that list and adds the
104     // target to `dirty_nodes`. It stops when it reaches a fixed
105     // point.
106     let clean_edges = compute_clean_edges(&serialized_dep_graph.edges,
107                                           &retraced,
108                                           &mut dirty_nodes);
109
110     // Add synthetic `foo->foo` edges for each clean node `foo` that
111     // we had before. This is sort of a hack to create clean nodes in
112     // the graph, since the existence of a node is a signal that the
113     // work it represents need not be repeated.
114     let clean_nodes =
115         serialized_dep_graph.nodes
116                             .iter()
117                             .filter_map(|node| retraced.map(node))
118                             .filter(|node| !dirty_nodes.contains(node))
119                             .map(|node| (node.clone(), node));
120
121     // Add nodes and edges that are not dirty into our main graph.
122     let dep_graph = tcx.dep_graph.clone();
123     for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
124         let _task = dep_graph.in_task(target.clone());
125         dep_graph.read(source.clone());
126
127         debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
128     }
129
130     Ok(())
131 }
132
133 fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
134                                  hashes: &[SerializedHash],
135                                  retraced: &RetracedDefIdDirectory)
136                                  -> DirtyNodes {
137     let mut hcx = HashContext::new(tcx);
138     let mut items_removed = false;
139     let mut dirty_nodes = FnvHashSet();
140     for hash in hashes {
141         match hash.node.map_def(|&i| retraced.def_id(i)) {
142             Some(dep_node) => {
143                 let current_hash = hcx.hash(&dep_node).unwrap();
144                 debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
145                        dep_node, current_hash, hash.hash);
146                 if current_hash != hash.hash {
147                     dirty_nodes.insert(dep_node);
148                 }
149             }
150             None => {
151                 items_removed = true;
152             }
153         }
154     }
155
156     // If any of the items in the krate have changed, then we consider
157     // the meta-node `Krate` to be dirty, since that means something
158     // which (potentially) read the contents of every single item.
159     if items_removed || !dirty_nodes.is_empty() {
160         dirty_nodes.insert(DepNode::Krate);
161     }
162
163     dirty_nodes
164 }
165
166 fn compute_clean_edges(serialized_edges: &[(SerializedEdge)],
167                        retraced: &RetracedDefIdDirectory,
168                        dirty_nodes: &mut DirtyNodes)
169                        -> CleanEdges {
170     // Build up an initial list of edges. Include an edge (source,
171     // target) if neither node has been removed. If the source has
172     // been removed, add target to the list of dirty nodes.
173     let mut clean_edges = Vec::with_capacity(serialized_edges.len());
174     for &(ref serialized_source, ref serialized_target) in serialized_edges {
175         if let Some(target) = retraced.map(serialized_target) {
176             if let Some(source) = retraced.map(serialized_source) {
177                 clean_edges.push((source, target))
178             } else {
179                 // source removed, target must be dirty
180                 dirty_nodes.insert(target);
181             }
182         } else {
183             // target removed, ignore the edge
184         }
185     }
186
187     debug!("compute_clean_edges: dirty_nodes={:#?}", dirty_nodes);
188
189     // Propagate dirty marks by iterating repeatedly over
190     // `clean_edges`. If we find an edge `(source, target)` where
191     // `source` is dirty, add `target` to the list of dirty nodes and
192     // remove it. Keep doing this until we find no more dirty nodes.
193     let mut previous_size = 0;
194     while dirty_nodes.len() > previous_size {
195         debug!("compute_clean_edges: previous_size={}", previous_size);
196         previous_size = dirty_nodes.len();
197         let mut i = 0;
198         while i < clean_edges.len() {
199             if dirty_nodes.contains(&clean_edges[i].0) {
200                 let (source, target) = clean_edges.swap_remove(i);
201                 debug!("compute_clean_edges: dirty source {:?} -> {:?}",
202                        source, target);
203                 dirty_nodes.insert(target);
204             } else if dirty_nodes.contains(&clean_edges[i].1) {
205                 let (source, target) = clean_edges.swap_remove(i);
206                 debug!("compute_clean_edges: dirty target {:?} -> {:?}",
207                        source, target);
208             } else {
209                 i += 1;
210             }
211         }
212     }
213
214     clean_edges
215 }