Skip to content

Commit 34af2de

Browse files
committed
Auto merge of #31304 - nikomatsakis:incr-comp-read-from-hir-map, r=michaelwoerister
This change also modifies the dep graph infrastructure to keep track of the number of active tasks, so that even if we are not building the full dep-graph, we still get assertions when there is no active task and one does something that would add a read/write edge. This is particularly helpful since, if the assertions are *not* active, you wind up with the error happening in the message processing thread, which is too late to know the correct backtrace. ~~Before landing, I need to do some performance measurements. Those are underway.~~ See measurements below. No real effect on time. r? @michaelwoerister
2 parents 6dc112d + a0f96d6 commit 34af2de

File tree

26 files changed

+289
-69
lines changed

26 files changed

+289
-69
lines changed

src/librustc/dep_graph/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,21 @@ pub enum DepNode {
4040
Hir(DefId),
4141

4242
// Represents different phases in the compiler.
43+
CrateReader,
44+
CollectLanguageItems,
45+
CheckStaticRecursion,
46+
ResolveLifetimes,
47+
RegionResolveCrate,
48+
CheckLoops,
49+
PluginRegistrar,
50+
StabilityIndex,
4351
CollectItem(DefId),
4452
Coherence,
53+
EffectCheck,
54+
Liveness,
55+
Resolve,
56+
EntryPoint,
57+
CheckEntryFn,
4558
CoherenceCheckImpl(DefId),
4659
CoherenceOverlapCheck(DefId),
4760
CoherenceOverlapCheckSpecial(DefId),
@@ -116,6 +129,13 @@ impl DepGraph {
116129
}
117130
}
118131

132+
/// True if we are actually building a dep-graph. If this returns false,
133+
/// then the other methods on this `DepGraph` will have no net effect.
134+
#[inline]
135+
pub fn enabled(&self) -> bool {
136+
self.data.enabled()
137+
}
138+
119139
pub fn query(&self) -> DepGraphQuery {
120140
self.data.query()
121141
}

src/librustc/dep_graph/thread.rs

+47-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
//! allocated (and both have a fairly large capacity).
2020
2121
use rustc_data_structures::veccell::VecCell;
22+
use std::cell::Cell;
2223
use std::sync::mpsc::{self, Sender, Receiver};
2324
use std::thread;
2425

