about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-03-28 17:36:56 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-04-06 12:42:02 -0400
commitb1e68b9e2d75e66bc866a194b744ddf8502ca129 (patch)
tree61c589cf387b254f6ab6f9c96f5dba5dc8c54c52
parentfe47ca0d0b04f1cd46195b19cbb42602319aaf98 (diff)
downloadrust-b1e68b9e2d75e66bc866a194b744ddf8502ca129.tar.gz
rust-b1e68b9e2d75e66bc866a194b744ddf8502ca129.zip
make an incremental crate
for now, this houses `svh` and the code to check `assert_dep_graph` is
sane
-rw-r--r--mk/crates.mk5
-rw-r--r--src/librustc/hir/svh.rs374
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/middle/cstore.rs1
-rw-r--r--src/librustc_driver/Cargo.toml1
-rw-r--r--src/librustc_driver/driver.rs12
-rw-r--r--src/librustc_driver/lib.rs1
-rw-r--r--src/librustc_incremental/Cargo.toml17
-rw-r--r--src/librustc_incremental/assert_dep_graph.rs (renamed from src/librustc_trans/assert_dep_graph.rs)130
-rw-r--r--src/librustc_incremental/calculate_svh.rs420
-rw-r--r--src/librustc_incremental/lib.rs41
-rw-r--r--src/librustc_metadata/decoder.rs6
-rw-r--r--src/librustc_trans/Cargo.toml1
-rw-r--r--src/librustc_trans/back/link.rs9
-rw-r--r--src/librustc_trans/base.rs5
-rw-r--r--src/librustc_trans/lib.rs2
16 files changed, 584 insertions, 442 deletions
diff --git a/mk/crates.mk b/mk/crates.mk
index 4c06afcae0c..84452945f27 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -58,7 +58,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_
                 rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
                 rustc_data_structures rustc_platform_intrinsics \
                 rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \
-                rustc_const_eval rustc_const_math
+                rustc_const_eval rustc_const_math rustc_incremental
 HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros \
 		flate arena graphviz rbml log serialize
 TOOLS := compiletest rustdoc rustc rustbook error_index_generator
@@ -117,7 +117,8 @@ DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
 DEPS_rustc_privacy := rustc log syntax
 DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \
                     log syntax serialize rustc_llvm rustc_platform_intrinsics \
-                    rustc_const_math rustc_const_eval
+                    rustc_const_math rustc_const_eval rustc_incremental
+DEPS_rustc_incremental := rbml rustc serialize rustc_data_structures
 DEPS_rustc_save_analysis := rustc log syntax
 DEPS_rustc_typeck := rustc syntax rustc_platform_intrinsics rustc_const_math \
                      rustc_const_eval
diff --git a/src/librustc/hir/svh.rs b/src/librustc/hir/svh.rs
index 7ae20f68ad0..08c3d70034a 100644
--- a/src/librustc/hir/svh.rs
+++ b/src/librustc/hir/svh.rs
@@ -47,9 +47,6 @@
 //! Original issue: https://github.com/rust-lang/rust/issues/10207
 
 use std::fmt;
-use std::hash::{Hash, SipHasher, Hasher};
-use hir;
-use hir::intravisit as visit;
 
 #[derive(Clone, PartialEq, Debug)]
 pub struct Svh {
@@ -57,53 +54,16 @@ pub struct Svh {
 }
 
 impl Svh {
-    pub fn new(hash: &str) -> Svh {
+    /// Create a new `Svh` given the hash. If you actually want to
+    /// compute the SVH from some HIR, you want the `calculate_svh`
+    /// function found in `librustc_trans`.
+    pub fn new(hash: String) -> Svh {
         assert!(hash.len() == 16);
-        Svh { hash: hash.to_string() }
+        Svh { hash: hash }
     }
 
-    pub fn as_str<'a>(&'a self) -> &'a str {
-        &self.hash
-    }
-
-    pub fn calculate(crate_disambiguator: &str, krate: &hir::Crate) -> Svh {
-        // FIXME (#14132): This is better than it used to be, but it still not
-        // ideal. We now attempt to hash only the relevant portions of the
-        // Crate AST as well as the top-level crate attributes. (However,
-        // the hashing of the crate attributes should be double-checked
-        // to ensure it is not incorporating implementation artifacts into
-        // the hash that are not otherwise visible.)
-
-        // FIXME: this should use SHA1, not SipHash. SipHash is not built to
-        //        avoid collisions.
-        let mut state = SipHasher::new();
-
-        "crate_disambiguator".hash(&mut state);
-        crate_disambiguator.len().hash(&mut state);
-        crate_disambiguator.hash(&mut state);
-
-        {
-            let mut visit = svh_visitor::make(&mut state, krate);
-            visit::walk_crate(&mut visit, krate);
-        }
-
-        // FIXME (#14132): This hash is still sensitive to e.g. the
-        // spans of the crate Attributes and their underlying
-        // MetaItems; we should make ContentHashable impl for those
-        // types and then use hash_content.  But, since all crate
-        // attributes should appear near beginning of the file, it is
-        // not such a big deal to be sensitive to their spans for now.
-        //
-        // We hash only the MetaItems instead of the entire Attribute
-        // to avoid hashing the AttrId
-        for attr in &krate.attrs {
-            attr.node.value.hash(&mut state);
-        }
-
-        let hash = state.finish();
-        return Svh {
-            hash: (0..64).step_by(4).map(|i| hex(hash >> i)).collect()
-        };
+    pub fn from_hash(hash: u64) -> Svh {
+        return Svh::new((0..64).step_by(4).map(|i| hex(hash >> i)).collect());
 
         fn hex(b: u64) -> char {
             let b = (b & 0xf) as u8;
@@ -114,6 +74,10 @@ impl Svh {
             b as char
         }
     }
+
+    pub fn as_str<'a>(&'a self) -> &'a str {
+        &self.hash
+    }
 }
 
 impl fmt::Display for Svh {
@@ -121,319 +85,3 @@ impl fmt::Display for Svh {
         f.pad(self.as_str())
     }
 }
