diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index f76f9279e5b..071f233b188 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -16,6 +16,7 @@ use session::config::OutputType; use std::cell::{Ref, RefCell}; use std::hash::Hash; use std::rc::Rc; +use ty::TyCtxt; use util::common::{ProfileQueriesMsg, profq_msg}; use ich::Fingerprint; @@ -28,6 +29,7 @@ use super::edges::{self, DepGraphEdges}; use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use super::prev::PreviousDepGraph; + #[derive(Clone)] pub struct DepGraph { data: Option>, @@ -62,8 +64,7 @@ impl DepNodeIndex { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum DepNodeColor { Red, - Green, - Gray + Green(DepNodeIndex) } struct DepGraphData { @@ -178,6 +179,8 @@ impl DepGraph { R: HashStable, { if let Some(ref data) = self.data { + debug_assert!(!data.colors.borrow().contains_key(&key)); + data.edges.borrow_mut().push_task(key); data.current.borrow_mut().push_task(key); if cfg!(debug_assertions) { @@ -212,7 +215,10 @@ impl DepGraph { let prev_fingerprint = data.previous.fingerprint_of(&key); let color = if Some(current_fingerprint) == prev_fingerprint { - DepNodeColor::Green + DepNodeColor::Green(DepNodeIndex { + legacy: dep_node_index_legacy, + new: dep_node_index_new, + }) } else { DepNodeColor::Red }; @@ -310,18 +316,6 @@ impl DepGraph { self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) } - pub fn node_color(&self, dep_node: &DepNode) -> DepNodeColor { - match self.data.as_ref().unwrap().colors.borrow().get(dep_node) { - Some(&color) => { - debug_assert!(color != DepNodeColor::Gray); - color - } - None => { - DepNodeColor::Gray - } - } - } - /// Indicates that a previous work product exists for `v`. This is /// invoked during initial start-up based on what nodes are clean /// (and what files exist in the incr. directory). @@ -426,6 +420,128 @@ impl DepGraph { edge_list_data, } } + + pub fn node_color(&self, dep_node: &DepNode) -> Option { + self.data.as_ref().and_then(|data| data.colors.borrow().get(dep_node).cloned()) + } + + pub fn try_mark_green(&self, + tcx: TyCtxt, + dep_node: &DepNode) + -> Option { + let data = self.data.as_ref().unwrap(); + + debug_assert!(!data.colors.borrow().contains_key(dep_node)); + debug_assert!(!data.current.borrow().node_to_node_index.contains_key(dep_node)); + + if dep_node.kind.is_input() { + // We should only hit try_mark_green() for inputs that do not exist + // anymore in the current compilation session. Existing inputs are + // eagerly marked as either red/green before any queries are + // executed. + debug_assert!(dep_node.extract_def_id(tcx).is_none()); + return None; + } + + let (prev_deps, prev_dep_node_index) = match data.previous.edges_from(dep_node) { + Some(prev) => { + // This DepNode and the corresponding query invocation existed + // in the previous compilation session too, so we can try to + // mark it as green by recursively marking all of its + // dependencies green. + prev + } + None => { + // This DepNode did not exist in the previous compilation session, + // so we cannot mark it as green. + return None + } + }; + + let mut current_deps = Vec::new(); + + for &dep_dep_node in prev_deps { + let dep_dep_node = &data.previous.index_to_node(dep_dep_node); + let dep_dep_node_color = data.colors.borrow().get(dep_dep_node).cloned(); + match dep_dep_node_color { + Some(DepNodeColor::Green(node_index)) => { + // This dependency has been marked as green before, we are + // still fine and can continue with checking the other + // dependencies. + current_deps.push(node_index); + } + Some(DepNodeColor::Red) => { + // We found a dependency the value of which has changed + // compared to the previous compilation session. We cannot + // mark the DepNode as green and also don't need to bother + // with checking any of the other dependencies. + return None + } + None => { + // We don't know the state of this dependency. Let's try to + // mark it green. + if let Some(node_index) = self.try_mark_green(tcx, dep_dep_node) { + current_deps.push(node_index); + } else { + // We failed to mark it green. This can have various + // reasons. + return None + } + } + } + } + + // If we got here without hitting a `return` that means that all + // dependencies of this DepNode could be marked as green. Therefore we + // can also mark this DepNode as green. We do so by... + + // ... allocating an entry for it in the current dependency graph and + // adding all the appropriate edges imported from the previous graph ... + let node_index_new = data.current + .borrow_mut() + .alloc_node(*dep_node, + current_deps.iter().map(|n| n.new).collect()); + let dep_node_index_legacy = { + let mut legacy_graph = data.edges.borrow_mut(); + legacy_graph.push_task(*dep_node); + for node_index in current_deps.into_iter().map(|n| n.legacy) { + legacy_graph.read_index(node_index); + } + legacy_graph.pop_task(*dep_node) + }; + + // ... copying the fingerprint from the previous graph too, so we don't + // have to recompute it ... + let fingerprint = data.previous.fingerprint_by_index(prev_dep_node_index); + assert!(self.fingerprints + .borrow_mut() + .insert(*dep_node, fingerprint) + .is_none()); + + let node_index = DepNodeIndex { + legacy: dep_node_index_legacy, + new: node_index_new, + }; + + // ... and finally storing a "Green" entry in the color map. + assert!(data.colors + .borrow_mut() + .insert(*dep_node, DepNodeColor::Green(node_index)) + .is_none()); + + Some(node_index) + } + + // Used in various assertions + pub fn is_green(&self, dep_node_index: DepNodeIndex) -> bool { + let dep_node = self.data.as_ref().unwrap().current.borrow().nodes[dep_node_index.new]; + self.data.as_ref().unwrap().colors.borrow().get(&dep_node).map(|&color| { + match color { + DepNodeColor::Red => false, + DepNodeColor::Green(_) => true, + } + }).unwrap_or(false) + } } /// A "work product" is an intermediate result that we save into the diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index cd77e06bdd6..a8f78336b37 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -21,7 +21,7 @@ mod serialized; pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId}; -pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex}; +pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor}; pub use self::prev::PreviousDepGraph; pub use self::query::DepGraphQuery; pub use self::safe::AssertDepGraphSafe; diff --git a/src/librustc/dep_graph/prev.rs b/src/librustc/dep_graph/prev.rs index 0236f9c2292..17001bbb0c3 100644 --- a/src/librustc/dep_graph/prev.rs +++ b/src/librustc/dep_graph/prev.rs @@ -28,20 +28,33 @@ impl PreviousDepGraph { PreviousDepGraph { data, index } } - pub fn with_edges_from(&self, dep_node: &DepNode, mut f: F) - where - F: FnMut(&(DepNode, Fingerprint)), - { - let node_index = self.index[dep_node]; - self.data - .edge_targets_from(node_index) - .into_iter() - .for_each(|&index| f(&self.data.nodes[index])); + #[inline] + pub fn edges_from(&self, + dep_node: &DepNode) + -> Option<(&[SerializedDepNodeIndex], SerializedDepNodeIndex)> { + self.index + .get(dep_node) + .map(|&node_index| { + (self.data.edge_targets_from(node_index), node_index) + }) } + #[inline] + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { + self.data.nodes[dep_node_index].0 + } + + #[inline] pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option { self.index .get(dep_node) .map(|&node_index| self.data.nodes[node_index].1) } + + #[inline] + pub fn fingerprint_by_index(&self, + dep_node_index: SerializedDepNodeIndex) + -> Fingerprint { + self.data.nodes[dep_node_index].1 + } } diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 4bd2d5be6d7..db66c279961 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -43,7 +43,6 @@ use rustc_back::PanicStrategy; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::StableVec; -use std::cell::{RefCell, Cell}; use std::ops::Deref; use std::rc::Rc; diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 581f47dc13c..c7c81e3cf89 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -36,6 +36,26 @@ pub(super) struct QueryValue { pub(super) diagnostics: Option>, } +impl QueryValue { + pub(super) fn new(value: T, + dep_node_index: DepNodeIndex, + diagnostics: Vec) + -> QueryValue { + QueryValue { + value, + index: dep_node_index, + diagnostics: if diagnostics.len() == 0 { + None + } else { + Some(Box::new(QueryDiagnostics { + diagnostics, + emitted_diagnostics: Cell::new(true), + })) + }, + } + } +} + pub(super) struct QueryDiagnostics { pub(super) diagnostics: Vec, pub(super) emitted_diagnostics: Cell, @@ -142,6 +162,10 @@ macro_rules! define_maps { (<$tcx:tt> $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*) => { + + use dep_graph::DepNodeIndex; + use std::cell::RefCell; + define_map_struct! { tcx: $tcx, input: ($(([$($modifiers)*] [$($attr)*] [$name]))*) @@ -200,6 +224,7 @@ macro_rules! define_maps { } impl<'a, $tcx, 'lcx> queries::$name<$tcx> { + #[allow(unused)] fn to_dep_node(tcx: TyCtxt<'a, $tcx, 'lcx>, key: &$K) -> DepNode { use dep_graph::DepConstructor::*; @@ -241,9 +266,6 @@ macro_rules! define_maps { tcx.dep_graph.read_index(value.index); return Ok(f(&value.value)); } - // else, we are going to run the provider: - profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); - // FIXME(eddyb) Get more valid Span's on queries. // def_span guard is necessary to prevent a recursive loop, // default_span calls def_span query internally. @@ -252,42 +274,92 @@ macro_rules! define_maps { } let dep_node = Self::to_dep_node(tcx, &key); + + if !dep_node.kind.is_input() && tcx.sess.opts.build_dep_graph() { + use dep_graph::DepNodeColor; + if let Some(DepNodeColor::Green(dep_node_index)) = tcx.dep_graph + .node_color(&dep_node) { + profq_msg!(tcx, ProfileQueriesMsg::CacheHit); + tcx.dep_graph.read_index(dep_node_index); + return Self::load_from_disk_and_cache_in_memory(tcx, + key, + span, + dep_node_index, + f) + } + + if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, &dep_node) { + debug_assert!(tcx.dep_graph.is_green(dep_node_index)); + profq_msg!(tcx, ProfileQueriesMsg::CacheHit); + tcx.dep_graph.read_index(dep_node_index); + return Self::load_from_disk_and_cache_in_memory(tcx, + key, + span, + dep_node_index, + f) + } + } + + // else, we are going to run the provider: + profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); + let res = tcx.cycle_check(span, Query::$name(key), || { tcx.sess.diagnostic().track_diagnostics(|| { if dep_node.kind.is_anon() { tcx.dep_graph.with_anon_task(dep_node.kind, || { - let provider = tcx.maps.providers[key.map_crate()].$name; - provider(tcx.global_tcx(), key) + Self::compute_result(tcx.global_tcx(), key) }) } else { - fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>, - key: $K) - -> $V { - let provider = tcx.maps.providers[key.map_crate()].$name; - provider(tcx.global_tcx(), key) - } - - tcx.dep_graph.with_task(dep_node, tcx, key, run_provider) + tcx.dep_graph.with_task(dep_node, + tcx, + key, + Self::compute_result) } }) })?; + profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd); let ((result, dep_node_index), diagnostics) = res; tcx.dep_graph.read_index(dep_node_index); - let value = QueryValue { - value: result, - index: dep_node_index, - diagnostics: if diagnostics.len() == 0 { - None - } else { - Some(Box::new(QueryDiagnostics { - diagnostics, - emitted_diagnostics: Cell::new(true), - })) - }, - }; + let value = QueryValue::new(result, dep_node_index, diagnostics); + + Ok(f(&tcx.maps + .$name + .borrow_mut() + .map + .entry(key) + .or_insert(value) + .value)) + } + + fn compute_result(tcx: TyCtxt<'a, $tcx, 'lcx>, key: $K) -> $V { + let provider = tcx.maps.providers[key.map_crate()].$name; + provider(tcx.global_tcx(), key) + } + + fn load_from_disk_and_cache_in_memory(tcx: TyCtxt<'a, $tcx, 'lcx>, + key: $K, + span: Span, + dep_node_index: DepNodeIndex, + f: F) + -> Result> + where F: FnOnce(&$V) -> R + { + debug_assert!(tcx.dep_graph.is_green(dep_node_index)); + + // We don't do any caching yet, so recompute + let (result, diagnostics) = tcx.cycle_check(span, Query::$name(key), || { + tcx.sess.diagnostic().track_diagnostics(|| { + // The dep-graph for this computation is already in place + tcx.dep_graph.with_ignore(|| { + Self::compute_result(tcx, key) + }) + }) + })?; + + let value = QueryValue::new(result, dep_node_index, diagnostics); Ok(f(&tcx.maps .$name diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 29da763f334..c1653cfb43b 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -14,7 +14,7 @@ //! We walk the set of items and, for each member, generate new constraints. use hir::def_id::DefId; -use rustc::dep_graph::{DepGraphSafe, DepKind}; +use rustc::dep_graph::{DepGraphSafe, DepKind, DepNodeColor}; use rustc::ich::StableHashingContext; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; @@ -162,10 +162,22 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // See README.md for a detailed discussion // on dep-graph management. let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints); - tcx.dep_graph.with_task(dep_node, - self, - def_id, - visit_item_task); + + if let Some(DepNodeColor::Green(_)) = tcx.dep_graph.node_color(&dep_node) { + // If the corresponding node has already been marked as green, the + // appropriate portion of the DepGraph has already been loaded from + // the previous graph, so we don't do any dep-tracking. Since we + // don't cache any values though, we still have to re-run the + // computation. + tcx.dep_graph.with_ignore(|| { + self.build_constraints_for_item(def_id); + }); + } else { + tcx.dep_graph.with_task(dep_node, + self, + def_id, + visit_item_task); + } fn visit_item_task<'a, 'tcx>(ccx: &mut ConstraintContext<'a, 'tcx>, def_id: DefId)