Skip to content

Commit

Permalink
Auto merge of #38944 - michaelwoerister:incr-generics-partitioning, r…
Browse files Browse the repository at this point in the history
…=nikomatsakis

trans: Treat generics like regular functions, not like #[inline] function, during CGU partitioning

This PR makes generics be treated just like regular functions during CGU partitioning:

+ the function instantiation is placed in a codegen unit based on the function's DefPath,
+ unless it is marked with `#[inline]`  -- which causes a private copy of the function to be placed in every referencing codegen unit.

This has the following effects:
+ Multi codegen unit builds will become faster because code for generic functions is duplicated less.
+ Multi codegen unit builds might have lower runtime performance, since generics are not available for inlining automatically any more.
+ Single codegen unit builds are not affected one way or the other.

This partitioning scheme is particularly good for incremental compilation as it drastically reduces the number of false positives during codegen unit invalidation.

I'd love to have a benchmark suite for estimating the effect on runtime performance for changes like this one.

r? @nikomatsakis

cc @rust-lang/compiler
  • Loading branch information
bors committed Jan 14, 2017
2 parents b4c0207 + fc9dfca commit ef04fc8
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 187 deletions.
10 changes: 2 additions & 8 deletions src/librustc/middle/cstore.rs
Expand Up @@ -211,6 +211,7 @@ pub trait CrateStore<'tcx> {
fn is_foreign_item(&self, did: DefId) -> bool;
fn is_dllimport_foreign_item(&self, def: DefId) -> bool;
fn is_statically_included_foreign_item(&self, def_id: DefId) -> bool;
fn is_exported_symbol(&self, def_id: DefId) -> bool;

// crate metadata
fn dylib_dependency_formats(&self, cnum: CrateNum)
Expand Down Expand Up @@ -258,11 +259,6 @@ pub trait CrateStore<'tcx> {
fn get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> Mir<'tcx>;
fn is_item_mir_available(&self, def: DefId) -> bool;

/// Take a look if we need to inline or monomorphize this. If so, we
/// will emit code for this item in the local crate, and thus
/// create a translation item for it.
fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool;

// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
fn crates(&self) -> Vec<CrateNum>;
Expand Down Expand Up @@ -368,6 +364,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
fn is_foreign_item(&self, did: DefId) -> bool { bug!("is_foreign_item") }
fn is_dllimport_foreign_item(&self, id: DefId) -> bool { false }
fn is_statically_included_foreign_item(&self, def_id: DefId) -> bool { false }
fn is_exported_symbol(&self, def_id: DefId) -> bool { false }

// crate metadata
fn dylib_dependency_formats(&self, cnum: CrateNum)
Expand Down Expand Up @@ -436,9 +433,6 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
fn is_item_mir_available(&self, def: DefId) -> bool {
bug!("is_item_mir_available")
}
fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool {
bug!("can_have_local_instance")
}

// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_metadata/creader.rs
Expand Up @@ -302,10 +302,13 @@ impl<'a> CrateLoader<'a> {
crate_root.def_path_table.decode(&metadata)
});

let exported_symbols = crate_root.exported_symbols.decode(&metadata).collect();

let mut cmeta = cstore::CrateMetadata {
name: name,
extern_crate: Cell::new(None),
def_path_table: def_path_table,
exported_symbols: exported_symbols,
proc_macros: crate_root.macro_derive_registrar.map(|_| {
self.load_derive_macros(&crate_root, dylib.clone().map(|p| p.0), span)
}),
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_metadata/cstore.rs
Expand Up @@ -80,6 +80,8 @@ pub struct CrateMetadata {
/// compilation support.
pub def_path_table: DefPathTable,

pub exported_symbols: FxHashSet<DefIndex>,

pub dep_kind: Cell<DepKind>,
pub source: CrateSource,

Expand Down
9 changes: 4 additions & 5 deletions src/librustc_metadata/cstore_impl.rs
Expand Up @@ -226,6 +226,10 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
self.do_is_statically_included_foreign_item(def_id)
}

fn is_exported_symbol(&self, def_id: DefId) -> bool {
self.get_crate_data(def_id.krate).exported_symbols.contains(&def_id.index)
}

fn is_dllimport_foreign_item(&self, def_id: DefId) -> bool {
if def_id.krate == LOCAL_CRATE {
self.dllimport_foreign_items.borrow().contains(&def_id.index)
Expand Down Expand Up @@ -466,11 +470,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
self.get_crate_data(def.krate).is_item_mir_available(def.index)
}

fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool {
self.dep_graph.read(DepNode::MetaData(def));
def.is_local() || self.get_crate_data(def.krate).can_have_local_instance(tcx, def.index)
}

fn crates(&self) -> Vec<CrateNum>
{
let mut result = vec![];
Expand Down
40 changes: 7 additions & 33 deletions src/librustc_metadata/decoder.rs
Expand Up @@ -445,14 +445,6 @@ impl<'tcx> EntryKind<'tcx> {
EntryKind::Closure(_) => return None,
})
}
fn is_const_fn(&self, meta: &CrateMetadata) -> bool {
let constness = match *self {
EntryKind::Method(data) => data.decode(meta).fn_data.constness,
EntryKind::Fn(data) => data.decode(meta).constness,
_ => hir::Constness::NotConst,
};
constness == hir::Constness::Const
}
}

