Skip to content

Commit

Permalink
Refactor task system for efficiency
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Dec 4, 2018
1 parent cf3e67e commit de6a468
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 201 deletions.
328 changes: 188 additions & 140 deletions src/librustc/dep_graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,197 +202,245 @@ impl DepGraph {
arg: A,
task: fn(C, A) -> R)
-> (R, DepNodeIndex)
where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
where C: DepGraphSafe + StableHashingContextProvider<'gcx> + Clone,
R: HashStable<StableHashingContext<'gcx>>,
{
self.with_task_impl(key, cx, arg, false, task,
|key| OpenTask::Regular(Lock::new(RegularOpenTask {
if let Some(ref data) = self.data {
let open_task = OpenTask::Regular(Lock::new(RegularOpenTask {
node: key,
reads: SmallVec::new(),
read_set: Default::default(),
})),
|data, key, task| data.borrow_mut().complete_task(key, task))
}));
let result = ty::tls::with_context(|icx| {
let icx = ty::tls::ImplicitCtxt {
task: &open_task,
..icx.clone()
};

ty::tls::enter_context(&icx, |_| {
task(cx.clone(), arg)
})
});
let dep_node_index = data.current.borrow_mut().complete_task(key, open_task);
self.finish_task_incr_on(data, key, cx, &result, dep_node_index);
(result, dep_node_index)
} else {
let result = task(cx.clone(), arg);
self.finish_task_incr_off(key, cx, &result);
(result, DepNodeIndex::INVALID)
}
}

/// Creates a new dep-graph input with value `input`
pub fn input_task<'gcx, C, R>(&self,
key: DepNode,
cx: C,
input: R)
-> (R, DepNodeIndex)
where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
R: HashStable<StableHashingContext<'gcx>>,
/// Execute something within an "eval-always" task which is a task
// that runs whenever anything changes.
// FIXME: Find a way to make F: DepGraphSafe
pub fn with_eval_always_task<'a, F, R>(
&self,
tcx: TyCtxt<'a, '_, '_>,
key: DepNode,
task: F,
) -> (R, DepNodeIndex)
where F: FnOnce(&OpenTask) -> R,
R: HashStable<StableHashingContext<'a>>,
{
fn identity_fn<C, A>(_: C, arg: A) -> A {
arg
if let Some(ref data) = self.data {
let open_task = OpenTask::EvalAlways { node: key };
let result = task(&open_task);
let dep_node_index = data.current.borrow_mut()
.complete_eval_always_task(key, open_task);
self.finish_task_incr_on(data, key, tcx, &result, dep_node_index);
(result, dep_node_index)
} else {
debug_assert!(!key.kind.fingerprint_needed_for_crate_hash());
(task(&OpenTask::Ignore), DepNodeIndex::INVALID)
}
}

self.with_task_impl(key, cx, input, true, identity_fn,
|_| OpenTask::Ignore,
|data, key, _| data.borrow_mut().alloc_node(key, SmallVec::new()))
// FIXME: Merge with with_task?
pub fn with_query_task<'a, F, R>(
&self,
tcx: TyCtxt<'a, '_, '_>,
key: DepNode,
task: F,
) -> (R, DepNodeIndex)
where F: FnOnce(&OpenTask) -> R,
R: HashStable<StableHashingContext<'a>>,
{
if let Some(ref data) = self.data {
let open_task = OpenTask::Regular(Lock::new(RegularOpenTask {
node: key,
reads: SmallVec::new(),
read_set: Default::default(),
}));
let result = task(&open_task);
// FIXME: Look at `complete_task` and the same for other functions
let dep_node_index = data.current.borrow_mut().complete_task(key, open_task);
self.finish_task_incr_on(data, key, tcx, &result, dep_node_index);
(result, dep_node_index)
} else {
debug_assert!(!key.kind.fingerprint_needed_for_crate_hash());
// with_task runs finish_task_incr_off here
(task(&OpenTask::Ignore), DepNodeIndex::INVALID)
}
}

/// Creates a new dep-graph input with value `input`
pub fn input_dep_index<'gcx, R>(
&self,
key: DepNode,
cx: &StableHashingContext<'gcx>,
input: &R
) -> DepNodeIndex
where R: HashStable<StableHashingContext<'gcx>>,
{
// This assumes that we don't have an ImplicitCtxt and thus have
// an implicit OpenTask::Ignore task
debug_assert!(ty::tls::with_opt(|tcx| tcx.is_none()));

if let Some(ref data) = self.data {
let dep_node_index = data.current.borrow_mut().alloc_node(key, SmallVec::new());
self.finish_task_incr_on(data, key, cx, input, dep_node_index);
dep_node_index
} else {
self.finish_task_incr_off(key, cx, input)
}
}

