load.rs 17.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Code to save/load the dep-graph from files.

13
use rustc::dep_graph::{DepNode, WorkProductId, DepKind};
N
Niko Matsakis 已提交
14
use rustc::hir::def_id::DefId;
15
use rustc::hir::svh::Svh;
16
use rustc::ich::Fingerprint;
17
use rustc::session::Session;
18
use rustc::ty::TyCtxt;
19
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
20
use rustc_data_structures::indexed_vec::IndexVec;
21
use rustc_serialize::Decodable as RustcDecodable;
22
use rustc_serialize::opaque::Decoder;
23
use std::path::{Path};
24

25
use IncrementalHashesMap;
26 27
use super::data::*;
use super::dirty_clean;
N
Niko Matsakis 已提交
28
use super::hash::*;
29
use super::fs::*;
30
use super::file_format;
31
use super::work_product;
32

33 34
// The key is a dirty node. The value is **some** base-input that we
// can blame it on.
35
pub type DirtyNodes = FxHashMap<DepNodeIndex, DepNodeIndex>;
36

N
Niko Matsakis 已提交
37 38 39 40 41 42
/// If we are in incremental mode, and a previous dep-graph exists,
/// then load up those nodes/edges that are still valid into the
/// dep-graph for this session. (This is assumed to be running very
/// early in compilation, before we've really done any work, but
/// actually it doesn't matter all that much.) See `README.md` for
/// more general overview.
43
pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
44
                                incremental_hashes_map: &IncrementalHashesMap) {
45 46 47 48
    if tcx.sess.opts.incremental.is_none() {
        return;
    }

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    match prepare_session_directory(tcx) {
        Ok(true) => {
            // We successfully allocated a session directory and there is
            // something in it to load, so continue
        }
        Ok(false) => {
            // We successfully allocated a session directory, but there is no
            // dep-graph data in it to load (because this is the first
            // compilation session with this incr. comp. dir.)
            return
        }
        Err(()) => {
            // Something went wrong while trying to allocate the session
            // directory. Don't try to use it any further.
            return
        }
    }

67
    let _ignore = tcx.dep_graph.in_ignore();
68
    load_dep_graph_if_exists(tcx, incremental_hashes_map);
69
}
70

71
fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
72
                                      incremental_hashes_map: &IncrementalHashesMap) {
73
    let dep_graph_path = dep_graph_path(tcx.sess);
74 75 76 77 78
    let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) {
        Some(p) => p,
        None => return // no file
    };

79
    let work_products_path = work_products_path(tcx.sess);
80 81 82 83 84
    let work_products_data = match load_data(tcx.sess, &work_products_path) {
        Some(p) => p,
        None => return // no file
    };

85
    match decode_dep_graph(tcx, incremental_hashes_map, &dep_graph_data, &work_products_data) {
86
        Ok(dirty_nodes) => dirty_nodes,
N
Niko Matsakis 已提交
87 88 89
        Err(err) => {
            tcx.sess.warn(
                &format!("decoding error in dep-graph from `{}` and `{}`: {}",
90 91
                         dep_graph_path.display(),
                         work_products_path.display(),
N
Niko Matsakis 已提交
92 93
                         err));
        }
94 95 96
    }
}

97
fn load_data(sess: &Session, path: &Path) -> Option<Vec<u8>> {
98
    match file_format::read_file(sess, path) {
99 100 101 102
        Ok(Some(data)) => return Some(data),
        Ok(None) => {
            // The file either didn't exist or was produced by an incompatible
            // compiler version. Neither is an error.
103
        }
104
        Err(err) => {
105
            sess.err(
106 107 108 109
                &format!("could not load dep-graph from `{}`: {}",
                         path.display(), err));
        }
    }
110 111 112 113 114 115 116 117

    if let Err(err) = delete_all_session_dir_contents(sess) {
        sess.err(&format!("could not clear incompatible incremental \
                           compilation session directory `{}`: {}",
                          path.display(), err));
    }

    None
118 119
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133
/// Check if a DepNode from the previous dep-graph refers to something that
/// still exists in the current compilation session. Only works for DepNode
/// variants that represent inputs (HIR and imported Metadata).
fn does_still_exist(tcx: TyCtxt, dep_node: &DepNode) -> bool {
    match dep_node.kind {
        DepKind::Hir |
        DepKind::HirBody |
        DepKind::MetaData => {
            dep_node.extract_def_id(tcx).is_some()
        }
        _ => {
            bug!("unexpected Input DepNode: {:?}", dep_node)
        }
    }
134 135
}

136
/// Decode the dep graph and load the edges/nodes that are still clean
N
Niko Matsakis 已提交
137
/// into `tcx.dep_graph`.
138
pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
139
                                  incremental_hashes_map: &IncrementalHashesMap,
140 141
                                  dep_graph_data: &[u8],
                                  work_products_data: &[u8])