-
-// FIXME (#14132): Even this SVH computation still has implementation
-// artifacts: namely, the order of item declaration will affect the
-// hash computation, but for many kinds of items the order of
-// declaration should be irrelevant to the ABI.
-
-mod svh_visitor {
-    pub use self::SawExprComponent::*;
-    pub use self::SawStmtComponent::*;
-    use self::SawAbiComponent::*;
-    use syntax::ast::{self, Name, NodeId};
-    use syntax::codemap::Span;
-    use syntax::parse::token;
-    use hir::intravisit as visit;
-    use hir::intravisit::{Visitor, FnKind};
-    use hir::*;
-    use hir;
-
-    use std::hash::{Hash, SipHasher};
-
-    pub struct StrictVersionHashVisitor<'a> {
-        pub krate: &'a Crate,
-        pub st: &'a mut SipHasher,
-    }
-
-    pub fn make<'a>(st: &'a mut SipHasher, krate: &'a Crate) -> StrictVersionHashVisitor<'a> {
-        StrictVersionHashVisitor { st: st, krate: krate }
-    }
-
-    // To off-load the bulk of the hash-computation on #[derive(Hash)],
-    // we define a set of enums corresponding to the content that our
-    // crate visitor will encounter as it traverses the ast.
-    //
-    // The important invariant is that all of the Saw*Component enums
-    // do not carry any Spans, Names, or Idents.
-    //
-    // Not carrying any Names/Idents is the important fix for problem
-    // noted on PR #13948: using the ident.name as the basis for a
-    // hash leads to unstable SVH, because ident.name is just an index
-    // into intern table (i.e. essentially a random address), not
-    // computed from the name content.
-    //
-    // With the below enums, the SVH computation is not sensitive to
-    // artifacts of how rustc was invoked nor of how the source code
-    // was laid out.  (Or at least it is *less* sensitive.)
-
-    // This enum represents the different potential bits of code the
-    // visitor could encounter that could affect the ABI for the crate,
-    // and assigns each a distinct tag to feed into the hash computation.
-    #[derive(Hash)]
-    enum SawAbiComponent<'a> {
-
-        // FIXME (#14132): should we include (some function of)
-        // ident.ctxt as well?
-        SawIdent(token::InternedString),
-        SawStructDef(token::InternedString),
-
-        SawLifetime(token::InternedString),
-        SawLifetimeDef(token::InternedString),
-
-        SawMod,
-        SawForeignItem,
-        SawItem,
-        SawDecl,
-        SawTy,
-        SawGenerics,
-        SawFn,
-        SawTraitItem,
-        SawImplItem,
-        SawStructField,
-        SawVariant,
-        SawExplicitSelf,
-        SawPath,
-        SawBlock,
-        SawPat,
-        SawLocal,
-        SawArm,
-        SawExpr(SawExprComponent<'a>),
-        SawStmt(SawStmtComponent),
-    }
-
-    /// SawExprComponent carries all of the information that we want
-    /// to include in the hash that *won't* be covered by the
-    /// subsequent recursive traversal of the expression's
-    /// substructure by the visitor.
-    ///
-    /// We know every Expr_ variant is covered by a variant because
-    /// `fn saw_expr` maps each to some case below.  Ensuring that
-    /// each variant carries an appropriate payload has to be verified
-    /// by hand.
-    ///
-    /// (However, getting that *exactly* right is not so important
-    /// because the SVH is just a developer convenience; there is no
-    /// guarantee of collision-freedom, hash collisions are just
-    /// (hopefully) unlikely.)
-    #[derive(Hash)]
-    pub enum SawExprComponent<'a> {
-
-        SawExprLoop(Option<token::InternedString>),
-        SawExprField(token::InternedString),
-        SawExprTupField(usize),
-        SawExprBreak(Option<token::InternedString>),
-        SawExprAgain(Option<token::InternedString>),
-
-        SawExprBox,
-        SawExprVec,
-        SawExprCall,
-        SawExprMethodCall,
-        SawExprTup,
-        SawExprBinary(hir::BinOp_),
-        SawExprUnary(hir::UnOp),
-        SawExprLit(ast::LitKind),
-        SawExprCast,
-        SawExprType,
-        SawExprIf,
-        SawExprWhile,
-        SawExprMatch,
-        SawExprClosure,
-        SawExprBlock,
-        SawExprAssign,
-        SawExprAssignOp(hir::BinOp_),
-        SawExprIndex,
-        SawExprPath(Option<usize>),
-        SawExprAddrOf(hir::Mutability),
-        SawExprRet,
-        SawExprInlineAsm(&'a hir::InlineAsm),
-        SawExprStruct,
-        SawExprRepeat,
-    }
-
-    fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
-        match *node {
-            ExprBox(..)              => SawExprBox,
-            ExprVec(..)              => SawExprVec,
-            ExprCall(..)             => SawExprCall,
-            ExprMethodCall(..)       => SawExprMethodCall,
-            ExprTup(..)              => SawExprTup,
-            ExprBinary(op, _, _)     => SawExprBinary(op.node),
-            ExprUnary(op, _)         => SawExprUnary(op),
-            ExprLit(ref lit)         => SawExprLit(lit.node.clone()),
-            ExprCast(..)             => SawExprCast,
-            ExprType(..)             => SawExprType,
-            ExprIf(..)               => SawExprIf,
-            ExprWhile(..)            => SawExprWhile,
-            ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.name.as_str())),
-            ExprMatch(..)            => SawExprMatch,
-            ExprClosure(..)          => SawExprClosure,
-            ExprBlock(..)            => SawExprBlock,
-            ExprAssign(..)           => SawExprAssign,
-            ExprAssignOp(op, _, _)   => SawExprAssignOp(op.node),
-            ExprField(_, name)       => SawExprField(name.node.as_str()),
-            ExprTupField(_, id)      => SawExprTupField(id.node),
-            ExprIndex(..)            => SawExprIndex,
-            ExprPath(ref qself, _)   => SawExprPath(qself.as_ref().map(|q| q.position)),
-            ExprAddrOf(m, _)         => SawExprAddrOf(m),
-            ExprBreak(id)            => SawExprBreak(id.map(|id| id.node.name.as_str())),
-            ExprAgain(id)            => SawExprAgain(id.map(|id| id.node.name.as_str())),
-            ExprRet(..)              => SawExprRet,
-            ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a),
-            ExprStruct(..)           => SawExprStruct,
-            ExprRepeat(..)           => SawExprRepeat,
-        }
-    }
-
-    /// SawStmtComponent is analogous to SawExprComponent, but for statements.
-    #[derive(Hash)]
-    pub enum SawStmtComponent {
-        SawStmtDecl,
-        SawStmtExpr,
-        SawStmtSemi,
-    }
-
-    fn saw_stmt(node: &Stmt_) -> SawStmtComponent {
-        match *node {
-            StmtDecl(..) => SawStmtDecl,
-            StmtExpr(..) => SawStmtExpr,
-            StmtSemi(..) => SawStmtSemi,
-        }
-    }
-
-    impl<'a> Visitor<'a> for StrictVersionHashVisitor<'a> {
-        fn visit_nested_item(&mut self, item: ItemId) {
-            self.visit_item(self.krate.item(item.id))
-        }
-
-        fn visit_variant_data(&mut self, s: &'a VariantData, name: Name,
-                              g: &'a Generics, _: NodeId, _: Span) {
-            SawStructDef(name.as_str()).hash(self.st);
-            visit::walk_generics(self, g);
-            visit::walk_struct_def(self, s)
-        }
-
-        fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) {
-            SawVariant.hash(self.st);
-            // walk_variant does not call walk_generics, so do it here.
-            visit::walk_generics(self, g);
-            visit::walk_variant(self, v, g, item_id)
-        }
-
-        // All of the remaining methods just record (in the hash
-        // SipHasher) that the visitor saw that particular variant
-        // (with its payload), and continue walking as the default
-        // visitor would.
-        //
-        // Some of the implementations have some notes as to how one
-        // might try to make their SVH computation less discerning
-        // (e.g. by incorporating reachability analysis).  But
-        // currently all of their implementations are uniform and
-        // uninteresting.
-        //
-        // (If you edit a method such that it deviates from the
-        // pattern, please move that method up above this comment.)
-
-        fn visit_name(&mut self, _: Span, name: Name) {
-            SawIdent(name.as_str()).hash(self.st);
-        }
-
-        fn visit_lifetime(&mut self, l: &'a Lifetime) {
-            SawLifetime(l.name.as_str()).hash(self.st);
-        }
-
-        fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) {
-            SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st);
-        }
-
-        // We do recursively walk the bodies of functions/methods
-        // (rather than omitting their bodies from the hash) since
-        // monomorphization and cross-crate inlining generally implies
-        // that a change to a crate body will require downstream
-        // crates to be recompiled.
-        fn visit_expr(&mut self, ex: &'a Expr) {
-            SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex)
-        }
-
-        fn visit_stmt(&mut self, s: &'a Stmt) {
-            SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s)
-        }
-
-        fn visit_foreign_item(&mut self, i: &'a ForeignItem) {
-            // FIXME (#14132) ideally we would incorporate privacy (or
-            // perhaps reachability) somewhere here, so foreign items
-            // that do not leak into downstream crates would not be
-            // part of the ABI.
-            SawForeignItem.hash(self.st); visit::walk_foreign_item(self, i)
-        }
-
-        fn visit_item(&mut self, i: &'a Item) {
-            // FIXME (#14132) ideally would incorporate reachability
-            // analysis somewhere here, so items that never leak into
-            // downstream crates (e.g. via monomorphisation or
-            // inlining) would not be part of the ABI.
-            SawItem.hash(self.st); visit::walk_item(self, i)
-        }
-
-        fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) {
-            SawMod.hash(self.st); visit::walk_mod(self, m)
-        }
-
-        fn visit_decl(&mut self, d: &'a Decl) {
-            SawDecl.hash(self.st); visit::walk_decl(self, d)
-        }
-
-        fn visit_ty(&mut self, t: &'a Ty) {
-            SawTy.hash(self.st); visit::walk_ty(self, t)
-        }
-
-        fn visit_generics(&mut self, g: &'a Generics) {
-            SawGenerics.hash(self.st); visit::walk_generics(self, g)
-        }
-
-        fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl,
-                    b: &'a Block, s: Span, _: NodeId) {
-            SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s)
-        }
-
-        fn visit_trait_item(&mut self, ti: &'a TraitItem) {
-            SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti)
-        }
-
-        fn visit_impl_item(&mut self, ii: &'a ImplItem) {
-            SawImplItem.hash(self.st); visit::walk_impl_item(self, ii)
-        }
-
-        fn visit_struct_field(&mut self, s: &'a StructField) {
-            SawStructField.hash(self.st); visit::walk_struct_field(self, s)
-        }
-
-        fn visit_explicit_self(&mut self, es: &'a ExplicitSelf) {
-            SawExplicitSelf.hash(self.st); visit::walk_explicit_self(self, es)
-        }
-
-        fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) {
-            SawPath.hash(self.st); visit::walk_path(self, path)
-        }
-
-        fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) {
-            SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item)
-        }
-
-        fn visit_block(&mut self, b: &'a Block) {
-            SawBlock.hash(self.st); visit::walk_block(self, b)
-        }
-
-        fn visit_pat(&mut self, p: &'a Pat) {
-            SawPat.hash(self.st); visit::walk_pat(self, p)
-        }
-
-        fn visit_local(&mut self, l: &'a Local) {
-            SawLocal.hash(self.st); visit::walk_local(self, l)
-        }
-
-        fn visit_arm(&mut self, a: &'a Arm) {
-            SawArm.hash(self.st); visit::walk_arm(self, a)
-        }
-    }
-}
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index deb20627772..342007d46c5 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -100,6 +100,7 @@ pub mod middle {
     pub mod recursion_limit;
     pub mod resolve_lifetime;
     pub mod stability;
+    pub mod svh;
     pub mod weak_lang_items;
 }
 
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 1f6328187a5..0409451043d 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -28,6 +28,7 @@ use hir::def::{self, Def};
 use middle::lang_items;
 use ty::{self, Ty, TyCtxt, VariantKind};
 use hir::def_id::{DefId, DefIndex};