impl<'a, 'tcx> CrateMetadata {
Expand Down Expand Up @@ -804,29 +796,6 @@ impl<'a, 'tcx> CrateMetadata {
self.maybe_entry(id).and_then(|item| item.decode(self).mir).is_some()
}

pub fn can_have_local_instance(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: DefIndex) -> bool {
self.maybe_entry(id).map_or(false, |item| {
let item = item.decode(self);
// if we don't have a MIR, then this item was never meant to be locally instantiated
// or we have a bug in the metadata serialization
item.mir.is_some() && (
// items with generics always can have local instances if monomorphized
item.generics.map_or(false, |generics| {
let generics = generics.decode((self, tcx));
generics.parent_types != 0 || !generics.types.is_empty()
}) ||
match item.kind {
EntryKind::Closure(_) => true,
_ => false,
} ||
item.kind.is_const_fn(self) ||
attr::requests_inline(&self.get_attributes(&item))
)
})
}

pub fn maybe_get_item_mir(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: DefIndex)
Expand Down Expand Up @@ -1031,7 +1000,7 @@ impl<'a, 'tcx> CrateMetadata {
}

pub fn get_exported_symbols(&self) -> Vec<DefId> {
self.root.exported_symbols.decode(self).map(|index| self.local_def_id(index)).collect()
self.exported_symbols.iter().map(|&index| self.local_def_id(index)).collect()
}

pub fn get_macro(&self, id: DefIndex) -> (ast::Name, MacroDef) {
Expand All @@ -1043,7 +1012,12 @@ impl<'a, 'tcx> CrateMetadata {
}

pub fn is_const_fn(&self, id: DefIndex) -> bool {
self.entry(id).kind.is_const_fn(self)
let constness = match self.entry(id).kind {
EntryKind::Method(data) => data.decode(self).fn_data.constness,
EntryKind::Fn(data) => data.decode(self).constness,
_ => hir::Constness::NotConst,
};
constness == hir::Constness::Const
}

pub fn is_foreign_item(&self, id: DefIndex) -> bool {
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_trans/back/symbol_names.rs
Expand Up @@ -152,6 +152,15 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
assert!(!substs.has_erasable_regions());
assert!(!substs.needs_subst());
substs.visit_with(&mut hasher);

// If this is an instance of a generic function, we also hash in
// the ID of the instantiating crate. This avoids symbol conflicts
// in case the same instances is emitted in two crates of the same
// project.
if substs.types().next().is_some() {
hasher.hash(scx.tcx().crate_name.as_str());
hasher.hash(scx.sess().local_crate_disambiguator().as_str());
}
}
});

Expand Down
80 changes: 53 additions & 27 deletions src/librustc_trans/collector.rs
Expand Up @@ -212,7 +212,7 @@ use glue::{self, DropGlueKind};
use monomorphize::{self, Instance};
use util::nodemap::{FxHashSet, FxHashMap, DefIdMap};

use trans_item::{TransItem, DefPathBasedNames};
use trans_item::{TransItem, DefPathBasedNames, InstantiationMode};

use std::iter;