142
                                  -> Result<(), String>
143
{
144 145
    // Decode the list of work_products
    let mut work_product_decoder = Decoder::new(work_products_data, 0);
146
    let work_products = <Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder)?;
147

148
    // Deserialize the directory and dep-graph.
149
    let mut dep_graph_decoder = Decoder::new(dep_graph_data, 0);
150
    let prev_commandline_args_hash = u64::decode(&mut dep_graph_decoder)?;
151 152

    if prev_commandline_args_hash != tcx.sess.opts.dep_tracking_hash() {
153 154 155 156
        if tcx.sess.opts.debugging_opts.incremental_info {
            println!("incremental: completely ignoring cache because of \
                      differing commandline arguments");
        }
157 158 159 160 161 162 163 164 165 166
        // We can't reuse the cache, purge it.
        debug!("decode_dep_graph: differing commandline arg hashes");
        for swp in work_products {
            delete_dirty_work_product(tcx, swp);
        }

        // No need to do any further work
        return Ok(());
    }

167
    let serialized_dep_graph = SerializedDepGraph::decode(&mut dep_graph_decoder)?;
168

169
    // Compute the set of nodes from the old graph where some input
170
    // has changed or been removed.
171 172
    let dirty_raw_nodes = initial_dirty_nodes(tcx,
                                              incremental_hashes_map,
173
                                              &serialized_dep_graph.nodes,
174
                                              &serialized_dep_graph.hashes);
175 176
    let dirty_raw_nodes = transitive_dirty_nodes(&serialized_dep_graph,
                                                 dirty_raw_nodes);
177 178 179 180

    // Recreate the edges in the graph that are still clean.
    let mut clean_work_products = FxHashSet();
    let mut dirty_work_products = FxHashSet(); // incomplete; just used to suppress debug output
181 182 183 184 185 186 187 188 189 190 191 192
    for (source, targets) in serialized_dep_graph.edge_list_indices.iter_enumerated() {
        let target_begin = targets.0 as usize;
        let target_end = targets.1 as usize;

        for &target in &serialized_dep_graph.edge_list_data[target_begin .. target_end] {
            process_edge(tcx,
                         source,
                         target,
                         &serialized_dep_graph.nodes,
                         &dirty_raw_nodes,
                         &mut clean_work_products,
                         &mut dirty_work_products);
193
        }
194 195
    }

196 197
    // Recreate bootstrap outputs, which are outputs that have no incoming edges
    // (and hence cannot be dirty).
198
    for bootstrap_output in &serialized_dep_graph.bootstrap_outputs {
199 200 201 202
        if let DepKind::WorkProduct = bootstrap_output.kind {
            let wp_id = WorkProductId::from_fingerprint(bootstrap_output.hash);
            clean_work_products.insert(wp_id);
        }
203

204
        tcx.dep_graph.with_task(*bootstrap_output, (), (), create_node);
N
Niko Matsakis 已提交
205

206 207
        fn create_node((): (), (): ()) {
            // just create the node with no inputs
208 209 210
        }
    }

211 212
    // Add in work-products that are still clean, and delete those that are
    // dirty.
213
    reconcile_work_products(tcx, work_products, &clean_work_products);
214

215 216 217
    dirty_clean::check_dirty_clean_annotations(tcx,
                                               &serialized_dep_graph.nodes,
                                               &dirty_raw_nodes);
218

219 220
    load_prev_metadata_hashes(tcx,
                              &mut *incremental_hashes_map.prev_metadata_hashes.borrow_mut());
221 222 223
    Ok(())
}

224 225
/// Computes which of the original set of def-ids are dirty. Stored in
/// a bit vector where the index is the DefPathIndex.
226 227
fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 incremental_hashes_map: &IncrementalHashesMap,
228 229
                                 nodes: &IndexVec<DepNodeIndex, DepNode>,
                                 serialized_hashes: &[(DepNodeIndex, Fingerprint)])