+use hir::svh::Svh;
 use mir::repr::Mir;
 use mir::mir_map::MirMap;
 use session::Session;
diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml
index bac5900f3ed..4533946d26e 100644
--- a/src/librustc_driver/Cargo.toml
+++ b/src/librustc_driver/Cargo.toml
@@ -23,6 +23,7 @@ rustc_mir = { path = "../librustc_mir" }
 rustc_plugin = { path = "../librustc_plugin" }
 rustc_passes = { path = "../librustc_passes" }
 rustc_privacy = { path = "../librustc_privacy" }
+rustc_incremental = { path = "../librustc_incremental" }
 rustc_resolve = { path = "../librustc_resolve" }
 rustc_save_analysis = { path = "../librustc_save_analysis" }
 rustc_trans = { path = "../librustc_trans" }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index a4d34aaf652..f35d5f25962 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -24,6 +24,7 @@ use rustc::util::common::time;
 use rustc::util::nodemap::NodeSet;
 use rustc_back::sha2::{Sha256, Digest};
 use rustc_borrowck as borrowck;
+use rustc_incremental;
 use rustc_resolve as resolve;
 use rustc_metadata::macro_import;
 use rustc_metadata::creader::LocalCrateReader;
@@ -952,9 +953,16 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>,
         passes.run_passes(tcx, &mut mir_map);
     });
 
+    let translation =
+        time(time_passes,
+             "translation",
+             move || trans::trans_crate(tcx, &mir_map, analysis));
+
     time(time_passes,
-         "translation",
-         move || trans::trans_crate(tcx, &mir_map, analysis))
+         "assert dep graph",
+         move || rustc_incremental::assert_dep_graph(tcx));
+
+    translation
 }
 
 /// Run LLVM itself, producing a bitcode file, assembly file or object file
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index b4b53d30e3c..85807dec0ff 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -45,6 +45,7 @@ extern crate rustc_passes;
 extern crate rustc_lint;
 extern crate rustc_plugin;
 extern crate rustc_privacy;
+extern crate rustc_incremental;
 extern crate rustc_metadata;
 extern crate rustc_mir;
 extern crate rustc_resolve;
diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml
new file mode 100644
index 00000000000..1a8b5a483e0
--- /dev/null
+++ b/src/librustc_incremental/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_serialize"
+version = "0.0.0"
+
+[lib]
+name = "rustc_serialize"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+graphviz = { path = "../libgraphviz" }
+rbml = { path = "../librbml" }
+rustc = { path = "../librustc" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_front = { path = "../librustc_front" }
+serialize = { path = "../libserialize" }
diff --git a/src/librustc_trans/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs
index 932f66f17cc..88d8ed8d581 100644
--- a/src/librustc_trans/assert_dep_graph.rs
+++ b/src/librustc_incremental/assert_dep_graph.rs
@@ -13,12 +13,17 @@
 //! will dump graphs in graphviz form to disk, and it searches for
 //! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]`
 //! annotations. These annotations can be used to test whether paths
-//! exist in the graph. We report errors on each
-//! `rustc_if_this_changed` annotation. If a path exists in all
-//! cases, then we would report "all path(s) exist". Otherwise, we
-//! report: "no path to `foo`" for each case where no path exists.
-//! `compile-fail` tests can then be used to check when paths exist or
-//! do not.
+//! exist in the graph. These checks run after trans, so they view the
+//! the final state of the dependency graph. Note that there are
+//! similar assertions found in `persist::dirty_clean` which check the
+//! **initial** state of the dependency graph, just after it has been
+//! loaded from disk.
+//!
+//! In this code, we report errors on each `rustc_if_this_changed`
+//! annotation. If a path exists in all cases, then we would report
+//! "all path(s) exist". Otherwise, we report: "no path to `foo`" for
+//! each case where no path exists.  `compile-fail` tests can then be
+//! used to check when paths exist or do not.
 //!
 //! The full form of the `rustc_if_this_changed` annotation is
 //! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and
@@ -61,7 +66,7 @@ const ID: &'static str = "id";
 pub fn assert_dep_graph(tcx: &TyCtxt) {
     let _ignore = tcx.dep_graph.in_ignore();
 
-    if tcx.sess.opts.dump_dep_graph {
+    if tcx.sess.opts.debugging_opts.dump_dep_graph {
         dump_graph(tcx);
     }
 
@@ -74,14 +79,23 @@ pub fn assert_dep_graph(tcx: &TyCtxt) {
         (visitor.if_this_changed, visitor.then_this_would_need)
     };
 
+    if !if_this_changed.is_empty() || !then_this_would_need.is_empty() {
+        assert!(tcx.sess.opts.debugging_opts.query_dep_graph,
+                "cannot use the `#[{}]` or `#[{}]` annotations \
+                 without supplying `-Z query-dep-graph`",
+                IF_THIS_CHANGED, THEN_THIS_WOULD_NEED);
+    }
+
     // Check paths.
     check_paths(tcx, &if_this_changed, &then_this_would_need);
 }
 
-type SourceHashMap = FnvHashMap<InternedString,
-                                FnvHashSet<(Span, DefId, DepNode)>>;
-type TargetHashMap = FnvHashMap<InternedString,
-                                FnvHashSet<(Span, InternedString, ast::NodeId, DepNode)>>;
+type SourceHashMap =
+    FnvHashMap<InternedString,
+               FnvHashSet<(Span, DefId, DepNode<DefId>)>>;
+type TargetHashMap =
+    FnvHashMap<InternedString,
+               FnvHashSet<(Span, InternedString, ast::NodeId, DepNode<DefId>)>>;
 
 struct IfThisChanged<'a, 'tcx:'a> {
     tcx: &'a TyCtxt<'tcx>,
@@ -124,34 +138,21 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
                         }
                     }
                 }
