]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/load.rs
Rollup merge of #39604 - est31:i128_tests, r=alexcrichton
[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 rustc::dep_graph::{DepNode, WorkProductId};
14 use rustc::hir::def_id::DefId;
15 use rustc::hir::svh::Svh;
16 use rustc::session::Session;
17 use rustc::ty::TyCtxt;
18 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
19 use rustc_serialize::Decodable as RustcDecodable;
20 use rustc_serialize::opaque::Decoder;
21 use std::path::{Path};
22 use std::sync::Arc;
23
24 use IncrementalHashesMap;
25 use ich::Fingerprint;
26 use super::data::*;
27 use super::directory::*;
28 use super::dirty_clean;
29 use super::hash::*;
30 use super::fs::*;
31 use super::file_format;
32 use super::work_product;
33
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>>;
37
38 /// If we are in incremental mode, and a previous dep-graph exists,
39 /// then load up those nodes/edges that are still valid into the
40 /// dep-graph for this session. (This is assumed to be running very
41 /// early in compilation, before we've really done any work, but
42 /// actually it doesn't matter all that much.) See `README.md` for
43 /// more general overview.
44 pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
45                                 incremental_hashes_map: &IncrementalHashesMap) {
46     if tcx.sess.opts.incremental.is_none() {
47         return;
48     }
49
50     match prepare_session_directory(tcx) {
51         Ok(true) => {
52             // We successfully allocated a session directory and there is
53             // something in it to load, so continue
54         }
55         Ok(false) => {
56             // We successfully allocated a session directory, but there is no
57             // dep-graph data in it to load (because this is the first
58             // compilation session with this incr. comp. dir.)
59             return
60         }
61         Err(()) => {
62             // Something went wrong while trying to allocate the session
63             // directory. Don't try to use it any further.
64             return
65         }
66     }
67
68     let _ignore = tcx.dep_graph.in_ignore();
69     load_dep_graph_if_exists(tcx, incremental_hashes_map);
70 }
71
72 fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
73                                       incremental_hashes_map: &IncrementalHashesMap) {
74     let dep_graph_path = dep_graph_path(tcx.sess);
75     let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) {
76         Some(p) => p,
77         None => return // no file
78     };
79
80     let work_products_path = work_products_path(tcx.sess);
81     let work_products_data = match load_data(tcx.sess, &work_products_path) {
82         Some(p) => p,
83         None => return // no file
84     };
85
86     match decode_dep_graph(tcx, incremental_hashes_map, &dep_graph_data, &work_products_data) {
87         Ok(dirty_nodes) => dirty_nodes,
88         Err(err) => {
89             tcx.sess.warn(
90                 &format!("decoding error in dep-graph from `{}` and `{}`: {}",
91                          dep_graph_path.display(),
92                          work_products_path.display(),
93                          err));
94         }
95     }
96 }
97
98 fn load_data(sess: &Session, path: &Path) -> Option<Vec<u8>> {
99     match file_format::read_file(sess, path) {
100         Ok(Some(data)) => return Some(data),
101         Ok(None) => {
102             // The file either didn't exist or was produced by an incompatible
103             // compiler version. Neither is an error.
104         }
105         Err(err) => {
106             sess.err(
107                 &format!("could not load dep-graph from `{}`: {}",
108                          path.display(), err));
109         }
110     }
111
112     if let Err(err) = delete_all_session_dir_contents(sess) {
113         sess.err(&format!("could not clear incompatible incremental \
114                            compilation session directory `{}`: {}",
115                           path.display(), err));
116     }
117
118     None
119 }
120
121 /// Decode the dep graph and load the edges/nodes that are still clean
122 /// into `tcx.dep_graph`.
123 pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
124                                   incremental_hashes_map: &IncrementalHashesMap,
125                                   dep_graph_data: &[u8],
126                                   work_products_data: &[u8])
127                                   -> Result<(), String>
128 {
129     // Decode the list of work_products
130     let mut work_product_decoder = Decoder::new(work_products_data, 0);
131     let work_products = <Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder)?;
132
133     // Deserialize the directory and dep-graph.
134     let mut dep_graph_decoder = Decoder::new(dep_graph_data, 0);
135     let prev_commandline_args_hash = u64::decode(&mut dep_graph_decoder)?;
136
137     if prev_commandline_args_hash != tcx.sess.opts.dep_tracking_hash() {
138         if tcx.sess.opts.debugging_opts.incremental_info {
139             println!("incremental: completely ignoring cache because of \
140                       differing commandline arguments");
141         }
142         // We can't reuse the cache, purge it.
143         debug!("decode_dep_graph: differing commandline arg hashes");
144         for swp in work_products {
145             delete_dirty_work_product(tcx, swp);
146         }
147
148         // No need to do any further work
149         return Ok(());
150     }
151
152     let directory = DefIdDirectory::decode(&mut dep_graph_decoder)?;
153     let serialized_dep_graph = SerializedDepGraph::decode(&mut dep_graph_decoder)?;
154
155     let edge_map: FxHashMap<_, _> = serialized_dep_graph.edges
156                                                         .into_iter()
157                                                         .map(|s| (s.source, s.targets))
158                                                         .collect();
159
160     // Retrace the paths in the directory to find their current location (if any).
161     let retraced = directory.retrace(tcx);
162
163     // Compute the set of nodes from the old graph where some input
164     // has changed or been removed. These are "raw" source nodes,
165     // which means that they still use the original `DefPathIndex`
166     // values from the encoding, rather than having been retraced to a
167     // `DefId`. The reason for this is that this way we can include
168     // nodes that have been removed (which no longer have a `DefId` in
169     // the current compilation).
170     let dirty_raw_nodes = initial_dirty_nodes(tcx,
171                                               incremental_hashes_map,
172                                               &serialized_dep_graph.hashes,
173                                               &retraced);
174     let dirty_raw_nodes = transitive_dirty_nodes(&edge_map, dirty_raw_nodes);
175
176     // Recreate the edges in the graph that are still clean.
177     let mut clean_work_products = FxHashSet();
178     let mut dirty_work_products = FxHashSet(); // incomplete; just used to suppress debug output
179     let mut extra_edges = vec![];
180     for (source, targets) in &edge_map {
181         for target in targets {
182             process_edges(tcx, source, target, &edge_map, &directory, &retraced, &dirty_raw_nodes,
183                           &mut clean_work_products, &mut dirty_work_products, &mut extra_edges);
184         }
185     }
186
187     // Subtle. Sometimes we have intermediate nodes that we can't recreate in the new graph.
188     // This is pretty unusual but it arises in a scenario like this:
189     //
190     //     Hir(X) -> Foo(Y) -> Bar
191     //
192     // Note that the `Hir(Y)` is not an input to `Foo(Y)` -- this
193     // almost never happens, but can happen in some obscure
194     // scenarios. In that case, if `Y` is removed, then we can't
195     // recreate `Foo(Y)` (the def-id `Y` no longer exists); what we do
196     // then is to push the edge `Hir(X) -> Bar` onto `extra_edges`
197     // (along with any other targets of `Foo(Y)`). We will then add
198     // the edge from `Hir(X)` to `Bar` (or, if `Bar` itself cannot be
199     // recreated, to the targets of `Bar`).
200     while let Some((source, target)) = extra_edges.pop() {
201         process_edges(tcx, source, target, &edge_map, &directory, &retraced, &dirty_raw_nodes,
202                       &mut clean_work_products, &mut dirty_work_products, &mut extra_edges);
203     }
204
205     // Add in work-products that are still clean, and delete those that are
206     // dirty.
207     reconcile_work_products(tcx, work_products, &clean_work_products);
208
209     dirty_clean::check_dirty_clean_annotations(tcx, &dirty_raw_nodes, &retraced);
210
211     load_prev_metadata_hashes(tcx,
212                               &retraced,
213                               &mut *incremental_hashes_map.prev_metadata_hashes.borrow_mut());
214     Ok(())
215 }
216
217 /// Computes which of the original set of def-ids are dirty. Stored in
218 /// a bit vector where the index is the DefPathIndex.
219 fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
220                                  incremental_hashes_map: &IncrementalHashesMap,
221                                  serialized_hashes: &[SerializedHash],
222                                  retraced: &RetracedDefIdDirectory)
223                                  -> DirtyNodes {
224     let mut hcx = HashContext::new(tcx, incremental_hashes_map);
225     let mut dirty_nodes = FxHashMap();
226
227     for hash in serialized_hashes {
228         if let Some(dep_node) = retraced.map(&hash.dep_node) {
229             let current_hash = hcx.hash(&dep_node).unwrap();
230             if current_hash == hash.hash {
231                 debug!("initial_dirty_nodes: {:?} is clean (hash={:?})",
232                    dep_node.map_def(|&def_id| Some(tcx.def_path(def_id))).unwrap(),
233                    current_hash);
234                 continue;
235             }
236
237             if tcx.sess.opts.debugging_opts.incremental_dump_hash {
238                 println!("node {:?} is dirty as hash is {:?} was {:?}",
239                          dep_node.map_def(|&def_id| Some(tcx.def_path(def_id))).unwrap(),
240                          current_hash,
241                          hash.hash);
242             }
243
244             debug!("initial_dirty_nodes: {:?} is dirty as hash is {:?}, was {:?}",
245                    dep_node.map_def(|&def_id| Some(tcx.def_path(def_id))).unwrap(),
246                    current_hash,
247                    hash.hash);
248         } else {
249             if tcx.sess.opts.debugging_opts.incremental_dump_hash {
250                 println!("node {:?} is dirty as it was removed",
251                          hash.dep_node);
252             }
253
254             debug!("initial_dirty_nodes: {:?} is dirty as it was removed",
255                    hash.dep_node);
256         }
257
258         dirty_nodes.insert(hash.dep_node.clone(), hash.dep_node.clone());
259     }
260
261     dirty_nodes
262 }
263
264 fn transitive_dirty_nodes(edge_map: &FxHashMap<DepNode<DefPathIndex>, Vec<DepNode<DefPathIndex>>>,
265                           mut dirty_nodes: DirtyNodes)
266                           -> DirtyNodes
267 {
268     let mut stack: Vec<(DepNode<DefPathIndex>, DepNode<DefPathIndex>)> = vec![];
269     stack.extend(dirty_nodes.iter().map(|(s, b)| (s.clone(), b.clone())));
270     while let Some((source, blame)) = stack.pop() {
271         // we know the source is dirty (because of the node `blame`)...
272         assert!(dirty_nodes.contains_key(&source));
273
274         // ...so we dirty all the targets (with the same blame)
275         if let Some(targets) = edge_map.get(&source) {
276             for target in targets {
277                 if !dirty_nodes.contains_key(target) {
278                     dirty_nodes.insert(target.clone(), blame.clone());
279                     stack.push((target.clone(), blame.clone()));
280                 }
281             }
282         }
283     }
284     dirty_nodes
285 }
286
287 /// Go through the list of work-products produced in the previous run.
288 /// Delete any whose nodes have been found to be dirty or which are
289 /// otherwise no longer applicable.
290 fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
291                                      work_products: Vec<SerializedWorkProduct>,
292                                      clean_work_products: &FxHashSet<Arc<WorkProductId>>) {
293     debug!("reconcile_work_products({:?})", work_products);
294     for swp in work_products {
295         if !clean_work_products.contains(&swp.id) {
296             debug!("reconcile_work_products: dep-node for {:?} is dirty", swp);
297             delete_dirty_work_product(tcx, swp);
298         } else {
299             let mut all_files_exist = true;
300             for &(_, ref file_name) in swp.work_product.saved_files.iter() {
301                 let path = in_incr_comp_dir_sess(tcx.sess, file_name);
302                 if !path.exists() {
303                     all_files_exist = false;
304
305                     if tcx.sess.opts.debugging_opts.incremental_info {
306                         println!("incremental: could not find file for up-to-date work product: {}",
307                                  path.display());
308                     }
309                 }
310             }
311
312             if all_files_exist {
313                 debug!("reconcile_work_products: all files for {:?} exist", swp);
314                 tcx.dep_graph.insert_previous_work_product(&swp.id, swp.work_product);
315             } else {
316                 debug!("reconcile_work_products: some file for {:?} does not exist", swp);
317                 delete_dirty_work_product(tcx, swp);
318             }
319         }
320     }
321 }
322
323 fn delete_dirty_work_product(tcx: TyCtxt,
324                              swp: SerializedWorkProduct) {
325     debug!("delete_dirty_work_product({:?})", swp);
326     work_product::delete_workproduct_files(tcx.sess, &swp.work_product);
327 }
328
329 fn load_prev_metadata_hashes(tcx: TyCtxt,
330                              retraced: &RetracedDefIdDirectory,
331                              output: &mut FxHashMap<DefId, Fingerprint>) {
332     if !tcx.sess.opts.debugging_opts.query_dep_graph {
333         return
334     }
335
336     debug!("load_prev_metadata_hashes() - Loading previous metadata hashes");
337
338     let file_path = metadata_hash_export_path(tcx.sess);
339
340     if !file_path.exists() {
341         debug!("load_prev_metadata_hashes() - Couldn't find file containing \
342                 hashes at `{}`", file_path.display());
343         return
344     }
345
346     debug!("load_prev_metadata_hashes() - File: {}", file_path.display());
347
348     let data = match file_format::read_file(tcx.sess, &file_path) {
349         Ok(Some(data)) => data,
350         Ok(None) => {
351             debug!("load_prev_metadata_hashes() - File produced by incompatible \
352                     compiler version: {}", file_path.display());
353             return
354         }
355         Err(err) => {
356             debug!("load_prev_metadata_hashes() - Error reading file `{}`: {}",
357                    file_path.display(), err);
358             return
359         }
360     };
361
362     debug!("load_prev_metadata_hashes() - Decoding hashes");
363     let mut decoder = Decoder::new(&data, 0);
364     let _ = Svh::decode(&mut decoder).unwrap();
365     let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder).unwrap();
366
367     debug!("load_prev_metadata_hashes() - Mapping DefIds");
368
369     assert_eq!(serialized_hashes.index_map.len(), serialized_hashes.hashes.len());
370     for serialized_hash in serialized_hashes.hashes {
371         let def_path_index = serialized_hashes.index_map[&serialized_hash.def_index];
372         if let Some(def_id) = retraced.def_id(def_path_index) {
373             let old = output.insert(def_id, serialized_hash.hash);
374             assert!(old.is_none(), "already have hash for {:?}", def_id);
375         }
376     }
377
378     debug!("load_prev_metadata_hashes() - successfully loaded {} hashes",
379            serialized_hashes.index_map.len());
380 }
381
382 fn process_edges<'a, 'tcx, 'edges>(
383     tcx: TyCtxt<'a, 'tcx, 'tcx>,
384     source: &'edges DepNode<DefPathIndex>,
385     target: &'edges DepNode<DefPathIndex>,
386     edges: &'edges FxHashMap<DepNode<DefPathIndex>, Vec<DepNode<DefPathIndex>>>,
387     directory: &DefIdDirectory,
388     retraced: &RetracedDefIdDirectory,
389     dirty_raw_nodes: &DirtyNodes,
390     clean_work_products: &mut FxHashSet<Arc<WorkProductId>>,
391     dirty_work_products: &mut FxHashSet<Arc<WorkProductId>>,
392     extra_edges: &mut Vec<(&'edges DepNode<DefPathIndex>, &'edges DepNode<DefPathIndex>)>)
393 {
394     // If the target is dirty, skip the edge. If this is an edge
395     // that targets a work-product, we can print the blame
396     // information now.
397     if let Some(blame) = dirty_raw_nodes.get(target) {
398         if let DepNode::WorkProduct(ref wp) = *target {
399             if tcx.sess.opts.debugging_opts.incremental_info {
400                 if dirty_work_products.insert(wp.clone()) {
401                     // It'd be nice to pretty-print these paths better than just
402                     // using the `Debug` impls, but wev.
403                     println!("incremental: module {:?} is dirty because {:?} \
404                               changed or was removed",
405                              wp,
406                              blame.map_def(|&index| {
407                                  Some(directory.def_path_string(tcx, index))
408                              }).unwrap());
409                 }
410             }
411         }
412         return;
413     }
414
415     // If the source is dirty, the target will be dirty.
416     assert!(!dirty_raw_nodes.contains_key(source));
417
418     // Retrace the source -> target edges to def-ids and then create
419     // an edge in the graph. Retracing may yield none if some of the
420     // data happens to have been removed.
421     if let Some(source_node) = retraced.map(source) {
422         if let Some(target_node) = retraced.map(target) {
423             let _task = tcx.dep_graph.in_task(target_node);
424             tcx.dep_graph.read(source_node);
425             if let DepNode::WorkProduct(ref wp) = *target {
426                 clean_work_products.insert(wp.clone());
427             }
428         } else {
429             // As discussed in `decode_dep_graph` above, sometimes the
430             // target cannot be recreated again, in which case we add
431             // edges to go from `source` to the targets of `target`.
432             extra_edges.extend(
433                 edges[target].iter().map(|t| (source, t)));
434         }
435     } else {
436         // It's also possible that the source can't be created! But we
437         // can ignore such cases, because (a) if `source` is a HIR
438         // node, it would be considered dirty; and (b) in other cases,
439         // there must be some input to this node that is clean, and so
440         // we'll re-create the edges over in the case where target is
441         // undefined.
442     }
443 }
444