about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-07-21 12:49:59 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-07-28 12:05:45 -0400
commit58d4b8edd319d0f0d76024504cdfc74f89a001b1 (patch)
tree83a92fe303444117e45ba19cfc78bbef41e2ff7d
parentcca4804251957646d4840bf33b3b13e3f2b645de (diff)
downloadrust-58d4b8edd319d0f0d76024504cdfc74f89a001b1.tar.gz
rust-58d4b8edd319d0f0d76024504cdfc74f89a001b1.zip
Modify trans to skip generating `.o` files
This checks the `previous_work_products` data from the dep-graph and
tries to simply copy a `.o` file if possible.  We also add new
work-products into the dep-graph, and create edges to/from the dep-node
for a work-product.
-rw-r--r--src/librustc_incremental/lib.rs2
-rw-r--r--src/librustc_incremental/persist/mod.rs3
-rw-r--r--src/librustc_incremental/persist/work_product.rs61
-rw-r--r--src/librustc_trans/back/write.rs67
-rw-r--r--src/librustc_trans/base.rs137
-rw-r--r--src/librustc_trans/consts.rs2
-rw-r--r--src/librustc_trans/context.rs69
-rw-r--r--src/librustc_trans/glue.rs2
-rw-r--r--src/librustc_trans/lib.rs14
-rw-r--r--src/librustc_trans/monomorphize.rs2
-rw-r--r--src/librustc_trans/partitioning.rs80
-rw-r--r--src/librustc_trans/trans_item.rs30
-rw-r--r--src/test/run-make/execution-engine/test.rs7
-rw-r--r--src/test/run-make/llvm-phase/test.rs7
14 files changed, 385 insertions, 98 deletions
diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs
index 352e5979d01..0d11b0794fe 100644
--- a/src/librustc_incremental/lib.rs
+++ b/src/librustc_incremental/lib.rs
@@ -41,4 +41,6 @@ pub use assert_dep_graph::assert_dep_graph;
 pub use calculate_svh::SvhCalculate;
 pub use persist::load_dep_graph;
 pub use persist::save_dep_graph;
+pub use persist::save_trans_partition;
 pub use persist::save_work_products;
+pub use persist::in_incr_comp_dir;
diff --git a/src/librustc_incremental/persist/mod.rs b/src/librustc_incremental/persist/mod.rs
index 30e7d7873ec..1157f494ce6 100644
--- a/src/librustc_incremental/persist/mod.rs
+++ b/src/librustc_incremental/persist/mod.rs
@@ -19,7 +19,10 @@ mod hash;
 mod load;
 mod save;
 mod util;
+mod work_product;
 
 pub use self::load::load_dep_graph;
 pub use self::save::save_dep_graph;
 pub use self::save::save_work_products;
+pub use self::work_product::save_trans_partition;
+pub use self::util::in_incr_comp_dir;
diff --git a/src/librustc_incremental/persist/work_product.rs b/src/librustc_incremental/persist/work_product.rs
new file mode 100644
index 00000000000..01ac3f6c391
--- /dev/null
+++ b/src/librustc_incremental/persist/work_product.rs
@@ -0,0 +1,61 @@
+// Copyright 2012-2015 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.
+
+//! This module contains files for saving intermediate work-products.
+
+use persist::util::*;
+use rustc::dep_graph::{WorkProduct, WorkProductId};
+use rustc::session::Session;
+use std::fs;
+use std::path::Path;
+use std::sync::Arc;
+
+pub fn save_trans_partition(sess: &Session,
+                            partition_name: &str,
+                            partition_hash: u64,
+                            path_to_obj_file: &Path) {
+    debug!("save_trans_partition({:?},{},{})",
+           partition_name,
+           partition_hash,
+           path_to_obj_file.display());
+    if sess.opts.incremental.is_none() {
+        return;
+    }
+    let id = Arc::new(WorkProductId::PartitionObjectFile(partition_name.to_string()));
+    let file_name = format!("cgu-{}", partition_name);
+    let path_in_incr_dir = in_incr_comp_dir(sess, &file_name).unwrap();
+
+    // try to delete the file if it already exists
+    //
+    // FIXME(#34955) we can be smarter here -- if we are re-using, no need to do anything
+    if path_in_incr_dir.exists() {
+        let _ = fs::remove_file(&path_in_incr_dir);
+    }
+
+    match
+        fs::hard_link(path_to_obj_file, &path_in_incr_dir)
+        .or_else(|_| fs::copy(path_to_obj_file, &path_in_incr_dir).map(|_| ()))
+    {
+        Ok(_) => {
+            let work_product = WorkProduct {
+                input_hash: partition_hash,
+                file_name: file_name,
+            };
+            sess.dep_graph.insert_work_product(&id, work_product);
+        }
+        Err(err) => {
+            sess.warn(&format!("error copying object file `{}` \
+                                to incremental directory as `{}`: {}",
+                               path_to_obj_file.display(),
+                               path_in_incr_dir.display(),
+                               err));
+        }
+    }
+}
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index 33cffa8a480..70925218781 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -10,13 +10,14 @@
 
 use back::lto;
 use back::link::{get_linker, remove};