fn with_task_impl<'gcx, C, A, R>(
fn finish_task_incr_on<'gcx, C, R>(
&self,
data: &DepGraphData,
key: DepNode,
cx: C,
arg: A,
no_tcx: bool,
task: fn(C, A) -> R,
// FIXME: Take OpenTask as a parameter instead
create_task: fn(DepNode) -> OpenTask,
finish_task_and_alloc_depnode: fn(&Lock<CurrentDepGraph>,
DepNode,
OpenTask) -> DepNodeIndex
) -> (R, DepNodeIndex)
result: &R,
dep_node_index: DepNodeIndex,
)
where
C: DepGraphSafe + StableHashingContextProvider<'gcx>,
R: HashStable<StableHashingContext<'gcx>>,
{
if let Some(ref data) = self.data {
let open_task = create_task(key);
// In incremental mode, hash the result of the task. We don't
// do anything with the hash yet, but we are computing it
// anyway so that
// - we make sure that the infrastructure works and
// - we can get an idea of the runtime cost.
let mut hcx = cx.get_stable_hashing_context();

if cfg!(debug_assertions) {
profq_msg(hcx.sess(), ProfileQueriesMsg::TaskBegin(key.clone()))
};

// In incremental mode, hash the result of the task. We don't
// do anything with the hash yet, but we are computing it
// anyway so that
// - we make sure that the infrastructure works and
// - we can get an idea of the runtime cost.
let mut hcx = cx.get_stable_hashing_context();
if cfg!(debug_assertions) {
profq_msg(hcx.sess(), ProfileQueriesMsg::TaskEnd)
};

if cfg!(debug_assertions) {
profq_msg(hcx.sess(), ProfileQueriesMsg::TaskBegin(key.clone()))
};
let mut stable_hasher = StableHasher::new();
result.hash_stable(&mut hcx, &mut stable_hasher);

let result = if no_tcx {
task(cx, arg)
} else {
ty::tls::with_context(|icx| {
let icx = ty::tls::ImplicitCtxt {
task: &open_task,
..icx.clone()
};

ty::tls::enter_context(&icx, |_| {
task(cx, arg)
})
})
};
let current_fingerprint = stable_hasher.finish();

if cfg!(debug_assertions) {
profq_msg(hcx.sess(), ProfileQueriesMsg::TaskEnd)
};
// Store the current fingerprint
{
let mut fingerprints = self.fingerprints.borrow_mut();

let dep_node_index = finish_task_and_alloc_depnode(&data.current, key, open_task);
if dep_node_index.index() >= fingerprints.len() {
fingerprints.resize(dep_node_index.index() + 1, Fingerprint::ZERO);
}

let mut stable_hasher = StableHasher::new();
result.hash_stable(&mut hcx, &mut stable_hasher);
debug_assert!(fingerprints[dep_node_index] == Fingerprint::ZERO,
"DepGraph::with_task() - Duplicate fingerprint \
insertion for {:?}", key);
fingerprints[dep_node_index] = current_fingerprint;
}

let current_fingerprint = stable_hasher.finish();
// Determine the color of the new DepNode.
if let Some(prev_index) = data.previous.node_to_index_opt(&key) {
let prev_fingerprint = data.previous.fingerprint_by_index(prev_index);

// Store the current fingerprint
{
let mut fingerprints = self.fingerprints.borrow_mut();
let color = if current_fingerprint == prev_fingerprint {
DepNodeColor::Green(dep_node_index)
} else {
DepNodeColor::Red
};

if dep_node_index.index() >= fingerprints.len() {
fingerprints.resize(dep_node_index.index() + 1, Fingerprint::ZERO);
}
let mut colors = data.colors.borrow_mut();
debug_assert!(colors.get(prev_index).is_none(),
"DepGraph::with_task() - Duplicate DepNodeColor \
insertion for {:?}", key);

debug_assert!(fingerprints[dep_node_index] == Fingerprint::ZERO,
"DepGraph::with_task() - Duplicate fingerprint \
insertion for {:?}", key);
fingerprints[dep_node_index] = current_fingerprint;
}
colors.insert(prev_index, color);
}
}

// Determine the color of the new DepNode.
if let Some(prev_index) = data.previous.node_to_index_opt(&key) {
let prev_fingerprint = data.previous.fingerprint_by_index(prev_index);
fn finish_task_incr_off<'gcx, C, R>(
&self,
key: DepNode,
cx: C,
result: &R,
) -> DepNodeIndex
where
C: DepGraphSafe + StableHashingContextProvider<'gcx>,
R: HashStable<StableHashingContext<'gcx>>,
{
debug_assert!(self.data.is_none());

let color = if current_fingerprint == prev_fingerprint {
DepNodeColor::Green(dep_node_index)
} else {
DepNodeColor::Red
};
if key.kind.fingerprint_needed_for_crate_hash() {
let mut hcx = cx.get_stable_hashing_context();
let mut stable_hasher = StableHasher::new();
result.hash_stable(&mut hcx, &mut stable_hasher);
let fingerprint = stable_hasher.finish();

let mut colors = data.colors.borrow_mut();
debug_assert!(colors.get(prev_index).is_none(),
"DepGraph::with_task() - Duplicate DepNodeColor \
insertion for {:?}", key);
let mut fingerprints = self.fingerprints.borrow_mut();
let dep_node_index = DepNodeIndex::new(fingerprints.len());
fingerprints.push(fingerprint);

colors.insert(prev_index, color);
}
debug_assert!(fingerprints[dep_node_index] == fingerprint,
"DepGraph::with_task() - Assigned fingerprint to \
unexpected index for {:?}", key);

(result, dep_node_index)
dep_node_index
} else {
if key.kind.fingerprint_needed_for_crate_hash() {
let mut hcx = cx.get_stable_hashing_context();
let result = task(cx, arg);
let mut stable_hasher = StableHasher::new();
result.hash_stable(&mut hcx, &mut stable_hasher);
let fingerprint = stable_hasher.finish();

let mut fingerprints = self.fingerprints.borrow_mut();
let dep_node_index = DepNodeIndex::new(fingerprints.len());
fingerprints.push(fingerprint);

debug_assert!(fingerprints[dep_node_index] == fingerprint,
"DepGraph::with_task() - Assigned fingerprint to \
unexpected index for {:?}", key);

(result, dep_node_index)
} else {
(task(cx, arg), DepNodeIndex::INVALID)
}
DepNodeIndex::INVALID
}
}

