incr.comp.: Add minimal version of try_mark_green procedure.

This commit is contained in:
Michael Woerister 2017-09-25 13:51:49 +02:00
parent a948be81cd
commit 6db27d9f90
6 changed files with 268 additions and 56 deletions

View file

@ -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

View file

@ -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;

View file

@ -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
}
}

View file

@ -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;

View file

@ -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

View file

@ -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)