+use rustc_incremental::save_trans_partition;
 use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
 use session::Session;
 use session::config::{self, OutputType};
 use llvm;
 use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
 use llvm::SMDiagnosticRef;
-use {CrateTranslation, ModuleTranslation};
+use {CrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation};
 use util::common::time;
 use util::common::path2cstr;
 use errors::{self, Handler, Level, DiagnosticBuilder};
@@ -26,6 +27,7 @@ use syntax_pos::MultiSpan;
 use std::collections::HashMap;
 use std::ffi::{CStr, CString};
 use std::fs;
+use std::io;
 use std::path::{Path, PathBuf};
 use std::str;
 use std::sync::{Arc, Mutex};
@@ -422,10 +424,11 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
 // Unsafe due to LLVM calls.
 unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
                                mtrans: ModuleTranslation,
+                               mllvm: ModuleLlvm,
                                config: ModuleConfig,
                                output_names: OutputFilenames) {
-    let llmod = mtrans.llmod;
-    let llcx = mtrans.llcx;
+    let llmod = mllvm.llmod;
+    let llcx = mllvm.llcx;
     let tm = config.tm;
 
     // llcx doesn't outlive this function, so we can put this on the stack.
@@ -628,8 +631,14 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
 pub fn cleanup_llvm(trans: &CrateTranslation) {
     for module in trans.modules.iter() {
         unsafe {
-            llvm::LLVMDisposeModule(module.llmod);
-            llvm::LLVMContextDispose(module.llcx);
+            match module.source {
+                ModuleSource::Translated(llvm) => {
+                    llvm::LLVMDisposeModule(llvm.llmod);
+                    llvm::LLVMContextDispose(llvm.llcx);
+                }
+                ModuleSource::Preexisting(_) => {
+                }
+            }
         }
     }
 }
@@ -743,6 +752,13 @@ pub fn run_passes(sess: &Session,
         run_work_multithreaded(sess, work_items, num_workers);
     }
 
+    // If in incr. comp. mode, preserve the `.o` files for potential re-use
+    for mtrans in trans.modules.iter() {
+        let path_to_obj = crate_output.temp_path(OutputType::Object, Some(&mtrans.name));
+        debug!("wrote module {:?} to {:?}", mtrans.name, path_to_obj);
+        save_trans_partition(sess, &mtrans.name, mtrans.symbol_name_hash, &path_to_obj);
+    }
+
     // All codegen is finished.
     unsafe {
         llvm::LLVMRustDisposeTargetMachine(tm);
@@ -913,13 +929,46 @@ fn build_work_item(sess: &Session,
     }
 }
 
+fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<()> {
+    let p = p.as_ref();
+    let q = q.as_ref();
+    if q.exists() {
+        try!(fs::remove_file(&q));
+    }
+    fs::hard_link(p, q)
+        .or_else(|_| fs::copy(p, q).map(|_| ()))
+}
+
 fn execute_work_item(cgcx: &CodegenContext,
                      work_item: WorkItem) {
     unsafe {
-        optimize_and_codegen(cgcx,
-                             work_item.mtrans,
-                             work_item.config,
-                             work_item.output_names);
+        match work_item.mtrans.source {
+            ModuleSource::Translated(mllvm) => {
+                debug!("llvm-optimizing {:?}", work_item.mtrans.name);
+                optimize_and_codegen(cgcx,
+                                     work_item.mtrans,
+                                     mllvm,
+                                     work_item.config,
+                                     work_item.output_names);
+            }
+            ModuleSource::Preexisting(ref buf) => {
+                let obj_out = work_item.output_names.temp_path(OutputType::Object,
+                                                               Some(&work_item.mtrans.name));
+                debug!("copying pre-existing module `{}` from {} to {}",
+                       work_item.mtrans.name,
+                       buf.display(),
+                       obj_out.display());
+                match link_or_copy(buf, &obj_out) {
+                    Ok(()) => { }
+                    Err(err) => {
+                        cgcx.handler.err(&format!("unable to copy {} to {}: {}",
+                                                  buf.display(),
+                                                  obj_out.display(),
+                                                  err));
+                    }
+                }
+            }
+        }
     }
 }
 
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index ea8c248d023..a77ababaa63 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -26,6 +26,8 @@
 #![allow(non_camel_case_types)]
 
 use super::CrateTranslation;
+use super::ModuleLlvm;
+use super::ModuleSource;
 use super::ModuleTranslation;
 
 use back::link;
@@ -43,6 +45,7 @@ use rustc::ty::adjustment::CustomCoerceUnsized;
 use rustc::dep_graph::DepNode;
 use rustc::hir::map as hir_map;
 use rustc::util::common::time;
+use rustc_incremental::in_incr_comp_dir;
 use rustc::mir::mir_map::MirMap;
 use rustc_data_structures::graph::OUTGOING;
 use session::config::{self, NoDebugInfo, FullDebugInfo};
@@ -99,6 +102,7 @@ use std::cell::{Cell, RefCell};
 use std::collections::HashMap;
 use std::ptr;
 use std::rc::Rc;
+use std::path::PathBuf;
 use std::str;
 use std::{i8, i16, i32, i64};
 use syntax_pos::{Span, DUMMY_SP};
@@ -2133,7 +2137,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) {
 
     let instance = Instance::mono(ccx.shared(), main_def_id);
 
-    if !ccx.codegen_unit().items.contains_key(&TransItem::Fn(instance)) {
+    if !ccx.codegen_unit().contains_item(&TransItem::Fn(instance)) {
         // We want to create the wrapper in the same codegen unit as Rust's main
         // function.
         return;
@@ -2270,7 +2274,7 @@ fn internalize_symbols<'a, 'tcx>(ccxs: &CrateContextList<'a, 'tcx>,
 
         // Collect all symbols that need to stay externally visible because they
         // are referenced via a declaration in some other codegen unit.
-        for ccx in ccxs.iter() {
+        for ccx in ccxs.iter_need_trans() {
             for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
                 let linkage = llvm::LLVMGetLinkage(val);
                 // We only care about external declarations (not definitions)
@@ -2315,7 +2319,7 @@ fn internalize_symbols<'a, 'tcx>(ccxs: &CrateContextList<'a, 'tcx>,
         // Examine each external definition.  If the definition is not used in
         // any other compilation unit, and is not reachable from other crates,
         // then give it internal linkage.
-        for ccx in ccxs.iter() {
+        for ccx in ccxs.iter_need_trans() {
             for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
                 let linkage = llvm::LLVMGetLinkage(val);
 
@@ -2362,7 +2366,7 @@ fn create_imps(cx: &CrateContextList) {
         "\x01__imp_"
     };
     unsafe {
-        for ccx in cx.iter() {
+        for ccx in cx.iter_need_trans() {
             let exported: Vec<_> = iter_globals(ccx.llmod())
                                        .filter(|&val| {
                                            llvm::LLVMGetLinkage(val) ==
@@ -2514,8 +2518,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let metadata_module = ModuleTranslation {
         name: "metadata".to_string(),
-        llcx: shared_ccx.metadata_llcx(),
-        llmod: shared_ccx.metadata_llmod(),
+        symbol_name_hash: 0, // we always rebuild metadata, at least for now
+        source: ModuleSource::Translated(ModuleLlvm {
+            llcx: shared_ccx.metadata_llcx(),
+            llmod: shared_ccx.metadata_llmod(),
+        }),
     };
     let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
 
@@ -2525,14 +2532,29 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let symbol_map = Rc::new(symbol_map);
 
+    let previous_work_products = trans_reuse_previous_work_products(tcx,
+                                                                    &codegen_units,
+                                                                    &symbol_map);
+
     let crate_context_list = CrateContextList::new(&shared_ccx,
                                                    codegen_units,
+                                                   previous_work_products,
                                                    symbol_map.clone());
-    let modules = crate_context_list.iter()
-        .map(|ccx| ModuleTranslation {
-            name: String::from(&ccx.codegen_unit().name[..]),
-            llcx: ccx.llcx(),
-            llmod: ccx.llmod()
+    let modules: Vec<_> = crate_context_list.iter_all()
+        .map(|ccx| {
+            let source = match ccx.previous_work_product() {
+                Some(buf) => ModuleSource::Preexisting(buf.clone()),
+                None => ModuleSource::Translated(ModuleLlvm {
+                    llcx: ccx.llcx(),
+                    llmod: ccx.llmod(),
+                }),
+            };
+
+            ModuleTranslation {
+                name: String::from(ccx.codegen_unit().name()),
+                symbol_name_hash: ccx.codegen_unit().compute_symbol_name_hash(tcx, &symbol_map),
+                source: source,
+            }
         })
         .collect();
 
@@ -2551,41 +2573,44 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 
     // Instantiate translation items without filling out definitions yet...
-    for ccx in crate_context_list.iter() {
-        let trans_items = ccx.codegen_unit()
-                             .items_in_deterministic_order(tcx, &symbol_map);
+    for ccx in crate_context_list.iter_need_trans() {
+        let cgu = ccx.codegen_unit();
+        let trans_items = cgu.items_in_deterministic_order(tcx, &symbol_map);
 
-        for (trans_item, linkage) in trans_items {
-            trans_item.predefine(&ccx, linkage);
-        }
+        tcx.dep_graph.with_task(cgu.work_product_dep_node(), || {
+            for (trans_item, linkage) in trans_items {
+                trans_item.predefine(&ccx, linkage);
+            }
+        });
     }
 
     // ... and now that we have everything pre-defined, fill out those definitions.
-    for ccx in crate_context_list.iter() {
-        let trans_items = ccx.codegen_unit()
-                             .items_in_deterministic_order(tcx, &symbol_map);
-
-        for (trans_item, _) in trans_items {
-           trans_item.define(&ccx);
-        }
+    for ccx in crate_context_list.iter_need_trans() {
+        let cgu = ccx.codegen_unit();
+        let trans_items = cgu.items_in_deterministic_order(tcx, &symbol_map);
+        tcx.dep_graph.with_task(cgu.work_product_dep_node(), || {
+            for (trans_item, _) in trans_items {
+                trans_item.define(&ccx);
+            }
 
-        // If this codegen unit contains the main function, also create the
-        // wrapper here
-        maybe_create_entry_wrapper(&ccx);
+            // If this codegen unit contains the main function, also create the
+            // wrapper here
+            maybe_create_entry_wrapper(&ccx);
 
-        // Run replace-all-uses-with for statics that need it
-        for &(old_g, new_g) in ccx.statics_to_rauw().borrow().iter() {
-            unsafe {
-                let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g));
-                llvm::LLVMReplaceAllUsesWith(old_g, bitcast);
-                llvm::LLVMDeleteGlobal(old_g);
+            // Run replace-all-uses-with for statics that need it
+            for &(old_g, new_g) in ccx.statics_to_rauw().borrow().iter() {
+                unsafe {
+                    let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g));
+                    llvm::LLVMReplaceAllUsesWith(old_g, bitcast);
+                    llvm::LLVMDeleteGlobal(old_g);
+                }
             }
-        }
 
-        // Finalize debuginfo
-        if ccx.sess().opts.debuginfo != NoDebugInfo {
-            debuginfo::finalize(&ccx);
-        }
+            // Finalize debuginfo
+            if ccx.sess().opts.debuginfo != NoDebugInfo {
+                debuginfo::finalize(&ccx);
+            }
+        });
     }
 
     symbol_names_test::report_symbol_names(&shared_ccx);
@@ -2679,6 +2704,38 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 }
 
+/// For each CGU, identify if we can reuse an existing object file (or
+/// maybe other context).
+fn trans_reuse_previous_work_products(tcx: TyCtxt,
+                                      codegen_units: &[CodegenUnit],
+                                      symbol_map: &SymbolMap)
+                                      -> Vec<Option<PathBuf>> {
+    debug!("trans_reuse_previous_work_products()");
+    codegen_units
+        .iter()
+        .map(|cgu| {
+            let id = cgu.work_product_id();
+
+            let hash = cgu.compute_symbol_name_hash(tcx, symbol_map);
+
+            debug!("trans_reuse_previous_work_products: id={:?} hash={}", id, hash);
+
+            if let Some(work_product) = tcx.dep_graph.previous_work_product(&id) {
+                if work_product.input_hash == hash {
+                    debug!("trans_reuse_previous_work_products: reusing {:?}", work_product);
+                    return Some(in_incr_comp_dir(tcx.sess, &work_product.file_name).unwrap());
+                } else {
+                    debug!("trans_reuse_previous_work_products: \
+                            not reusing {:?} because hash changed to {:?}",
+                           work_product, hash);
+                }
+            }
+
+            None
+        })
+        .collect()
+}
+
 fn collect_and_partition_translation_items<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>)
                                                      -> (Vec<CodegenUnit<'tcx>>, SymbolMap<'tcx>) {
     let time_passes = scx.sess().time_passes();
@@ -2739,10 +2796,10 @@ fn collect_and_partition_translation_items<'a, 'tcx>(scx: &SharedCrateContext<'a
         let mut item_to_cgus = HashMap::new();
 
         for cgu in &codegen_units {
-            for (&trans_item, &linkage) in &cgu.items {
+            for (&trans_item, &linkage) in cgu.items() {
                 item_to_cgus.entry(trans_item)
                             .or_insert(Vec::new())
-                            .push((cgu.name.clone(), linkage));
+                            .push((cgu.name().clone(), linkage));
             }
         }
 
diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs
index f662ba75cc6..571d2731fb2 100644
--- a/src/librustc_trans/consts.rs
+++ b/src/librustc_trans/consts.rs
@@ -1029,7 +1029,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
                 assert!(!ccx.external_srcs().borrow().contains_key(&id));
 
                 let defined_in_current_codegen_unit = ccx.codegen_unit()
-                                                         .items
+                                                         .items()
                                                          .contains_key(&TransItem::Static(id));
                 if defined_in_current_codegen_unit {
                     if declare::get_declared_value(ccx, sym).is_none() {
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index 88903726d64..bf696298212 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -40,6 +40,7 @@ use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet};
 
 use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
+use std::path::PathBuf;
 use std::marker::PhantomData;
 use std::ptr;
 use std::rc::Rc;
@@ -95,6 +96,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
 pub struct LocalCrateContext<'tcx> {
     llmod: ModuleRef,
     llcx: ContextRef,
+    previous_work_product: Option<PathBuf>,
     tn: TypeNames, // FIXME: This seems to be largely unused.
     codegen_unit: CodegenUnit<'tcx>,
     needs_unwind_cleanup_cache: RefCell<FnvHashMap<Ty<'tcx>, bool>>,
@@ -198,24 +200,39 @@ pub struct CrateContextList<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx: 'a> CrateContextList<'a, 'tcx> {
-
     pub fn new(shared_ccx: &'a SharedCrateContext<'a, 'tcx>,
                codegen_units: Vec<CodegenUnit<'tcx>>,
+               previous_work_products: Vec<Option<PathBuf>>,
                symbol_map: Rc<SymbolMap<'tcx>>)
                -> CrateContextList<'a, 'tcx> {
         CrateContextList {
             shared: shared_ccx,
-            local_ccxs: codegen_units.into_iter().map(|codegen_unit| {
-                LocalCrateContext::new(shared_ccx, codegen_unit, symbol_map.clone())
+            local_ccxs: codegen_units.into_iter().zip(previous_work_products).map(|(cgu, path)| {
+                LocalCrateContext::new(shared_ccx, cgu, path, symbol_map.clone())
             }).collect()
         }
     }
 
-    pub fn iter<'b>(&'b self) -> CrateContextIterator<'b, 'tcx> {
+    /// Iterate over all crate contexts, whether or not they need
+    /// translation.  That is, whether or not a `.o` file is available
+    /// for re-use from a previous incr. comp.).
+    pub fn iter_all<'b>(&'b self) -> CrateContextIterator<'b, 'tcx> {
         CrateContextIterator {
             shared: self.shared,
             index: 0,
-            local_ccxs: &self.local_ccxs[..]
+            local_ccxs: &self.local_ccxs[..],
+            filter_to_previous_work_product_unavail: false,
+        }
+    }
+
+    /// Iterator over all CCX that need translation (cannot reuse results from
+    /// previous incr. comp.).
+    pub fn iter_need_trans<'b>(&'b self) -> CrateContextIterator<'b, 'tcx> {
+        CrateContextIterator {
+            shared: self.shared,
+            index: 0,
+            local_ccxs: &self.local_ccxs[..],
+            filter_to_previous_work_product_unavail: true,
         }
     }
 
@@ -239,24 +256,38 @@ pub struct CrateContextIterator<'a, 'tcx: 'a> {
     shared: &'a SharedCrateContext<'a, 'tcx>,
     local_ccxs: &'a [LocalCrateContext<'tcx>],
     index: usize,
+
+    /// if true, only return results where `previous_work_product` is none
+    filter_to_previous_work_product_unavail: bool,
 }
 
 impl<'a, 'tcx> Iterator for CrateContextIterator<'a,'tcx> {
     type Item = CrateContext<'a, 'tcx>;
 
     fn next(&mut self) -> Option<CrateContext<'a, 'tcx>> {
-        if self.index >= self.local_ccxs.len() {
-            return None;
-        }
+        loop {
+            if self.index >= self.local_ccxs.len() {
+                return None;
+            }
 
-        let index = self.index;
-        self.index += 1;
+            let index = self.index;
+            self.index += 1;
 
-        Some(CrateContext {
-            shared: self.shared,
-            index: index,
-            local_ccxs: self.local_ccxs,
-        })
+            let ccx = CrateContext {
+                shared: self.shared,
+                index: index,
+                local_ccxs: self.local_ccxs,
+            };
+
+            if
+                self.filter_to_previous_work_product_unavail &&
+                ccx.previous_work_product().is_some()
+            {
+                continue;
+            }
+
+            return Some(ccx);
+        }
     }
 }
 
@@ -510,6 +541,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
 impl<'tcx> LocalCrateContext<'tcx> {
     fn new<'a>(shared: &SharedCrateContext<'a, 'tcx>,
                codegen_unit: CodegenUnit<'tcx>,
+               previous_work_product: Option<PathBuf>,
                symbol_map: Rc<SymbolMap<'tcx>>)
            -> LocalCrateContext<'tcx> {
         unsafe {
@@ -521,7 +553,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
             // crashes if the module identifier is same as other symbols
             // such as a function name in the module.
             // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
-            let llmod_id = format!("{}.rs", codegen_unit.name);
+            let llmod_id = format!("{}.rs", codegen_unit.name());
 
             let (llcx, llmod) = create_context_and_module(&shared.tcx.sess,
                                                           &llmod_id[..]);
@@ -535,6 +567,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
             let local_ccx = LocalCrateContext {
                 llmod: llmod,
                 llcx: llcx,
+                previous_work_product: previous_work_product,
                 codegen_unit: codegen_unit,
                 tn: TypeNames::new(),
                 needs_unwind_cleanup_cache: RefCell::new(FnvHashMap()),
@@ -694,6 +727,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         self.local().llcx
     }
 
+    pub fn previous_work_product(&self) -> Option<&PathBuf> {
+        self.local().previous_work_product.as_ref()
+    }
+
     pub fn codegen_unit(&self) -> &CodegenUnit<'tcx> {
         &self.local().codegen_unit
     }
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index ef7d0ea165d..6bc48546dfa 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -239,7 +239,7 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                     Falling back to on-demand instantiation.",
                     g,
                     TransItem::DropGlue(g).to_raw_string(),
-                    ccx.codegen_unit().name);
+                    ccx.codegen_unit().name());
 
             ccx.stats().n_fallback_instantiations.set(ccx.stats()
                                                          .n_fallback_instantiations
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index fa0a1fdc375..6ac64d32910 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -37,6 +37,8 @@
 #![feature(unicode)]
 #![feature(question_mark)]
 
+use std::path::PathBuf;
+
 extern crate arena;
 extern crate flate;
 extern crate getopts;
@@ -133,6 +135,18 @@ mod value;
 #[derive(Clone)]
 pub struct ModuleTranslation {
     pub name: String,
+    pub symbol_name_hash: u64,
+    pub source: ModuleSource,
+}
+
+#[derive(Clone)]
+pub enum ModuleSource {
+    Preexisting(PathBuf),
+    Translated(ModuleLlvm),
+}
+
+#[derive(Copy, Clone)]
+pub struct ModuleLlvm {
     pub llcx: llvm::ContextRef,
     pub llmod: llvm::ModuleRef,
 }
diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs
index 00c0e911035..96a05f11bfd 100644
--- a/src/librustc_trans/monomorphize.rs
+++ b/src/librustc_trans/monomorphize.rs
@@ -52,7 +52,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         debug!("leaving monomorphic fn {:?}", instance);
         return (val, mono_ty);
     } else {
-        assert!(!ccx.codegen_unit().items.contains_key(&TransItem::Fn(instance)));
+        assert!(!ccx.codegen_unit().contains_item(&TransItem::Fn(instance)));
     }
 
     debug!("monomorphic_fn({:?})", instance);
diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs
index 8073359ede8..12df8bd8370 100644
--- a/src/librustc_trans/partitioning.rs
+++ b/src/librustc_trans/partitioning.rs
@@ -119,12 +119,15 @@
 use collector::InliningMap;
 use llvm;
 use monomorphize;
+use rustc::dep_graph::{DepNode, WorkProductId};
 use rustc::hir::def_id::DefId;
 use rustc::hir::map::DefPathData;
 use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER;
 use rustc::ty::TyCtxt;
 use rustc::ty::item_path::characteristic_def_id_of_type;
 use std::cmp::Ordering;
+use std::hash::{Hash, Hasher, SipHasher};
+use std::sync::Arc;
 use symbol_map::SymbolMap;
 use syntax::ast::NodeId;
 use syntax::parse::token::{self, InternedString};
@@ -140,11 +143,54 @@ pub enum PartitioningStrategy {
 }
 
 pub struct CodegenUnit<'tcx> {
-    pub name: InternedString,
-    pub items: FnvHashMap<TransItem<'tcx>, llvm::Linkage>,
+    name: InternedString,
+    items: FnvHashMap<TransItem<'tcx>, llvm::Linkage>,
 }
 
 impl<'tcx> CodegenUnit<'tcx> {
+    pub fn new(name: InternedString,
+               items: FnvHashMap<TransItem<'tcx>, llvm::Linkage>)
+               -> Self {
+        CodegenUnit {
+            name: name,
+            items: items,
+        }
+    }
+
+    pub fn empty(name: InternedString) -> Self {
+        Self::new(name, FnvHashMap())
+    }
+
+    pub fn contains_item(&self, item: &TransItem<'tcx>) -> bool {
+        self.items.contains_key(item)
+    }
+
+    pub fn name(&self) -> &str {
+        &self.name
+    }
+
+    pub fn items(&self) -> &FnvHashMap<TransItem<'tcx>, llvm::Linkage> {
+        &self.items
+    }
+
+    pub fn work_product_id(&self) -> Arc<WorkProductId> {
+        Arc::new(WorkProductId::PartitionObjectFile(self.name().to_string()))
+    }
+
+    pub fn work_product_dep_node(&self) -> DepNode<DefId> {
+        DepNode::WorkProduct(self.work_product_id())
+    }
+
+    pub fn compute_symbol_name_hash(&self, tcx: TyCtxt, symbol_map: &SymbolMap) -> u64 {
+        let mut state = SipHasher::new();
+        let all_items = self.items_in_deterministic_order(tcx, symbol_map);
+        for (item, _) in all_items {
+            let symbol_name = symbol_map.get(item).unwrap();
+            symbol_name.hash(&mut state);
+        }
+        state.finish()
+    }
+
     pub fn items_in_deterministic_order(&self,
                                         tcx: TyCtxt,
                                         symbol_map: &SymbolMap)
@@ -277,10 +323,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             };
 
             let make_codegen_unit = || {
-                CodegenUnit {
-                    name: codegen_unit_name.clone(),
-                    items: FnvHashMap(),
-                }
+                CodegenUnit::empty(codegen_unit_name.clone())
             };
 
             let mut codegen_unit = codegen_units.entry(codegen_unit_name.clone())
@@ -319,10 +362,8 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     if codegen_units.is_empty() {
         let codegen_unit_name = InternedString::new(FALLBACK_CODEGEN_UNIT);
         codegen_units.entry(codegen_unit_name.clone())
-                     .or_insert_with(|| CodegenUnit {
-                         name: codegen_unit_name.clone(),
-                         items: FnvHashMap(),
-                     });
+                     .or_insert_with(|| CodegenUnit::new(codegen_unit_name.clone(),
+                                                         FnvHashMap()));
     }
 
     PreInliningPartitioning {
@@ -362,10 +403,8 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<
     // we reach the target count
     while codegen_units.len() < target_cgu_count {
         let index = codegen_units.len();
-        codegen_units.push(CodegenUnit {
-            name: numbered_codegen_unit_name(crate_name, index),
-            items: FnvHashMap()
-        });
+        codegen_units.push(
+            CodegenUnit::empty(numbered_codegen_unit_name(crate_name, index)));
     }
 }
 
@@ -381,10 +420,8 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit
             follow_inlining(*root, inlining_map, &mut reachable);
         }
 
-        let mut new_codegen_unit = CodegenUnit {
-            name: codegen_unit.name.clone(),
-            items: FnvHashMap(),
-        };
+        let mut new_codegen_unit =
+            CodegenUnit::empty(codegen_unit.name.clone());
 
         // Add all translation items that are not already there
         for trans_item in reachable {
@@ -560,10 +597,9 @@ fn single_codegen_unit<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         items.insert(trans_item, linkage);
     }
 
-    CodegenUnit {
-        name: numbered_codegen_unit_name(&tcx.crate_name[..], 0),
-        items: items
-    }
+    CodegenUnit::new(
+        numbered_codegen_unit_name(&tcx.crate_name[..], 0),
+        items)
 }
 
 fn numbered_codegen_unit_name(crate_name: &str, index: usize) -> InternedString {
diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs
index fc95d208f32..3afedb49090 100644
--- a/src/librustc_trans/trans_item.rs
+++ b/src/librustc_trans/trans_item.rs
@@ -23,12 +23,12 @@ use glue::DropGlueKind;
 use llvm;
 use monomorphize::{self, Instance};
 use inline;
+use rustc::dep_graph::DepNode;
 use rustc::hir;
 use rustc::hir::map as hir_map;
 use rustc::hir::def_id::DefId;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::subst;
-use rustc::dep_graph::DepNode;
 use rustc_const_eval::fatal_const_eval_err;
 use std::hash::{Hash, Hasher};
 use syntax::ast::{self, NodeId};
@@ -68,16 +68,27 @@ impl<'tcx> Hash for TransItem<'tcx> {
 impl<'a, 'tcx> TransItem<'tcx> {
 
     pub fn define(&self, ccx: &CrateContext<'a, 'tcx>) {
-
         debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}",
                   self.to_string(ccx.tcx()),
                   self.to_raw_string(),
-                  ccx.codegen_unit().name);
+                  ccx.codegen_unit().name());
+
+        // (*) This code executes in the context of a dep-node for the
+        // entire CGU. In some cases, we introduce dep-nodes for
+        // particular items that we are translating (these nodes will
+        // have read edges coming into the CGU node). These smaller
+        // nodes are not needed for correctness -- we always
+        // invalidate an entire CGU at a time -- but they enable
+        // finer-grained testing, since you can write tests that check
+        // that the incoming edges to a particular fn are from a
+        // particular set.
 
         self.register_reads(ccx);
 
         match *self {
             TransItem::Static(node_id) => {
+                let def_id = ccx.tcx().map.local_def_id(node_id);
+                let _task = ccx.tcx().dep_graph.in_task(DepNode::TransCrateItem(def_id)); // (*)
                 let item = ccx.tcx().map.expect_item(node_id);
                 if let hir::ItemStatic(_, m, ref expr) = item.node {
                     match consts::trans_static(&ccx, m, expr, item.id, &item.attrs) {
@@ -93,6 +104,13 @@ impl<'a, 'tcx> TransItem<'tcx> {
                 }
             }
             TransItem::Fn(instance) => {
+                let _task;
+
+                if instance.def.is_local() {
+                    _task = ccx.tcx().dep_graph.in_task(
+                        DepNode::TransCrateItem(instance.def)); // (*)
+                }
+
                 base::trans_instance(&ccx, instance);
             }
             TransItem::DropGlue(dg) => {
@@ -103,7 +121,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
         debug!("END IMPLEMENTING '{} ({})' in cgu {}",
                self.to_string(ccx.tcx()),
                self.to_raw_string(),
-               ccx.codegen_unit().name);
+               ccx.codegen_unit().name());
     }
 
     /// If necessary, creates a subtask for trans'ing a particular item and registers reads on
@@ -152,7 +170,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
         debug!("BEGIN PREDEFINING '{} ({})' in cgu {}",
                self.to_string(ccx.tcx()),
                self.to_raw_string(),
-               ccx.codegen_unit().name);
+               ccx.codegen_unit().name());
 
         let symbol_name = ccx.symbol_map()
                              .get_or_compute(ccx.shared(), *self);
@@ -174,7 +192,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
         debug!("END PREDEFINING '{} ({})' in cgu {}",
                self.to_string(ccx.tcx()),
                self.to_raw_string(),
-               ccx.codegen_unit().name);
+               ccx.codegen_unit().name());
     }
 
     fn predefine_static(ccx: &CrateContext<'a, 'tcx>,
diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs
index 2e90b518432..b58295d47f2 100644
--- a/src/test/run-make/execution-engine/test.rs
+++ b/src/test/run-make/execution-engine/test.rs
@@ -20,6 +20,7 @@ extern crate rustc_metadata;
 extern crate rustc_resolve;
 extern crate rustc_errors;
 extern crate rustc_errors as errors;
+extern crate rustc_trans;
 #[macro_use] extern crate syntax;
 
 use std::ffi::{CStr, CString};
@@ -37,6 +38,7 @@ use rustc::session::build_session;
 use rustc_driver::{driver, abort_on_err};
 use rustc_resolve::MakeGlobMap;
 use rustc_metadata::cstore::CStore;
+use rustc_trans::ModuleSource;
 use libc::c_void;
 
 use rustc_errors::registry::Registry;
@@ -261,7 +263,10 @@ fn compile_program(input: &str, sysroot: PathBuf)
                 .filter_map(|(_, p)| p).collect();
 
             assert_eq!(trans.modules.len(), 1);
-            let llmod = trans.modules[0].llmod;
+            let llmod = match trans.modules[0].source {
+                ModuleSource::Preexisting(_) => unimplemented!(),
+                ModuleSource::Translated(llvm) => llvm.llmod,
+            };
 
             // Workaround because raw pointers do not impl Send
             let modp = llmod as usize;
diff --git a/src/test/run-make/llvm-phase/test.rs b/src/test/run-make/llvm-phase/test.rs
index 402b5ed8355..19e410fef53 100644
--- a/src/test/run-make/llvm-phase/test.rs
+++ b/src/test/run-make/llvm-phase/test.rs
@@ -13,11 +13,13 @@
 extern crate rustc;
 extern crate rustc_driver;
 extern crate rustc_llvm;
+extern crate rustc_trans;
 #[macro_use] extern crate syntax;
 extern crate getopts;
 
 use rustc_driver::{CompilerCalls, Compilation};
 use rustc_driver::driver::CompileController;
+use rustc_trans::ModuleSource;
 use rustc::session::Session;
 use syntax::codemap::FileLoader;
 use std::io;
@@ -51,7 +53,10 @@ impl<'a> CompilerCalls<'a> for JitCalls {
             state.session.abort_if_errors();
             let trans = state.trans.unwrap();
             assert_eq!(trans.modules.len(), 1);
-            let rs_llmod = trans.modules[0].llmod;
+            let rs_llmod = match trans.modules[0].source {
+                ModuleSource::Preexisting(_) => unimplemented!(),
+                ModuleSource::Translated(llvm) => llvm.llmod,
+            };
             unsafe { rustc_llvm::LLVMDumpModule(rs_llmod) };
         });
         cc