/// Execute something within an "anonymous" task, that is, a task the
/// DepNode of which is determined by the list of inputs it read from.
pub fn with_anon_task<OP,R>(&self, dep_kind: DepKind, op: OP) -> (R, DepNodeIndex)
where OP: FnOnce() -> R
pub fn with_anon_open_task<OP,R>(&self, dep_kind: DepKind, op: OP) -> (R, DepNodeIndex)
where OP: FnOnce(&OpenTask) -> R
{
if let Some(ref data) = self.data {
let (result, open_task) = ty::tls::with_context(|icx| {
let task = OpenTask::Anon(Lock::new(AnonOpenTask {
reads: SmallVec::new(),
read_set: Default::default(),
}));

let r = {
let icx = ty::tls::ImplicitCtxt {
task: &task,
..icx.clone()
};

ty::tls::enter_context(&icx, |_| {
op()
})
};
let task = OpenTask::Anon(Lock::new(AnonOpenTask {
reads: SmallVec::new(),
read_set: Default::default(),
}));

(r, task)
});
let result = op(&task);
let dep_node_index = data.current
.borrow_mut()
.pop_anon_task(dep_kind, open_task);
.pop_anon_task(dep_kind, task);
(result, dep_node_index)
} else {
(op(), DepNodeIndex::INVALID)
(op(&OpenTask::Ignore), DepNodeIndex::INVALID)
}
}

/// Execute something within an "eval-always" task which is a task
// that runs whenever anything changes.
pub fn with_eval_always_task<'gcx, C, A, R>(&self,
key: DepNode,
cx: C,
arg: A,
task: fn(C, A) -> R)
-> (R, DepNodeIndex)
where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
R: HashStable<StableHashingContext<'gcx>>,
/// Execute something within an "anonymous" task, that is, a task the
/// DepNode of which is determined by the list of inputs it read from.
pub fn with_anon_task<OP,R>(&self, dep_kind: DepKind, op: OP) -> (R, DepNodeIndex)
where OP: FnOnce() -> R
{
self.with_task_impl(key, cx, arg, false, task,
|key| OpenTask::EvalAlways { node: key },
|data, key, task| data.borrow_mut().complete_eval_always_task(key, task))
self.with_anon_open_task(dep_kind, |task| {
ty::tls::with_context(|icx| {
let icx = ty::tls::ImplicitCtxt {
task,
..icx.clone()
};

ty::tls::enter_context(&icx, |_| {
op()
})
})
})
}

#[inline]
Expand Down
Loading

0 comments on commit de6a468

Please sign in to comment.