230
                                 -> DirtyNodes {
231
    let mut hcx = HashContext::new(tcx, incremental_hashes_map);
232
    let mut dirty_nodes = FxHashMap();
233

234 235
    for &(dep_node_index, prev_hash) in serialized_hashes {
        let dep_node = nodes[dep_node_index];
236 237 238 239 240
        if does_still_exist(tcx, &dep_node) {
            let current_hash = hcx.hash(&dep_node).unwrap_or_else(|| {
                bug!("Cannot find current ICH for input that still exists?")
            });

241
            if current_hash == prev_hash {
242 243
                debug!("initial_dirty_nodes: {:?} is clean (hash={:?})",
                       dep_node,
244
                       current_hash);
245 246
                continue;
            }
247

248 249 250 251
            if tcx.sess.opts.debugging_opts.incremental_dump_hash {
                println!("node {:?} is dirty as hash is {:?}, was {:?}",
                         dep_node,
                         current_hash,
252
                         prev_hash);
253
            }
254 255 256 257

            debug!("initial_dirty_nodes: {:?} is dirty as hash is {:?}, was {:?}",
                   dep_node,
                   current_hash,
258
                   prev_hash);
259
        } else {
260 261 262 263 264
            if tcx.sess.opts.debugging_opts.incremental_dump_hash {
                println!("node {:?} is dirty as it was removed", dep_node);
            }

            debug!("initial_dirty_nodes: {:?} is dirty as it was removed", dep_node);
265
        }
266
        dirty_nodes.insert(dep_node_index, dep_node_index);
267 268
    }

269
    dirty_nodes
270
}
271

272
fn transitive_dirty_nodes(serialized_dep_graph: &SerializedDepGraph,
273 274 275
                          mut dirty_nodes: DirtyNodes)
                          -> DirtyNodes
{
276 277
    let mut stack: Vec<(DepNodeIndex, DepNodeIndex)> = vec![];
    stack.extend(dirty_nodes.iter().map(|(&s, &b)| (s, b)));
N
Niko Matsakis 已提交
278 279
    while let Some((source, blame)) = stack.pop() {
        // we know the source is dirty (because of the node `blame`)...
280
        debug_assert!(dirty_nodes.contains_key(&source));
N
Niko Matsakis 已提交
281 282

        // ...so we dirty all the targets (with the same blame)
283 284 285 286
        for &target in serialized_dep_graph.edge_targets_from(source) {
            if !dirty_nodes.contains_key(&target) {
                dirty_nodes.insert(target, blame);
                stack.push((target, blame));
287 288 289 290 291 292
            }
        }
    }
    dirty_nodes
}

293 294 295 296 297
/// Go through the list of work-products produced in the previous run.
/// Delete any whose nodes have been found to be dirty or which are
/// otherwise no longer applicable.
fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     work_products: Vec<SerializedWorkProduct>,
298
                                     clean_work_products: &FxHashSet<WorkProductId>) {
299 300
    debug!("reconcile_work_products({:?})", work_products);
    for swp in work_products {
301
        if !clean_work_products.contains(&swp.id) {
302 303 304
            debug!("reconcile_work_products: dep-node for {:?} is dirty", swp);
            delete_dirty_work_product(tcx, swp);
        } else {
305 306 307 308 309 310 311 312 313 314 315 316 317
            let mut all_files_exist = true;
            for &(_, ref file_name) in swp.work_product.saved_files.iter() {
                let path = in_incr_comp_dir_sess(tcx.sess, file_name);
                if !path.exists() {
                    all_files_exist = false;

                    if tcx.sess.opts.debugging_opts.incremental_info {
                        println!("incremental: could not find file for up-to-date work product: {}",
                                 path.display());
                    }
                }
            }

318 319
            if all_files_exist {
                debug!("reconcile_work_products: all files for {:?} exist", swp);
320 321
                tcx.dep_graph.insert_previous_work_product(&swp.id, swp.work_product);
            } else {
322 323
                debug!("reconcile_work_products: some file for {:?} does not exist", swp);
                delete_dirty_work_product(tcx, swp);
324 325 326 327 328 329 330 331
            }
        }
    }
}

fn delete_dirty_work_product(tcx: TyCtxt,
                             swp: SerializedWorkProduct) {
    debug!("delete_dirty_work_product({:?})", swp);
332
    work_product::delete_workproduct_files(tcx.sess, &swp.work_product);
333
}
334 335