@@ -39,6 +40,13 @@ pub enum DepMessage {
3940
pub struct DepGraphThreadData {
4041
enabled: bool,
4142

43+
// Local counter that just tracks how many tasks are pushed onto the
44+
// stack, so that we still get an error in the case where one is
45+
// missing. If dep-graph construction is enabled, we'd get the same
46+
// error when processing tasks later on, but that's annoying because
47+
// it lacks precision about the source of the error.
48+
tasks_pushed: Cell<usize>,
49+
4250
// current buffer, where we accumulate messages
4351
messages: VecCell<DepMessage>,
4452

@@ -59,18 +67,26 @@ impl DepGraphThreadData {
5967
let (tx1, rx1) = mpsc::channel();
6068
let (tx2, rx2) = mpsc::channel();
6169
let (txq, rxq) = mpsc::channel();
70+
6271
if enabled {
6372
thread::spawn(move || main(rx1, tx2, txq));
6473
}
74+
6575
DepGraphThreadData {
6676
enabled: enabled,
77+
tasks_pushed: Cell::new(0),
6778
messages: VecCell::with_capacity(INITIAL_CAPACITY),
6879
swap_in: rx2,
6980
swap_out: tx1,
7081
query_in: rxq,
7182
}
7283
}
7384

85+
#[inline]
86+
pub fn enabled(&self) -> bool {
87+
self.enabled
88+
}
89+
7490
/// Sends the current batch of messages to the thread. Installs a
7591
/// new vector of messages.
7692
fn swap(&self) {
@@ -100,13 +116,40 @@ impl DepGraphThreadData {
100116
/// the buffer is full, this may swap.)
101117
#[inline]
102118
pub fn enqueue(&self, message: DepMessage) {
119+
// Regardless of whether dep graph construction is enabled, we
120+
// still want to check that we always have a valid task on the
121+
// stack when a read/write/etc event occurs.
122+
match message {
123+
DepMessage::Read(_) | DepMessage::Write(_) =>
124+
if self.tasks_pushed.get() == 0 {
125+
self.invalid_message("read/write but no current task")
126+
},
127+
DepMessage::PushTask(_) | DepMessage::PushIgnore =>
128+
self.tasks_pushed.set(self.tasks_pushed.get() + 1),
129+
DepMessage::PopTask(_) | DepMessage::PopIgnore =>
130+
self.tasks_pushed.set(self.tasks_pushed.get() - 1),
131+
DepMessage::Query =>
132+
(),
133+
}
134+
103135
if self.enabled {
104-
let len = self.messages.push(message);
105-
if len == INITIAL_CAPACITY {
106-
self.swap();
107-
}
136+
self.enqueue_enabled(message);
108137
}
109138
}
139+
140+
// Outline this fn since I expect it may want to be inlined
141+
// separately.
142+
fn enqueue_enabled(&self, message: DepMessage) {
143+
let len = self.messages.push(message);
144+
if len == INITIAL_CAPACITY {
145+
self.swap();
146+
}
147+
}
148+
149+
// Outline this too.
150+
fn invalid_message(&self, string: &str) {
151+
panic!("{}; see src/librustc/dep_graph/README.md for more information", string)
152+
}
110153
}
111154

112155
/// Definition of the depgraph thread.

src/librustc/front/map/collector.rs

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl<'ast> NodeCollector<'ast> {
7979

8080
fn create_def(&mut self, node_id: NodeId, data: DefPathData) -> DefIndex {
8181
let parent_def = self.parent_def();
82+
debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
8283
self.definitions.create_def_with_parent(parent_def, node_id, data)
8384
}
8485

@@ -115,10 +116,13 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
115116
/// deep walking so that we walk nested items in the context of
116117
/// their outer items.
117118
fn visit_nested_item(&mut self, item: ItemId) {
119+
debug!("visit_nested_item: {:?}", item);
118120
self.visit_item(self.krate.item(item.id))
119121
}
120122

121123
fn visit_item(&mut self, i: &'ast Item) {
124+
debug!("visit_item: {:?}", i);
125+
122126
// Pick the def data. This need not be unique, but the more
123127
// information we encapsulate into
124128
let def_data = match i.node {

src/librustc/front/map/mod.rs

+89-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use self::MapEntry::*;
1414
use self::collector::NodeCollector;
1515
pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, DisambiguatedDefPathData};
1616

17+
use dep_graph::{DepGraph, DepNode};
18+
1719
use middle::cstore::InlinedItem;
1820
use middle::cstore::InlinedItem as II;
1921
use middle::def_id::DefId;
@@ -228,19 +230,22 @@ impl<'ast> MapEntry<'ast> {
228230

229231
/// Stores a crate and any number of inlined items from other crates.
230232
pub struct Forest {
231-
pub krate: Crate,
233+
krate: Crate,
234+
pub dep_graph: DepGraph,
232235
inlined_items: TypedArena<InlinedParent>
233236
}
234237

235238
impl Forest {
236-
pub fn new(krate: Crate) -> Forest {
239+
pub fn new(krate: Crate, dep_graph: DepGraph) -> Forest {
237240
Forest {
238241
krate: krate,
242+
dep_graph: dep_graph,
239243
inlined_items: TypedArena::new()
240244
}
241245
}
242246

243247
pub fn krate<'ast>(&'ast self) -> &'ast Crate {
248+
self.dep_graph.read(DepNode::Krate);
244249
&self.krate
245250
}
246251
}
@@ -252,6 +257,10 @@ pub struct Map<'ast> {
252257
/// The backing storage for all the AST nodes.
253258
pub forest: &'ast Forest,
254259

260+
/// Same as the dep_graph in forest, just available with one fewer
261+
/// deref. This is a gratuitious micro-optimization.
262+
pub dep_graph: DepGraph,
263+
255264
/// NodeIds are sequential integers from 0, so we can be
256265
/// super-compact by storing them in a vector. Not everything with
257266
/// a NodeId is in the map, but empirically the occupancy is about
@@ -267,6 +276,60 @@ pub struct Map<'ast> {
267276
}
268277

269278
impl<'ast> Map<'ast> {
279+
/// Registers a read in the dependency graph of the AST node with
280+
/// the given `id`. This needs to be called each time a public
281+
/// function returns the HIR for a node -- in other words, when it
282+
/// "reveals" the content of a node to the caller (who might not
283+
/// otherwise have had access to those contents, and hence needs a
284+
/// read recorded). If the function just returns a DefId or
285+
/// NodeId, no actual content was returned, so no read is needed.
286+
fn read(&self, id: NodeId) {
287+
self.dep_graph.read(self.dep_node(id));
288+
}
289+
290+
fn dep_node(&self, id0: NodeId) -> DepNode {
291+
let map = self.map.borrow();
292+
let mut id = id0;
293+
loop {
294+
match map[id as usize] {
295+
EntryItem(_, item) => {
296+
let def_id = self.local_def_id(item.id);
297+
// NB ^~~~~~~
298+
//
299+
// You would expect that `item.id == id`, but this
300+
// is not always the case. In particular, for a
301+
// ViewPath item like `use self::{mem, foo}`, we
302+
// map the ids for `mem` and `foo` to the
303+
// enclosing view path item. This seems mega super
304+
// ultra wrong, but then who am I to judge?
305+
// -nmatsakis
306+
return DepNode::Hir(def_id);
307+
}
308+
309+
EntryForeignItem(p, _) |
310+
EntryTraitItem(p, _) |
311+
EntryImplItem(p, _) |
312+
EntryVariant(p, _) |
313+
EntryExpr(p, _) |
314+
EntryStmt(p, _) |
315+
EntryLocal(p, _) |
316+
EntryPat(p, _) |
317+
EntryBlock(p, _) |
318+
EntryStructCtor(p, _) |
319+
EntryLifetime(p, _) |
320+
EntryTyParam(p, _) =>
321+
id = p,
322+
323+
RootCrate |
324+
RootInlinedParent(_) => // FIXME(#2369) clarify story about cross-crate dep tracking
325+
return DepNode::Krate,
326+
327+
NotPresent =>
328+
panic!("Walking parents from `{}` led to `NotPresent` at `{}`", id0, id),
329+
}
330+
}
331+
}
332+
270333
pub fn num_local_def_ids(&self) -> usize {
271334
self.definitions.borrow().len()
272335
}
@@ -309,26 +372,30 @@ impl<'ast> Map<'ast> {
309372
}
310373

311374
pub fn krate(&self) -> &'ast Crate {
312-
&self.forest.krate
375+
self.forest.krate()
313376
}
314377

315378
/// Retrieve the Node corresponding to `id`, panicking if it cannot
316379
/// be found.
317380
pub fn get(&self, id: NodeId) -> Node<'ast> {
318381
match self.find(id) {
319-
Some(node) => node,
382+
Some(node) => node, // read recorded by `find`
320383
None => panic!("couldn't find node id {} in the AST map", id)
321384
}
322385
}
323386

324387
pub fn get_if_local(&self, id: DefId) -> Option<Node<'ast>> {
325-
self.as_local_node_id(id).map(|id| self.get(id))
388+
self.as_local_node_id(id).map(|id| self.get(id)) // read recorded by `get`
326389
}
327390

328391
/// Retrieve the Node corresponding to `id`, returning None if
329392
/// cannot be found.
330393
pub fn find(&self, id: NodeId) -> Option<Node<'ast>> {
331-
self.find_entry(id).and_then(|x| x.to_node())
394+
let result = self.find_entry(id).and_then(|x| x.to_node());
395+
if result.is_some() {
396+
self.read(id);
397+
}
398+
result
332399
}
333400

334401
/// Similar to get_parent, returns the parent node id or id if there is no
@@ -459,22 +526,25 @@ impl<'ast> Map<'ast> {
459526
_ => None
460527
};
461528
match abi {
462-
Some(abi) => abi,
529+
Some(abi) => {
530+
self.read(id); // reveals some of the content of a node
531+
abi
532+
}
463533
None => panic!("expected foreign mod or inlined parent, found {}",
464534
self.node_to_string(parent))
465535
}
466536
}
467537

468538
pub fn get_foreign_vis(&self, id: NodeId) -> Visibility {
469-
let vis = self.expect_foreign_item(id).vis;
470-
match self.find(self.get_parent(id)) {
539+
let vis = self.expect_foreign_item(id).vis; // read recorded by `expect_foreign_item`
540+
match self.find(self.get_parent(id)) { // read recorded by `find`
471541
Some(NodeItem(i)) => vis.inherit_from(i.vis),
472542
_ => vis
473543
}
474544
}
475545

476546
pub fn expect_item(&self, id: NodeId) -> &'ast Item {
477-
match self.find(id) {
547+
match self.find(id) { // read recorded by `find`
478548
Some(NodeItem(item)) => item,
479549
_ => panic!("expected item, found {}", self.node_to_string(id))
480550
}
@@ -521,7 +591,7 @@ impl<'ast> Map<'ast> {
521591
}
522592

523593
pub fn expect_expr(&self, id: NodeId) -> &'ast Expr {
524-
match self.find(id) {
594+
match self.find(id) { // read recorded by find
525595
Some(NodeExpr(expr)) => expr,
526596
_ => panic!("expected expr, found {}", self.node_to_string(id))
527597
}
@@ -571,6 +641,11 @@ impl<'ast> Map<'ast> {
571641
fn with_path_next<T, F>(&self, id: NodeId, next: LinkedPath, f: F) -> T where
572642
F: FnOnce(PathElems) -> T,
573643
{
644+
// This function reveals the name of the item and hence is a
645+
// kind of read. This is inefficient, since it walks ancestors
646+
// and we are walking them anyhow, but whatever.
647+
self.read(id);
648+
574649
let parent = self.get_parent(id);
575650
let parent = match self.find_entry(id) {
576651
Some(EntryForeignItem(..)) => {
@@ -602,6 +677,7 @@ impl<'ast> Map<'ast> {
602677
/// Given a node ID, get a list of attributes associated with the AST
603678
/// corresponding to the Node ID
604679
pub fn attrs(&self, id: NodeId) -> &'ast [ast::Attribute] {
680+
self.read(id); // reveals attributes on the node
605681
let attrs = match self.find(id) {
606682
Some(NodeItem(i)) => Some(&i.attrs[..]),
607683
Some(NodeForeignItem(fi)) => Some(&fi.attrs[..]),
@@ -655,6 +731,7 @@ impl<'ast> Map<'ast> {
655731
}
656732

657733
pub fn span(&self, id: NodeId) -> Span {
734+
self.read(id); // reveals span from node
658735
self.opt_span(id)
659736
.unwrap_or_else(|| panic!("AstMap.span: could not find span for id {:?}", id))
660737
}
@@ -833,6 +910,7 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest) -> Map<'ast> {
833910

834911
Map {
835912
forest: forest,
913+
dep_graph: forest.dep_graph.clone(),
836914
map: RefCell::new(map),
837915
definitions: RefCell::new(definitions),
838916
}

src/librustc/middle/effect.rs

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! `unsafe`.
1313
use self::RootUnsafeContext::*;
1414

15+
use dep_graph::DepNode;
1516
use middle::def::Def;
1617
use middle::ty::{self, Ty};
1718
use middle::ty::MethodCall;
@@ -182,6 +183,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
182183
}
183184

184185
pub fn check_crate(tcx: &ty::ctxt) {
186+
let _task = tcx.dep_graph.in_task(DepNode::EffectCheck);
187+
185188
let mut visitor = EffectCheckVisitor {
186189
tcx: tcx,
187190
unsafe_context: UnsafeContext::new(SafeContext),

0 commit comments

Comments
 (0)