Expand Down Expand Up @@ -337,6 +337,10 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
}
TransItem::Static(node_id) => {
let def_id = scx.tcx().map.local_def_id(node_id);

// Sanity check whether this ended up being collected accidentally
debug_assert!(should_trans_locally(scx.tcx(), def_id));

let ty = scx.tcx().item_type(def_id);
let ty = glue::get_drop_glue_type(scx, ty);
neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
Expand All @@ -346,6 +350,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors);
}
TransItem::Fn(instance) => {
// Sanity check whether this ended up being collected accidentally
debug_assert!(should_trans_locally(scx.tcx(), instance.def));

// Keep track of the monomorphization recursion depth
recursion_depth_reset = Some(check_recursion_limit(scx.tcx(),
instance,
Expand Down Expand Up @@ -374,7 +381,7 @@ fn record_inlining_canditates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
callees: &[TransItem<'tcx>],
inlining_map: &mut InliningMap<'tcx>) {
let is_inlining_candidate = |trans_item: &TransItem<'tcx>| {
trans_item.needs_local_copy(tcx)
trans_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy
};

let inlining_candidates = callees.into_iter()
Expand Down Expand Up @@ -490,15 +497,16 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
.require(ExchangeMallocFnLangItem)
.unwrap_or_else(|e| self.scx.sess().fatal(&e));

assert!(can_have_local_instance(self.scx.tcx(), exchange_malloc_fn_def_id));
let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id);
let exchange_malloc_fn_trans_item =
create_fn_trans_item(self.scx,
exchange_malloc_fn_def_id,
empty_substs,
self.param_substs);
if should_trans_locally(self.scx.tcx(), exchange_malloc_fn_def_id) {
let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id);
let exchange_malloc_fn_trans_item =
create_fn_trans_item(self.scx,
exchange_malloc_fn_def_id,
empty_substs,
self.param_substs);

self.output.push(exchange_malloc_fn_trans_item);
self.output.push(exchange_malloc_fn_trans_item);
}
}
_ => { /* not interesting */ }
}
Expand Down Expand Up @@ -609,7 +617,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
match tcx.item_type(def_id).sty {
ty::TyFnDef(def_id, _, f) => {
// Some constructors also have type TyFnDef but they are
// always instantiated inline and don't result in
// always instantiated inline and don't result in a
// translation item. Same for FFI functions.
if let Some(hir_map::NodeForeignItem(_)) = tcx.map.get_if_local(def_id) {
return false;
Expand All @@ -625,7 +633,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
_ => return false
}

can_have_local_instance(tcx, def_id)
should_trans_locally(tcx, def_id)
}
}

Expand Down Expand Up @@ -675,10 +683,27 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
}
}

fn can_have_local_instance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> bool {
tcx.sess.cstore.can_have_local_instance(tcx, def_id)
// Returns true if we should translate an instance in the local crate.
// Returns false if we can just link to the upstream crate and therefore don't
// need a translation item.
fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> bool {
if def_id.is_local() {
true
} else {
if tcx.sess.cstore.is_exported_symbol(def_id) ||
tcx.sess.cstore.is_foreign_item(def_id) {
// We can link to the item in question, no instance needed in this
// crate
false
} else {
if !tcx.sess.cstore.is_item_mir_available(def_id) {
bug!("Cannot create local trans-item for {:?}", def_id)
}
true
}
}
}

fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
Expand All @@ -698,14 +723,15 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
// Make sure the BoxFreeFn lang-item gets translated if there is a boxed value.
if let ty::TyBox(content_type) = ty.sty {
let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem);
assert!(can_have_local_instance(scx.tcx(), def_id));
let box_free_fn_trans_item =
create_fn_trans_item(scx,
def_id,
scx.tcx().mk_substs(iter::once(Kind::from(content_type))),
scx.tcx().intern_substs(&[]));

output.push(box_free_fn_trans_item);

if should_trans_locally(scx.tcx(), def_id) {
let box_free_fn_trans_item =
create_fn_trans_item(scx,
def_id,
scx.tcx().mk_substs(iter::once(Kind::from(content_type))),
scx.tcx().intern_substs(&[]));
output.push(box_free_fn_trans_item);
}
}

// If the type implements Drop, also add a translation item for the
Expand Down Expand Up @@ -735,7 +761,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
_ => bug!()
};

if can_have_local_instance(scx.tcx(), destructor_did) {
if should_trans_locally(scx.tcx(), destructor_did) {
let trans_item = create_fn_trans_item(scx,
destructor_did,
substs,
Expand Down Expand Up @@ -1080,7 +1106,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
None
}
})
.filter(|&(def_id, _)| can_have_local_instance(scx.tcx(), def_id))
.filter(|&(def_id, _)| should_trans_locally(scx.tcx(), def_id))
.map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs));
output.extend(methods);
}
Expand Down Expand Up @@ -1255,7 +1281,7 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, '
continue;
}

if can_have_local_instance(tcx, method.def_id) {
if should_trans_locally(tcx, method.def_id) {
let item = create_fn_trans_item(scx,
method.def_id,
callee_substs,
Expand Down

0 comments on commit ef04fc8

Please sign in to comment.