-                let dep_node_str = dep_node_interned.as_ref().map(|s| &**s);
-                macro_rules! match_depnode_name {
-                    ($input:expr, $def_id:expr, match { $($variant:ident,)* } else $y:expr) => {
-                        match $input {
-                            $(Some(stringify!($variant)) => DepNode::$variant($def_id),)*
-                            _ => $y
+                let dep_node = match dep_node_interned {
+                    Some(ref n) => {
+                        match DepNode::from_label_string(&n[..], def_id) {
+                            Ok(n) => n,
+                            Err(()) => {
+                                self.tcx.sess.span_fatal(
+                                    attr.span,
+                                    &format!("unrecognized DepNode variant {:?}", n));
+                            }
                         }
                     }
-                }
-                let dep_node = match_depnode_name! {
-                    dep_node_str, def_id, match {
-                        CollectItem,
-                        BorrowCheck,
-                        TransCrateItem,
-                        TypeckItemType,
-                        TypeckItemBody,
-                        ImplOrTraitItems,
-                        ItemSignature,
-                        FieldTy,
-                        TraitItemDefIds,
-                        InherentImpls,
-                        ImplItems,
-                        TraitImpls,
-                        ReprHints,
-                    } else {
+                    None => {
                         self.tcx.sess.span_fatal(
                             attr.span,
-                            &format!("unrecognized DepNode variant {:?}", dep_node_str));
+                            &format!("missing DepNode variant"));
                     }
                 };
                 let id = id.unwrap_or(InternedString::new(ID));
@@ -194,7 +195,7 @@ fn check_paths(tcx: &TyCtxt,
         };
 
         for &(_, source_def_id, source_dep_node) in sources {
-            let dependents = query.dependents(source_dep_node);
+            let dependents = query.transitive_dependents(source_dep_node);
             for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
                 if !dependents.contains(&target_dep_node) {
                     tcx.sess.span_err(
@@ -251,33 +252,34 @@ fn dump_graph(tcx: &TyCtxt) {
     }
 }
 
-pub struct GraphvizDepGraph(FnvHashSet<DepNode>, Vec<(DepNode, DepNode)>);
+pub struct GraphvizDepGraph(FnvHashSet<DepNode<DefId>>,
+                            Vec<(DepNode<DefId>, DepNode<DefId>)>);
 
 impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph {
-    type Node = DepNode;
-    type Edge = (DepNode, DepNode);
-    fn nodes(&self) -> dot::Nodes<DepNode> {
+    type Node = DepNode<DefId>;
+    type Edge = (DepNode<DefId>, DepNode<DefId>);
+    fn nodes(&self) -> dot::Nodes<DepNode<DefId>> {
         let nodes: Vec<_> = self.0.iter().cloned().collect();
         nodes.into_cow()
     }
-    fn edges(&self) -> dot::Edges<(DepNode, DepNode)> {
+    fn edges(&self) -> dot::Edges<(DepNode<DefId>, DepNode<DefId>)> {
         self.1[..].into_cow()
     }
-    fn source(&self, edge: &(DepNode, DepNode)) -> DepNode {
+    fn source(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
         edge.0
     }
-    fn target(&self, edge: &(DepNode, DepNode)) -> DepNode {
+    fn target(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
         edge.1
     }
 }
 
 impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
-    type Node = DepNode;
-    type Edge = (DepNode, DepNode);
+    type Node = DepNode<DefId>;
+    type Edge = (DepNode<DefId>, DepNode<DefId>);
     fn graph_id(&self) -> dot::Id {
         dot::Id::new("DependencyGraph").unwrap()
     }
-    fn node_id(&self, n: &DepNode) -> dot::Id {
+    fn node_id(&self, n: &DepNode<DefId>) -> dot::Id {
         let s: String =
             format!("{:?}", n).chars()
                               .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
@@ -285,7 +287,7 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
         debug!("n={:?} s={:?}", n, s);
         dot::Id::new(s).unwrap()
     }
-    fn node_label(&self, n: &DepNode) -> dot::LabelText {
+    fn node_label(&self, n: &DepNode<DefId>) -> dot::LabelText {
         dot::LabelText::label(format!("{:?}", n))
     }
 }
@@ -293,7 +295,9 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
 // Given an optional filter like `"x,y,z"`, returns either `None` (no
 // filter) or the set of nodes whose labels contain all of those
 // substrings.
-fn node_set(query: &DepGraphQuery, filter: &str) -> Option<FnvHashSet<DepNode>> {
+fn node_set(query: &DepGraphQuery<DefId>, filter: &str)
+            -> Option<FnvHashSet<DepNode<DefId>>>
+{
     debug!("node_set(filter={:?})", filter);
 
     if filter.trim().is_empty() {
@@ -313,10 +317,10 @@ fn node_set(query: &DepGraphQuery, filter: &str) -> Option<FnvHashSet<DepNode>>
         .collect())
 }
 
-fn filter_nodes(query: &DepGraphQuery,
-                sources: &Option<FnvHashSet<DepNode>>,
-                targets: &Option<FnvHashSet<DepNode>>)
-                -> FnvHashSet<DepNode>
+fn filter_nodes(query: &DepGraphQuery<DefId>,
+                sources: &Option<FnvHashSet<DepNode<DefId>>>,
+                targets: &Option<FnvHashSet<DepNode<DefId>>>)
+                -> FnvHashSet<DepNode<DefId>>
 {
     if let &Some(ref sources) = sources {
         if let &Some(ref targets) = targets {
@@ -331,10 +335,10 @@ fn filter_nodes(query: &DepGraphQuery,
     }
 }
 
-fn walk_nodes(query: &DepGraphQuery,
-              starts: &FnvHashSet<DepNode>,
+fn walk_nodes(query: &DepGraphQuery<DefId>,
+              starts: &FnvHashSet<DepNode<DefId>>,
               direction: Direction)
-              -> FnvHashSet<DepNode>
+              -> FnvHashSet<DepNode<DefId>>
 {
     let mut set = FnvHashSet();
     for start in starts {
@@ -355,10 +359,10 @@ fn walk_nodes(query: &DepGraphQuery,
     set
 }
 
-fn walk_between(query: &DepGraphQuery,
-                sources: &FnvHashSet<DepNode>,
-                targets: &FnvHashSet<DepNode>)
-                -> FnvHashSet<DepNode>
+fn walk_between(query: &DepGraphQuery<DefId>,
+                sources: &FnvHashSet<DepNode<DefId>>,
+                targets: &FnvHashSet<DepNode<DefId>>)
+                -> FnvHashSet<DepNode<DefId>>
 {
     // This is a bit tricky. We want to include a node only if it is:
     // (a) reachable from a source and (b) will reach a target. And we
@@ -386,7 +390,7 @@ fn walk_between(query: &DepGraphQuery,
                 })
                 .collect();
 
-    fn recurse(query: &DepGraphQuery,
+    fn recurse(query: &DepGraphQuery<DefId>,
                node_states: &mut [State],
                node: NodeIndex)
                -> bool
@@ -423,9 +427,9 @@ fn walk_between(query: &DepGraphQuery,
     }
 }
 
-fn filter_edges(query: &DepGraphQuery,
-                nodes: &FnvHashSet<DepNode>)
-                -> Vec<(DepNode, DepNode)>
+fn filter_edges(query: &DepGraphQuery<DefId>,
+                nodes: &FnvHashSet<DepNode<DefId>>)
+                -> Vec<(DepNode<DefId>, DepNode<DefId>)>
 {
     query.edges()
          .into_iter()
diff --git a/src/librustc_incremental/calculate_svh.rs b/src/librustc_incremental/calculate_svh.rs
new file mode 100644
index 00000000000..158db34ee8a
--- /dev/null
+++ b/src/librustc_incremental/calculate_svh.rs
@@ -0,0 +1,420 @@
+// Copyright 2012-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.
+
+//! Calculation of a Strict Version Hash for crates.  For a length
+//! comment explaining the general idea, see `librustc/middle/svh.rs`.
+
+use std::hash::{Hash, SipHasher, Hasher};
+use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
+use rustc::hir::svh::Svh;
+use rustc::ty;
+use rustc_front::intravisit::{self, Visitor};
+
+use self::svh_visitor::StrictVersionHashVisitor;
+
+pub trait SvhCalculate {
+    /// Calculate the SVH for an entire krate.
+    fn calculate_krate_hash(&self) -> Svh;
+
+    /// Calculate the SVH for a particular item.
+    fn calculate_item_hash(&self, def_id: DefId) -> u64;
+}
+
+impl<'tcx> SvhCalculate for ty::TyCtxt<'tcx> {
+    fn calculate_krate_hash(&self) -> Svh {
+        // FIXME (#14132): This is better than it used to be, but it still not
+        // ideal. We now attempt to hash only the relevant portions of the
+        // Crate AST as well as the top-level crate attributes. (However,
+        // the hashing of the crate attributes should be double-checked
+        // to ensure it is not incorporating implementation artifacts into
+        // the hash that are not otherwise visible.)
+
+        let crate_disambiguator = self.sess.crate_disambiguator.get();
+        let krate = self.map.krate();
+
+        // FIXME: this should use SHA1, not SipHash. SipHash is not built to
+        //        avoid collisions.
+        let mut state = SipHasher::new();
+        debug!("state: {:?}", state);
+
+        "crate_disambiguator".hash(&mut state);
+        crate_disambiguator.as_str().len().hash(&mut state);
+        crate_disambiguator.as_str().hash(&mut state);
+
+        debug!("crate_disambiguator: {:?}", crate_disambiguator.as_str());
+        debug!("state: {:?}", state);
+
+        {
+            let mut visit = StrictVersionHashVisitor::new(&mut state, self);
+            krate.visit_all_items(&mut visit);
+        }
+
+        // FIXME (#14132): This hash is still sensitive to e.g. the
+        // spans of the crate Attributes and their underlying
+        // MetaItems; we should make ContentHashable impl for those
+        // types and then use hash_content.  But, since all crate
+        // attributes should appear near beginning of the file, it is
+        // not such a big deal to be sensitive to their spans for now.
+        //
+        // We hash only the MetaItems instead of the entire Attribute
+        // to avoid hashing the AttrId
+        for attr in &krate.attrs {
+            debug!("krate attr {:?}", attr);
+            attr.node.value.hash(&mut state);
+        }
+
+        Svh::from_hash(state.finish())
+    }
+
+    fn calculate_item_hash(&self, def_id: DefId) -> u64 {
+        assert!(def_id.is_local());
+
+        let mut state = SipHasher::new();
+
+        {
+            let mut visit = StrictVersionHashVisitor::new(&mut state, self);
+            if def_id.index == CRATE_DEF_INDEX {
+                // the crate root itself is not registered in the map
+                // as an item, so we have to fetch it this way
+                let krate = self.map.krate();
+                intravisit::walk_crate(&mut visit, krate);
+            } else {
+                let node_id = self.map.as_local_node_id(def_id).unwrap();
+                visit.visit_item(self.map.expect_item(node_id));
+            }
+        }
+
+        state.finish()
+    }
+}
+
+// FIXME (#14132): Even this SVH computation still has implementation
+// artifacts: namely, the order of item declaration will affect the
+// hash computation, but for many kinds of items the order of
+// declaration should be irrelevant to the ABI.
+
+mod svh_visitor {
+    pub use self::SawExprComponent::*;
+    pub use self::SawStmtComponent::*;
+    use self::SawAbiComponent::*;
+    use syntax::ast::{self, Name, NodeId};
+    use syntax::codemap::Span;
+    use syntax::parse::token;
+    use rustc::ty;
+    use rustc_front::intravisit as visit;
+    use rustc_front::intravisit::{Visitor, FnKind};
+    use rustc_front::hir::*;
+    use rustc_front::hir;
+
+    use std::hash::{Hash, SipHasher};
+
+    pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> {
+        pub tcx: &'a ty::TyCtxt<'tcx>,
+        pub st: &'a mut SipHasher,
+    }
+
+    impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> {
+        pub fn new(st: &'a mut SipHasher,
+                   tcx: &'a ty::TyCtxt<'tcx>)
+                   -> Self {
+            StrictVersionHashVisitor { st: st, tcx: tcx }
+        }
+    }
+
+    // To off-load the bulk of the hash-computation on #[derive(Hash)],
+    // we define a set of enums corresponding to the content that our
+    // crate visitor will encounter as it traverses the ast.
+    //
+    // The important invariant is that all of the Saw*Component enums
+    // do not carry any Spans, Names, or Idents.
+    //
+    // Not carrying any Names/Idents is the important fix for problem
+    // noted on PR #13948: using the ident.name as the basis for a
+    // hash leads to unstable SVH, because ident.name is just an index
+    // into intern table (i.e. essentially a random address), not
+    // computed from the name content.
+    //
+    // With the below enums, the SVH computation is not sensitive to
+    // artifacts of how rustc was invoked nor of how the source code
+    // was laid out.  (Or at least it is *less* sensitive.)
+
+    // This enum represents the different potential bits of code the
+    // visitor could encounter that could affect the ABI for the crate,
+    // and assigns each a distinct tag to feed into the hash computation.
+    #[derive(Hash)]
+    enum SawAbiComponent<'a> {
+
+        // FIXME (#14132): should we include (some function of)
+        // ident.ctxt as well?
+        SawIdent(token::InternedString),
+        SawStructDef(token::InternedString),
+
+        SawLifetime(token::InternedString),
+        SawLifetimeDef(token::InternedString),
+
+        SawMod,
+        SawForeignItem,
+        SawItem,
+        SawDecl,
+        SawTy,
+        SawGenerics,
+        SawFn,
+        SawTraitItem,
+        SawImplItem,
+        SawStructField,
+        SawVariant,
+        SawExplicitSelf,
+        SawPath,
+        SawBlock,
+        SawPat,
+        SawLocal,
+        SawArm,
+        SawExpr(SawExprComponent<'a>),
+        SawStmt(SawStmtComponent),
+    }
+
+    /// SawExprComponent carries all of the information that we want
+    /// to include in the hash that *won't* be covered by the
+    /// subsequent recursive traversal of the expression's
+    /// substructure by the visitor.
+    ///
+    /// We know every Expr_ variant is covered by a variant because
+    /// `fn saw_expr` maps each to some case below.  Ensuring that
+    /// each variant carries an appropriate payload has to be verified
+    /// by hand.
+    ///
+    /// (However, getting that *exactly* right is not so important
+    /// because the SVH is just a developer convenience; there is no
+    /// guarantee of collision-freedom, hash collisions are just
+    /// (hopefully) unlikely.)
+    #[derive(Hash)]
+    pub enum SawExprComponent<'a> {
+
+        SawExprLoop(Option<token::InternedString>),
+        SawExprField(token::InternedString),
+        SawExprTupField(usize),
+        SawExprBreak(Option<token::InternedString>),
+        SawExprAgain(Option<token::InternedString>),
+
+        SawExprBox,
+        SawExprVec,
+        SawExprCall,
+        SawExprMethodCall,
+        SawExprTup,
+        SawExprBinary(hir::BinOp_),
+        SawExprUnary(hir::UnOp),
+        SawExprLit(ast::LitKind),
+        SawExprCast,
+        SawExprType,
+        SawExprIf,
+        SawExprWhile,
+        SawExprMatch,
+        SawExprClosure,
+        SawExprBlock,
+        SawExprAssign,
+        SawExprAssignOp(hir::BinOp_),
+        SawExprIndex,
+        SawExprPath(Option<usize>),
+        SawExprAddrOf(hir::Mutability),
+        SawExprRet,
+        SawExprInlineAsm(&'a hir::InlineAsm),
+        SawExprStruct,
+        SawExprRepeat,
+    }
+
+    fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
+        match *node {
+            ExprBox(..)              => SawExprBox,
+            ExprVec(..)              => SawExprVec,
+            ExprCall(..)             => SawExprCall,
+            ExprMethodCall(..)       => SawExprMethodCall,
+            ExprTup(..)              => SawExprTup,
+            ExprBinary(op, _, _)     => SawExprBinary(op.node),
+            ExprUnary(op, _)         => SawExprUnary(op),
+            ExprLit(ref lit)         => SawExprLit(lit.node.clone()),
+            ExprCast(..)             => SawExprCast,
+            ExprType(..)             => SawExprType,
+            ExprIf(..)               => SawExprIf,
+            ExprWhile(..)            => SawExprWhile,
+            ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.name.as_str())),
+            ExprMatch(..)            => SawExprMatch,
+            ExprClosure(..)          => SawExprClosure,
+            ExprBlock(..)            => SawExprBlock,
+            ExprAssign(..)           => SawExprAssign,
+            ExprAssignOp(op, _, _)   => SawExprAssignOp(op.node),
+            ExprField(_, name)       => SawExprField(name.node.as_str()),
+            ExprTupField(_, id)      => SawExprTupField(id.node),
+            ExprIndex(..)            => SawExprIndex,
+            ExprPath(ref qself, _)   => SawExprPath(qself.as_ref().map(|q| q.position)),
+            ExprAddrOf(m, _)         => SawExprAddrOf(m),
+            ExprBreak(id)            => SawExprBreak(id.map(|id| id.node.name.as_str())),
+            ExprAgain(id)            => SawExprAgain(id.map(|id| id.node.name.as_str())),
+            ExprRet(..)              => SawExprRet,
+            ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a),
+            ExprStruct(..)           => SawExprStruct,
+            ExprRepeat(..)           => SawExprRepeat,
+        }
+    }
+
+    /// SawStmtComponent is analogous to SawExprComponent, but for statements.
+    #[derive(Hash)]
+    pub enum SawStmtComponent {
+        SawStmtDecl,
+        SawStmtExpr,
+        SawStmtSemi,
+    }
+
+    fn saw_stmt(node: &Stmt_) -> SawStmtComponent {
+        match *node {
+            StmtDecl(..) => SawStmtDecl,
+            StmtExpr(..) => SawStmtExpr,
+            StmtSemi(..) => SawStmtSemi,
+        }
+    }
+
+    impl<'a, 'tcx> Visitor<'a> for StrictVersionHashVisitor<'a, 'tcx> {
+        fn visit_nested_item(&mut self, item: ItemId) {
+            debug!("visit_nested_item: {:?} st={:?}", item, self.st);
+            let def_path = self.tcx.map.def_path_from_id(item.id);
+            def_path.hash(self.st);
+        }
+
+        fn visit_variant_data(&mut self, s: &'a VariantData, name: Name,
+                              g: &'a Generics, _: NodeId, _: Span) {
+            SawStructDef(name.as_str()).hash(self.st);
+            visit::walk_generics(self, g);
+            visit::walk_struct_def(self, s)
+        }
+
+        fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) {
+            SawVariant.hash(self.st);
+            // walk_variant does not call walk_generics, so do it here.
+            visit::walk_generics(self, g);
+            visit::walk_variant(self, v, g, item_id)
+        }
+
+        // All of the remaining methods just record (in the hash
+        // SipHasher) that the visitor saw that particular variant
+        // (with its payload), and continue walking as the default
+        // visitor would.
+        //
+        // Some of the implementations have some notes as to how one
+        // might try to make their SVH computation less discerning
+        // (e.g. by incorporating reachability analysis).  But
+        // currently all of their implementations are uniform and
+        // uninteresting.
+        //
+        // (If you edit a method such that it deviates from the
+        // pattern, please move that method up above this comment.)
+
+        fn visit_name(&mut self, _: Span, name: Name) {
+            SawIdent(name.as_str()).hash(self.st);
+        }
+
+        fn visit_lifetime(&mut self, l: &'a Lifetime) {
+            SawLifetime(l.name.as_str()).hash(self.st);
+        }
+
+        fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) {
+            SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st);
+        }
+
+        // We do recursively walk the bodies of functions/methods
+        // (rather than omitting their bodies from the hash) since
+        // monomorphization and cross-crate inlining generally implies
+        // that a change to a crate body will require downstream
+        // crates to be recompiled.
+        fn visit_expr(&mut self, ex: &'a Expr) {
+            SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex)
+        }
+
+        fn visit_stmt(&mut self, s: &'a Stmt) {
+            SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s)
+        }
+
+        fn visit_foreign_item(&mut self, i: &'a ForeignItem) {
+            // FIXME (#14132) ideally we would incorporate privacy (or
+            // perhaps reachability) somewhere here, so foreign items
+            // that do not leak into downstream crates would not be
+            // part of the ABI.
+            SawForeignItem.hash(self.st); visit::walk_foreign_item(self, i)
+        }
+
+        fn visit_item(&mut self, i: &'a Item) {
+            debug!("visit_item: {:?} st={:?}", i, self.st);
+            // FIXME (#14132) ideally would incorporate reachability
+            // analysis somewhere here, so items that never leak into
+            // downstream crates (e.g. via monomorphisation or
+            // inlining) would not be part of the ABI.
+            SawItem.hash(self.st); visit::walk_item(self, i)
+        }
+
+        fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) {
+            SawMod.hash(self.st); visit::walk_mod(self, m)
+        }
+
+        fn visit_decl(&mut self, d: &'a Decl) {
+            SawDecl.hash(self.st); visit::walk_decl(self, d)
+        }
+
+        fn visit_ty(&mut self, t: &'a Ty) {
+            SawTy.hash(self.st); visit::walk_ty(self, t)
+        }
+
+        fn visit_generics(&mut self, g: &'a Generics) {
+            SawGenerics.hash(self.st); visit::walk_generics(self, g)
+        }
+
+        fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl,
+                    b: &'a Block, s: Span, _: NodeId) {
+            SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s)
+        }
+
+        fn visit_trait_item(&mut self, ti: &'a TraitItem) {
+            SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti)
+        }
+
+        fn visit_impl_item(&mut self, ii: &'a ImplItem) {
+            SawImplItem.hash(self.st); visit::walk_impl_item(self, ii)
+        }
+
+        fn visit_struct_field(&mut self, s: &'a StructField) {
+            SawStructField.hash(self.st); visit::walk_struct_field(self, s)
+        }
+
+        fn visit_explicit_self(&mut self, es: &'a ExplicitSelf) {
+            SawExplicitSelf.hash(self.st); visit::walk_explicit_self(self, es)
+        }
+
+        fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) {
+            SawPath.hash(self.st); visit::walk_path(self, path)
+        }
+
+        fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) {
+            SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item)
+        }
+
+        fn visit_block(&mut self, b: &'a Block) {
+            SawBlock.hash(self.st); visit::walk_block(self, b)
+        }
+
+        fn visit_pat(&mut self, p: &'a Pat) {
+            SawPat.hash(self.st); visit::walk_pat(self, p)
+        }
+
+        fn visit_local(&mut self, l: &'a Local) {
+            SawLocal.hash(self.st); visit::walk_local(self, l)
+        }
+
+        fn visit_arm(&mut self, a: &'a Arm) {
+            SawArm.hash(self.st); visit::walk_arm(self, a)
+        }
+    }
+}
diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs
new file mode 100644
index 00000000000..3af8fe5cc5f
--- /dev/null
+++ b/src/librustc_incremental/lib.rs
@@ -0,0 +1,41 @@
+// Copyright 2012-2013 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.
+
+//! Support for serializing the dep-graph and reloading it.
+
+#![crate_name = "rustc_incremental"]
+#![unstable(feature = "rustc_private", issue = "27812")]
+#![crate_type = "dylib"]
+#![crate_type = "rlib"]
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+      html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![cfg_attr(not(stage0), deny(warnings))]
+
+#![feature(rustc_private)]
+#![feature(staged_api)]
+
+extern crate graphviz;
+extern crate rbml;
+extern crate rustc;
+extern crate rustc_data_structures;
+extern crate serialize as rustc_serialize;
+
+#[macro_use] extern crate log;
+#[macro_use] extern crate syntax;
+
+mod assert_dep_graph;
+mod calculate_svh;
+mod persist;
+
+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;
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 6b53edbbff1..bb95104ffea 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -1242,7 +1242,7 @@ pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
 
     reader::tagged_docs(depsdoc, tag_crate_dep).enumerate().map(|(crate_num, depdoc)| {
         let name = docstr(depdoc, tag_crate_dep_crate_name);
-        let hash = Svh::new(&docstr(depdoc, tag_crate_dep_hash));
+        let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash));
         let doc = reader::get_doc(depdoc, tag_crate_dep_explicitly_linked);
         let explicitly_linked = reader::doc_as_u8(doc) != 0;
         CrateDep {
@@ -1266,14 +1266,14 @@ fn list_crate_deps(data: &[u8], out: &mut io::Write) -> io::Result<()> {
 pub fn maybe_get_crate_hash(data: &[u8]) -> Option<Svh> {
     let cratedoc = rbml::Doc::new(data);
     reader::maybe_get_doc(cratedoc, tag_crate_hash).map(|doc| {
-        Svh::new(doc.as_str_slice())
+        Svh::new(doc.as_str_slice().to_string())
     })
 }
 
 pub fn get_crate_hash(data: &[u8]) -> Svh {
     let cratedoc = rbml::Doc::new(data);
     let hashdoc = reader::get_doc(cratedoc, tag_crate_hash);
-    Svh::new(hashdoc.as_str_slice())
+    Svh::new(hashdoc.as_str_slice().to_string())
 }
 
 pub fn maybe_get_crate_name(data: &[u8]) -> Option<&str> {
diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml
index ea4cef03b70..ccb430fbb78 100644
--- a/src/librustc_trans/Cargo.toml
+++ b/src/librustc_trans/Cargo.toml
@@ -18,6 +18,7 @@ rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_incremental = { path = "../librustc_incremental" }
 rustc_llvm = { path = "../librustc_llvm" }
 rustc_mir = { path = "../librustc_mir" }
 rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 130499603e7..1d15e67651a 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -13,7 +13,6 @@ use super::linker::{Linker, GnuLinker, MsvcLinker};
 use super::rpath::RPathConfig;
 use super::rpath;
 use super::msvc;
-use super::svh::Svh;
 use session::config;
 use session::config::NoDebugInfo;
 use session::config::{OutputFilenames, Input, OutputType};
@@ -26,8 +25,10 @@ use middle::dependency_format::Linkage;
 use CrateTranslation;
 use util::common::time;
 use util::fs::fix_windows_verbatim_for_gcc;
+use rustc::ty::TyCtxt;
 use rustc_back::tempdir::TempDir;
 
+use rustc_incremental::SvhCalculate;
 use std::ascii;
 use std::char;
 use std::env;
@@ -122,15 +123,15 @@ pub fn find_crate_name(sess: Option<&Session>,
     }
 
     "rust_out".to_string()
+
 }
 
-pub fn build_link_meta(sess: &Session,
-                       krate: &hir::Crate,
+pub fn build_link_meta(tcx: &TyCtxt,
                        name: &str)
                        -> LinkMeta {
     let r = LinkMeta {
         crate_name: name.to_owned(),
-        crate_hash: Svh::calculate(&sess.crate_disambiguator.get().as_str(), krate),
+        crate_hash: tcx.calculate_krate_hash(),
     };
     info!("{:?}", r);
     return r;
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index c8ed4e629e4..17230eff6e6 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -54,7 +54,6 @@ use session::Session;
 use _match;
 use abi::{self, Abi, FnType};
 use adt;
-use assert_dep_graph;
 use attributes;
 use build::*;
 use builder::{Builder, noname};
@@ -2730,7 +2729,7 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
         }
     }
 
-    let link_meta = link::build_link_meta(&tcx.sess, krate, name);
+    let link_meta = link::build_link_meta(&tcx, name);
 
     let codegen_units = tcx.sess.opts.cg.codegen_units;
     let shared_ccx = SharedCrateContext::new(&link_meta.crate_name,
@@ -2856,8 +2855,6 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
     };
     let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
 
-    assert_dep_graph::assert_dep_graph(tcx);
-
     CrateTranslation {
         modules: modules,
         metadata_module: metadata_module,
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index 19a172e7f10..cb421b6be47 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -46,6 +46,7 @@ extern crate libc;
 #[macro_use] extern crate rustc;
 extern crate rustc_back;
 extern crate rustc_data_structures;
+extern crate rustc_incremental;
 pub extern crate rustc_llvm as llvm;
 extern crate rustc_mir;
 extern crate rustc_platform_intrinsics as intrinsics;
@@ -85,7 +86,6 @@ mod macros;
 mod abi;
 mod adt;
 mod asm;
-mod assert_dep_graph;
 mod attributes;
 mod base;
 mod basic_block;