incr.comp.: Add minimal version of try_mark_green procedure.
This commit is contained in:
parent
a948be81cd
commit
6db27d9f90
6 changed files with 268 additions and 56 deletions
|
@ -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<Rc<DepGraphData>>,
|
||||
|
@ -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<HCX>,
|
||||
{
|
||||
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<DepNodeColor> {
|
||||
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<DepNodeIndex> {
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -28,20 +28,33 @@ impl PreviousDepGraph {
|
|||
PreviousDepGraph { data, index }
|
||||
}
|
||||
|
||||
pub fn with_edges_from<F>(&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<Fingerprint> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -36,6 +36,26 @@ pub(super) struct QueryValue<T> {
|
|||
pub(super) diagnostics: Option<Box<QueryDiagnostics>>,
|
||||
}
|
||||
|
||||
impl<T> QueryValue<T> {
|
||||
pub(super) fn new(value: T,
|
||||
dep_node_index: DepNodeIndex,
|
||||
diagnostics: Vec<Diagnostic>)
|
||||
-> QueryValue<T> {
|
||||
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<Diagnostic>,
|
||||
pub(super) emitted_diagnostics: Cell<bool>,
|
||||
|
@ -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<F, R>(tcx: TyCtxt<'a, $tcx, 'lcx>,
|
||||
key: $K,
|
||||
span: Span,
|
||||
dep_node_index: DepNodeIndex,
|
||||
f: F)
|
||||
-> Result<R, CycleError<'a, $tcx>>
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue