rustc: store ty::Tables separately for each body (except closures').

This commit is contained in:
Eduard-Mihai Burtescu 2017-01-04 04:01:58 +02:00
parent 85a4a192c7
commit cde0a7e7e0
17 changed files with 223 additions and 477 deletions

View file

@ -113,6 +113,7 @@ pub enum DepNode<D: Clone + Debug> {
SizedConstraint(D),
AssociatedItemDefIds(D),
InherentImpls(D),
Tables(D),
// The set of impls for a given trait. Ultimately, it would be
// nice to get more fine-grained here (e.g., to include a
@ -162,6 +163,7 @@ impl<D: Clone + Debug> DepNode<D> {
ItemSignature,
AssociatedItemDefIds,
InherentImpls,
Tables,
TraitImpls,
ReprHints,
}
@ -230,6 +232,7 @@ impl<D: Clone + Debug> DepNode<D> {
SizedConstraint(ref d) => op(d).map(SizedConstraint),
AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds),
InherentImpls(ref d) => op(d).map(InherentImpls),
Tables(ref d) => op(d).map(Tables),
TraitImpls(ref d) => op(d).map(TraitImpls),
TraitItems(ref d) => op(d).map(TraitItems),
ReprHints(ref d) => op(d).map(ReprHints),

View file

@ -23,10 +23,6 @@ pub struct NodeCollector<'ast> {
pub(super) map: Vec<MapEntry<'ast>>,
/// The parent of this node
pub parent_node: NodeId,
/// If true, completely ignore nested items. We set this when loading
/// HIR from metadata, since in that case we only want the HIR for
/// one specific item (and not the ones nested inside of it).
pub ignore_nested_items: bool
}
impl<'ast> NodeCollector<'ast> {
@ -35,30 +31,12 @@ impl<'ast> NodeCollector<'ast> {
krate: krate,
map: vec![],
parent_node: CRATE_NODE_ID,
ignore_nested_items: false
};
collector.insert_entry(CRATE_NODE_ID, RootCrate);
collector
}
pub(super) fn extend(krate: &'ast Crate,
parent: &'ast InlinedItem,
parent_node: NodeId,
map: Vec<MapEntry<'ast>>)
-> NodeCollector<'ast> {
let mut collector = NodeCollector {
krate: krate,
map: map,
parent_node: parent_node,
ignore_nested_items: true
};
collector.insert_entry(parent_node, RootInlinedParent(parent));
collector
}
fn insert_entry(&mut self, id: NodeId, entry: MapEntry<'ast>) {
debug!("ast_map: {:?} => {:?}", id, entry);
let len = self.map.len();
@ -92,27 +70,19 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
fn visit_nested_item(&mut self, item: ItemId) {
debug!("visit_nested_item: {:?}", item);
if !self.ignore_nested_items {
self.visit_item(self.krate.item(item.id))
}
self.visit_item(self.krate.item(item.id));
}
fn visit_nested_trait_item(&mut self, item_id: TraitItemId) {
if !self.ignore_nested_items {
self.visit_trait_item(self.krate.trait_item(item_id))
}
self.visit_trait_item(self.krate.trait_item(item_id));
}
fn visit_nested_impl_item(&mut self, item_id: ImplItemId) {
if !self.ignore_nested_items {
self.visit_impl_item(self.krate.impl_item(item_id))
}
self.visit_impl_item(self.krate.impl_item(item_id));
}
fn visit_nested_body(&mut self, id: BodyId) {
if !self.ignore_nested_items {
self.visit_body(self.krate.body(id))
}
self.visit_body(self.krate.body(id));
}
fn visit_item(&mut self, i: &'ast Item) {

View file

@ -25,28 +25,18 @@ use syntax::codemap::Spanned;
use syntax_pos::Span;
use hir::*;
use hir::intravisit::Visitor;
use hir::print::Nested;
use util::nodemap::DefIdMap;
use arena::TypedArena;
use std::cell::RefCell;
use std::io;
use std::mem;
pub mod blocks;
mod collector;
mod def_collector;
pub mod definitions;
/// The data we save and restore about an inlined item or method. This is not
/// part of the AST that we parse from a file, but it becomes part of the tree
/// that we trans.
#[derive(Debug)]
struct InlinedItem {
def_id: DefId,
body: Body,
}
#[derive(Copy, Clone, Debug)]
pub enum Node<'ast> {
NodeItem(&'ast Item),
@ -99,7 +89,6 @@ enum MapEntry<'ast> {
/// Roots for node trees.
RootCrate,
RootInlinedParent(&'ast InlinedItem)
}
impl<'ast> Clone for MapEntry<'ast> {
@ -152,8 +141,7 @@ impl<'ast> MapEntry<'ast> {
EntryVisibility(id, _) => id,
NotPresent |
RootCrate |
RootInlinedParent(_) => return None,
RootCrate => return None,
})
}
@ -225,7 +213,7 @@ impl<'ast> MapEntry<'ast> {
pub struct Forest {
krate: Crate,
pub dep_graph: DepGraph,
inlined_items: TypedArena<InlinedItem>
inlined_bodies: TypedArena<Body>
}
impl Forest {
@ -233,7 +221,7 @@ impl Forest {
Forest {
krate: krate,
dep_graph: dep_graph.clone(),
inlined_items: TypedArena::new()
inlined_bodies: TypedArena::new()
}
}
@ -263,20 +251,15 @@ pub struct Map<'ast> {
///
/// Also, indexing is pretty quick when you've got a vector and
/// plain old integers.
map: RefCell<Vec<MapEntry<'ast>>>,
map: Vec<MapEntry<'ast>>,
definitions: Definitions,
/// All NodeIds that are numerically greater or equal to this value come
/// from inlined items.
local_node_id_watermark: NodeId,
/// Bodies inlined from other crates are cached here.
inlined_bodies: RefCell<DefIdMap<&'ast Body>>,
}
impl<'ast> Map<'ast> {
pub fn is_inlined_node_id(&self, id: NodeId) -> bool {
id >= self.local_node_id_watermark
}
/// Registers a read in the dependency graph of the AST node with
/// the given `id`. This needs to be called each time a public
/// function returns the HIR for a node -- in other words, when it
@ -289,111 +272,71 @@ impl<'ast> Map<'ast> {
}
fn dep_node(&self, id0: NodeId) -> DepNode<DefId> {
let map = self.map.borrow();
let mut id = id0;
if !self.is_inlined_node_id(id) {
let mut last_expr = None;
loop {
let entry = map[id.as_usize()];
match entry {
EntryItem(..) |
EntryTraitItem(..) |
EntryImplItem(..) => {
if let Some(last_id) = last_expr {
// The body may have a separate dep node
if entry.is_body_owner(last_id) {
let def_id = self.local_def_id(id);
return DepNode::HirBody(def_id);
}
}
return DepNode::Hir(self.local_def_id(id));
}
EntryVariant(p, v) => {
id = p;
if last_expr.is_some() {
if v.node.disr_expr.map(|e| e.node_id) == last_expr {
// The enum parent holds both Hir and HirBody nodes.
let def_id = self.local_def_id(id);
return DepNode::HirBody(def_id);
}
let mut last_expr = None;
loop {
let entry = self.map[id.as_usize()];
match entry {
EntryItem(..) |
EntryTraitItem(..) |
EntryImplItem(..) => {
if let Some(last_id) = last_expr {
// The body may have a separate dep node
if entry.is_body_owner(last_id) {
let def_id = self.local_def_id(id);
return DepNode::HirBody(def_id);
}
}
EntryForeignItem(p, _) |
EntryField(p, _) |
EntryStmt(p, _) |
EntryTy(p, _) |
EntryTraitRef(p, _) |
EntryLocal(p, _) |
EntryPat(p, _) |
EntryBlock(p, _) |
EntryStructCtor(p, _) |
EntryLifetime(p, _) |
EntryTyParam(p, _) |
EntryVisibility(p, _) =>
id = p,
EntryExpr(p, _) => {
last_expr = Some(id);
id = p;
}
RootCrate => {
return DepNode::Hir(DefId::local(CRATE_DEF_INDEX));
}
RootInlinedParent(_) =>
bug!("node {} has inlined ancestor but is not inlined", id0),
NotPresent =>
// Some nodes, notably macro definitions, are not
// present in the map for whatever reason, but
// they *do* have def-ids. So if we encounter an
// empty hole, check for that case.
return self.opt_local_def_id(id)
.map(|def_id| DepNode::Hir(def_id))
.unwrap_or_else(|| {
bug!("Walking parents from `{}` \
led to `NotPresent` at `{}`",
id0, id)
}),
return DepNode::Hir(self.local_def_id(id));
}
}
} else {
// reading from an inlined def-id is really a read out of
// the metadata from which we loaded the item.
loop {
match map[id.as_usize()] {
EntryItem(p, _) |
EntryForeignItem(p, _) |
EntryTraitItem(p, _) |
EntryImplItem(p, _) |
EntryVariant(p, _) |
EntryField(p, _) |
EntryExpr(p, _) |
EntryStmt(p, _) |
EntryTy(p, _) |
EntryTraitRef(p, _) |
EntryLocal(p, _) |
EntryPat(p, _) |
EntryBlock(p, _) |
EntryStructCtor(p, _) |
EntryLifetime(p, _) |
EntryTyParam(p, _) |
EntryVisibility(p, _) =>
id = p,
RootInlinedParent(parent) =>
return DepNode::MetaData(parent.def_id),
EntryVariant(p, v) => {
id = p;
RootCrate =>
bug!("node {} has crate ancestor but is inlined", id0),
NotPresent =>
bug!("node {} is inlined but not present in map", id0),
if last_expr.is_some() {
if v.node.disr_expr.map(|e| e.node_id) == last_expr {
// The enum parent holds both Hir and HirBody nodes.
let def_id = self.local_def_id(id);
return DepNode::HirBody(def_id);
}
}
}
EntryForeignItem(p, _) |
EntryField(p, _) |
EntryStmt(p, _) |
EntryTy(p, _) |
EntryTraitRef(p, _) |
EntryLocal(p, _) |
EntryPat(p, _) |
EntryBlock(p, _) |
EntryStructCtor(p, _) |
EntryLifetime(p, _) |
EntryTyParam(p, _) |
EntryVisibility(p, _) =>
id = p,
EntryExpr(p, _) => {
last_expr = Some(id);
id = p;
}
RootCrate => {
return DepNode::Hir(DefId::local(CRATE_DEF_INDEX));
}
NotPresent =>
// Some nodes, notably macro definitions, are not
// present in the map for whatever reason, but
// they *do* have def-ids. So if we encounter an
// empty hole, check for that case.
return self.opt_local_def_id(id)
.map(|def_id| DepNode::Hir(def_id))
.unwrap_or_else(|| {
bug!("Walking parents from `{}` \
led to `NotPresent` at `{}`",
id0, id)
}),
}
}
}
@ -442,11 +385,11 @@ impl<'ast> Map<'ast> {
}
fn entry_count(&self) -> usize {
self.map.borrow().len()
self.map.len()
}
fn find_entry(&self, id: NodeId) -> Option<MapEntry<'ast>> {
self.map.borrow().get(id.as_usize()).cloned()
self.map.get(id.as_usize()).cloned()
}
pub fn krate(&self) -> &'ast Crate {
@ -483,7 +426,7 @@ impl<'ast> Map<'ast> {
/// for embedded constant expressions (e.g. `N` in `[T; N]`).
pub fn body_owner(&self, BodyId { node_id }: BodyId) -> NodeId {
let parent = self.get_parent_node(node_id);
if self.map.borrow()[parent.as_usize()].is_body_owner(node_id) {
if self.map[parent.as_usize()].is_body_owner(node_id) {
parent
} else {
node_id
@ -644,11 +587,7 @@ impl<'ast> Map<'ast> {
}
pub fn get_parent_did(&self, id: NodeId) -> DefId {
let parent = self.get_parent(id);
match self.find_entry(parent) {
Some(RootInlinedParent(ii)) => ii.def_id,
_ => self.local_def_id(parent)
}
self.local_def_id(self.get_parent(id))
}
pub fn get_foreign_abi(&self, id: NodeId) -> Abi {
@ -660,8 +599,6 @@ impl<'ast> Map<'ast> {
_ => None
}
}
/// Wrong but OK, because the only inlined foreign items are intrinsics.
Some(RootInlinedParent(_)) => Some(Abi::RustIntrinsic),
_ => None
};
match abi {
@ -737,11 +674,17 @@ impl<'ast> Map<'ast> {
}
}
pub fn expect_inlined_body(&self, id: NodeId) -> &'ast Body {
match self.find_entry(id) {
Some(RootInlinedParent(inlined_item)) => &inlined_item.body,
_ => bug!("expected inlined item, found {}", self.node_to_string(id)),
}
pub fn get_inlined_body(&self, def_id: DefId) -> Option<&'ast Body> {
self.inlined_bodies.borrow().get(&def_id).map(|&body| {
self.dep_graph.read(DepNode::MetaData(def_id));
body
})
}
pub fn intern_inlined_body(&self, def_id: DefId, body: Body) -> &'ast Body {
let body = self.forest.inlined_bodies.alloc(body);
self.inlined_bodies.borrow_mut().insert(def_id, body);
body
}
/// Returns the name associated with the given NodeId's AST.
@ -824,7 +767,6 @@ impl<'ast> Map<'ast> {
Some(EntryVisibility(_, v)) => bug!("unexpected Visibility {:?}", v),
Some(RootCrate) => self.forest.krate.span,
Some(RootInlinedParent(parent)) => parent.body.value.span,
Some(NotPresent) | None => {
bug!("hir::map::Map::span: id not in map: {:?}", id)
}
@ -973,41 +915,15 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest,
entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
}
let local_node_id_watermark = NodeId::new(map.len());
Map {
forest: forest,
dep_graph: forest.dep_graph.clone(),
map: RefCell::new(map),
map: map,
definitions: definitions,
local_node_id_watermark: local_node_id_watermark,
inlined_bodies: RefCell::new(DefIdMap()),
}
}
/// Used for bodies loaded from external crate that are being inlined into this
/// crate.
pub fn map_decoded_body<'ast>(map: &Map<'ast>,
def_id: DefId,
body: Body,
parent_id: NodeId)
-> &'ast Body {
let _ignore = map.forest.dep_graph.in_ignore();
let ii = map.forest.inlined_items.alloc(InlinedItem {
def_id: def_id,
body: body
});
let mut collector = NodeCollector::extend(map.krate(),
ii,
parent_id,
mem::replace(&mut *map.map.borrow_mut(), vec![]));
collector.visit_body(&ii.body);
*map.map.borrow_mut() = collector.map;
&ii.body
}
/// Identical to the `PpAnn` implementation for `hir::Crate`,
/// except it avoids creating a dependency on the whole crate.
impl<'ast> print::PpAnn for Map<'ast> {

View file

@ -65,6 +65,7 @@ pub struct GlobalArenas<'tcx> {
trait_def: TypedArena<ty::TraitDef>,
adt_def: TypedArena<ty::AdtDef>,
mir: TypedArena<RefCell<Mir<'tcx>>>,
tables: TypedArena<ty::Tables<'tcx>>,
}
impl<'tcx> GlobalArenas<'tcx> {
@ -75,6 +76,7 @@ impl<'tcx> GlobalArenas<'tcx> {
trait_def: TypedArena::new(),
adt_def: TypedArena::new(),
mir: TypedArena::new(),
tables: TypedArena::new(),
}
}
}
@ -189,6 +191,7 @@ pub struct CommonTypes<'tcx> {
pub err: Ty<'tcx>,
}
#[derive(RustcEncodable, RustcDecodable)]
pub struct Tables<'tcx> {
/// Resolved definitions for `<T>::X` associated paths.
pub type_relative_path_defs: NodeMap<Def>,
@ -399,7 +402,7 @@ pub struct GlobalCtxt<'tcx> {
free_region_maps: RefCell<NodeMap<FreeRegionMap>>,
// FIXME: jroesch make this a refcell
pub tables: RefCell<Tables<'tcx>>,
pub tables: RefCell<DepTrackingMap<maps::Tables<'tcx>>>,
/// Maps from a trait item to the trait item "descriptor"
pub associated_items: RefCell<DepTrackingMap<maps::AssociatedItems<'tcx>>>,
@ -651,6 +654,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.global_arenas.mir.alloc(RefCell::new(mir))
}
pub fn alloc_tables(self, tables: ty::Tables<'gcx>) -> &'gcx ty::Tables<'gcx> {
self.global_arenas.tables.alloc(tables)
}
pub fn alloc_trait_def(self, def: ty::TraitDef) -> &'gcx ty::TraitDef {
self.global_arenas.trait_def.alloc(def)
}
@ -749,7 +756,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
variance_computed: Cell::new(false),
sess: s,
trait_map: resolutions.trait_map,
tables: RefCell::new(Tables::empty()),
tables: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),

View file

@ -48,3 +48,4 @@ dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc<Vec<attr::ReprAttr>> }
dep_map_ty! { Mir: Mir(DefId) -> &'tcx RefCell<mir::Mir<'tcx>> }
dep_map_ty! { ClosureKinds: ItemSignature(DefId) -> ty::ClosureKind }
dep_map_ty! { ClosureTypes: ItemSignature(DefId) -> ty::ClosureTy<'tcx> }
dep_map_ty! { Tables: Tables(DefId) -> &'tcx ty::Tables<'tcx> }

View file

@ -321,7 +321,7 @@ pub struct MethodCallee<'tcx> {
/// needed to add to the side tables. Thus to disambiguate
/// we also keep track of whether there's an adjustment in
/// our key.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct MethodCall {
pub expr_id: NodeId,
pub autoderef: u32
@ -501,7 +501,7 @@ impl<T> Slice<T> {
/// Upvars do not get their own node-id. Instead, we use the pair of
/// the original var id (that is, the root variable that is referenced
/// by the upvar) and the id of the closure expression.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct UpvarId {
pub var_id: NodeId,
pub closure_expr_id: NodeId,
@ -1917,19 +1917,30 @@ impl BorrowKind {
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn tables(self) -> Ref<'a, Tables<'gcx>> {
self.tables.borrow()
}
pub fn body_tables(self, body: hir::BodyId) -> &'a Tables<'gcx> {
pub fn body_tables(self, body: hir::BodyId) -> &'gcx Tables<'gcx> {
self.item_tables(self.map.body_owner_def_id(body))
}
pub fn item_tables(self, _def_id: DefId) -> &'a Tables<'gcx> {
// HACK(eddyb) temporarily work around RefCell until proper per-body tables
unsafe {
mem::transmute::<&Tables, &Tables>(&self.tables())
}
pub fn item_tables(self, def_id: DefId) -> &'gcx Tables<'gcx> {
self.tables.memoize(def_id, || {
if def_id.is_local() {
// Closures' tables come from their outermost function,
// as they are part of the same "inference environment".
let outer_def_id = self.closure_base_def_id(def_id);
if outer_def_id != def_id {
return self.item_tables(outer_def_id);
}
bug!("No def'n found for {:?} in tcx.tables", def_id);
}
// Cross-crate side-tables only exist alongside serialized HIR.
self.sess.cstore.maybe_get_item_body(self.global_tcx(), def_id).map(|_| {
self.tables.borrow()[&def_id]
}).unwrap_or_else(|| {
bug!("tcx.item_tables({:?}): missing from metadata", def_id)
})
})
}
pub fn expr_span(self, id: NodeId) -> Span {