fn load_prev_metadata_hashes(tcx: TyCtxt,
336
                             output: &mut FxHashMap<DefId, Fingerprint>) {
337
    if !tcx.sess.opts.debugging_opts.query_dep_graph {
338
        // Previous metadata hashes are only needed for testing.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
        return
    }

    debug!("load_prev_metadata_hashes() - Loading previous metadata hashes");

    let file_path = metadata_hash_export_path(tcx.sess);

    if !file_path.exists() {
        debug!("load_prev_metadata_hashes() - Couldn't find file containing \
                hashes at `{}`", file_path.display());
        return
    }

    debug!("load_prev_metadata_hashes() - File: {}", file_path.display());

354
    let data = match file_format::read_file(tcx.sess, &file_path) {
355 356 357 358 359 360 361 362 363 364 365 366
        Ok(Some(data)) => data,
        Ok(None) => {
            debug!("load_prev_metadata_hashes() - File produced by incompatible \
                    compiler version: {}", file_path.display());
            return
        }
        Err(err) => {
            debug!("load_prev_metadata_hashes() - Error reading file `{}`: {}",
                   file_path.display(), err);
            return
        }
    };
367 368

    debug!("load_prev_metadata_hashes() - Decoding hashes");
369
    let mut decoder = Decoder::new(&data, 0);
370 371 372 373 374
    let _ = Svh::decode(&mut decoder).unwrap();
    let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder).unwrap();

    debug!("load_prev_metadata_hashes() - Mapping DefIds");

375
    assert_eq!(serialized_hashes.index_map.len(), serialized_hashes.entry_hashes.len());
376 377
    let def_path_hash_to_def_id = tcx.def_path_hash_to_def_id.as_ref().unwrap();

378
    for serialized_hash in serialized_hashes.entry_hashes {
379 380
        let def_path_hash = serialized_hashes.index_map[&serialized_hash.def_index];
        if let Some(&def_id) = def_path_hash_to_def_id.get(&def_path_hash) {
381 382 383 384 385 386 387 388
            let old = output.insert(def_id, serialized_hash.hash);
            assert!(old.is_none(), "already have hash for {:?}", def_id);
        }
    }

    debug!("load_prev_metadata_hashes() - successfully loaded {} hashes",
           serialized_hashes.index_map.len());
}
389

390
fn process_edge<'a, 'tcx, 'edges>(
391
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
392 393 394
    source: DepNodeIndex,
    target: DepNodeIndex,
    nodes: &IndexVec<DepNodeIndex, DepNode>,
395
    dirty_raw_nodes: &DirtyNodes,
396
    clean_work_products: &mut FxHashSet<WorkProductId>,
397
    dirty_work_products: &mut FxHashSet<WorkProductId>)
398 399 400 401
{
    // If the target is dirty, skip the edge. If this is an edge
    // that targets a work-product, we can print the blame
    // information now.
402 403
    if let Some(&blame) = dirty_raw_nodes.get(&target) {
        let target = nodes[target];
404
        if let DepKind::WorkProduct = target.kind {
405
            if tcx.sess.opts.debugging_opts.incremental_info {
406 407 408
                let wp_id = WorkProductId::from_fingerprint(target.hash);

                if dirty_work_products.insert(wp_id) {
409 410 411
                    // Try to reconstruct the human-readable version of the
                    // DepNode. This cannot be done for things that where
                    // removed.
412
                    let blame = nodes[blame];
413 414 415 416
                    let blame_str = if let Some(def_id) = blame.extract_def_id(tcx) {
                        format!("{:?}({})",
                                blame.kind,
                                tcx.def_path(def_id).to_string(tcx))
417
                    } else {
418
                        format!("{:?}", blame)
419 420
                    };

421 422
                    println!("incremental: module {:?} is dirty because {:?} \
                              changed or was removed",
423 424
                             wp_id,
                             blame_str);
425 426 427 428 429 430
                }
            }
        }
        return;
    }

431 432 433 434 435 436
    // At this point we have asserted that the target is clean -- otherwise, we
    // would have hit the return above. We can do some further consistency
    // checks based on this fact:

    // We should never have an edge where the target is clean but the source
    // was dirty. Otherwise something was wrong with the dirtying pass above:
437
    debug_assert!(!dirty_raw_nodes.contains_key(&source));
438 439 440 441

    // We also never should encounter an edge going from a removed input to a
    // clean target because removing the input would have dirtied the input
    // node and transitively dirtied the target.
442
    debug_assert!(match nodes[source].kind {
443
        DepKind::Hir | DepKind::HirBody | DepKind::MetaData => {
444
            does_still_exist(tcx, &nodes[source])
445 446 447 448
        }
        _ => true,
    });

449 450 451 452 453
    if !dirty_raw_nodes.contains_key(&target) {
        let target = nodes[target];
        let source = nodes[source];
        let _task = tcx.dep_graph.in_task(target);
        tcx.dep_graph.read(source);
454 455 456 457

        if let DepKind::WorkProduct = target.kind {
            let wp_id = WorkProductId::from_fingerprint(target.hash);
            clean_work_products.insert(wp_id);
458 459 460 461
        }
    }
}