Skip to content

Commit 5f90947

Browse files
trans: Treat generics like regular functions, not like #[inline] functions during CGU partitioning.
1 parent 02c7b11 commit 5f90947

File tree

9 files changed

+145
-124
lines changed

9 files changed

+145
-124
lines changed

src/librustc_trans/collector.rs

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ use glue::{self, DropGlueKind};
212212
use monomorphize::{self, Instance};
213213
use util::nodemap::{FxHashSet, FxHashMap, DefIdMap};
214214

215-
use trans_item::{TransItem, DefPathBasedNames};
215+
use trans_item::{TransItem, DefPathBasedNames, InstantiationMode};
216216

217217
use std::iter;
218218

@@ -337,6 +337,10 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
337337
}
338338
TransItem::Static(node_id) => {
339339
let def_id = scx.tcx().map.local_def_id(node_id);
340+
341+
// Sanity check whether this ended up being collected accidentally
342+
debug_assert!(should_trans_locally(scx.tcx(), def_id));
343+
340344
let ty = scx.tcx().item_type(def_id);
341345
let ty = glue::get_drop_glue_type(scx, ty);
342346
neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
@@ -346,6 +350,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
346350
collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors);
347351
}
348352
TransItem::Fn(instance) => {
353+
// Sanity check whether this ended up being collected accidentally
354+
debug_assert!(should_trans_locally(scx.tcx(), instance.def));
355+
349356
// Keep track of the monomorphization recursion depth
350357
recursion_depth_reset = Some(check_recursion_limit(scx.tcx(),
351358
instance,
@@ -374,7 +381,7 @@ fn record_inlining_canditates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
374381
callees: &[TransItem<'tcx>],
375382
inlining_map: &mut InliningMap<'tcx>) {
376383
let is_inlining_candidate = |trans_item: &TransItem<'tcx>| {
377-
trans_item.needs_local_copy(tcx)
384+
trans_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy
378385
};
379386

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

493-
assert!(can_have_local_instance(self.scx.tcx(), exchange_malloc_fn_def_id));
494-
let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id);
495-
let exchange_malloc_fn_trans_item =
496-
create_fn_trans_item(self.scx,
497-
exchange_malloc_fn_def_id,
498-
empty_substs,
499-
self.param_substs);
500+
if should_trans_locally(self.scx.tcx(), exchange_malloc_fn_def_id) {
501+
let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id);
502+
let exchange_malloc_fn_trans_item =
503+
create_fn_trans_item(self.scx,
504+
exchange_malloc_fn_def_id,
505+
empty_substs,
506+
self.param_substs);
500507

501-
self.output.push(exchange_malloc_fn_trans_item);
508+
self.output.push(exchange_malloc_fn_trans_item);
509+
}
502510
}
503511
_ => { /* not interesting */ }
504512
}
@@ -609,7 +617,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
609617
match tcx.item_type(def_id).sty {
610618
ty::TyFnDef(def_id, _, f) => {
611619
// Some constructors also have type TyFnDef but they are
612-
// always instantiated inline and don't result in
620+
// always instantiated inline and don't result in a
613621
// translation item. Same for FFI functions.
614622
if let Some(hir_map::NodeForeignItem(_)) = tcx.map.get_if_local(def_id) {
615623
return false;
@@ -625,7 +633,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
625633
_ => return false
626634
}
627635

628-
can_have_local_instance(tcx, def_id)
636+
should_trans_locally(tcx, def_id)
629637
}
630638
}
631639

@@ -675,10 +683,27 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
675683
}
676684
}
677685

678-
fn can_have_local_instance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
679-
def_id: DefId)
680-
-> bool {
681-
tcx.sess.cstore.can_have_local_instance(tcx, def_id)
686+
// Returns true if we should translate an instance in the local crate.
687+
// Returns false if we can just link to the upstream crate and therefore don't
688+
// need a translation item.
689+
fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
690+
def_id: DefId)
691+
-> bool {
692+
if def_id.is_local() {
693+
true
694+
} else {
695+
if tcx.sess.cstore.is_exported_symbol(def_id) ||
696+
tcx.sess.cstore.is_foreign_item(def_id) {
697+
// We can link to the item in question, no instance needed in this
698+
// crate
699+
false
700+
} else {
701+
if !tcx.sess.cstore.can_have_local_instance(tcx, def_id) {
702+
bug!("Cannot create local trans-item for {:?}", def_id)
703+
}
704+
true
705+
}
706+
}
682707
}
683708

684709
fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
@@ -698,14 +723,15 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
698723
// Make sure the BoxFreeFn lang-item gets translated if there is a boxed value.
699724
if let ty::TyBox(content_type) = ty.sty {
700725
let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem);
701-
assert!(can_have_local_instance(scx.tcx(), def_id));
702-
let box_free_fn_trans_item =
703-
create_fn_trans_item(scx,
704-
def_id,
705-
scx.tcx().mk_substs(iter::once(Kind::from(content_type))),
706-
scx.tcx().intern_substs(&[]));
707-
708-
output.push(box_free_fn_trans_item);
726+
727+
if should_trans_locally(scx.tcx(), def_id) {
728+
let box_free_fn_trans_item =
729+
create_fn_trans_item(scx,
730+
def_id,
731+
scx.tcx().mk_substs(iter::once(Kind::from(content_type))),
732+
scx.tcx().intern_substs(&[]));
733+
output.push(box_free_fn_trans_item);
734+
}
709735
}
710736

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

738-
if can_have_local_instance(scx.tcx(), destructor_did) {
764+
if should_trans_locally(scx.tcx(), destructor_did) {
739765
let trans_item = create_fn_trans_item(scx,
740766
destructor_did,
741767
substs,
@@ -1080,7 +1106,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
10801106
None
10811107
}
10821108
})
1083-
.filter(|&(def_id, _)| can_have_local_instance(scx.tcx(), def_id))
1109+
.filter(|&(def_id, _)| should_trans_locally(scx.tcx(), def_id))
10841110
.map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs));
10851111
output.extend(methods);
10861112
}
@@ -1255,7 +1281,7 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, '
12551281
continue;
12561282
}
12571283

1258-
if can_have_local_instance(tcx, method.def_id) {
1284+
if should_trans_locally(tcx, method.def_id) {
12591285
let item = create_fn_trans_item(scx,
12601286
method.def_id,
12611287
callee_substs,

src/librustc_trans/partitioning.rs

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ use std::sync::Arc;
133133
use symbol_map::SymbolMap;
134134
use syntax::ast::NodeId;
135135
use syntax::symbol::{Symbol, InternedString};
136-
use trans_item::TransItem;
136+
use trans_item::{TransItem, InstantiationMode};
137137
use util::nodemap::{FxHashMap, FxHashSet};
138138

139139
pub enum PartitioningStrategy {
@@ -326,13 +326,15 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>,
326326
let tcx = scx.tcx();
327327
let mut roots = FxHashSet();
328328
let mut codegen_units = FxHashMap();
329+
let is_incremental_build = tcx.sess.opts.incremental.is_some();
329330

330331
for trans_item in trans_items {
331-
let is_root = !trans_item.is_instantiated_only_on_demand(tcx);
332+
let is_root = trans_item.instantiation_mode(tcx) == InstantiationMode::GloballyShared;
332333

333334
if is_root {
334335
let characteristic_def_id = characteristic_def_id_of_trans_item(scx, trans_item);
335-
let is_volatile = trans_item.is_generic_fn();
336+
let is_volatile = is_incremental_build &&
337+
trans_item.is_generic_fn();
336338

337339
let codegen_unit_name = match characteristic_def_id {
338340
Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile),
@@ -350,25 +352,9 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>,
350352
Some(explicit_linkage) => explicit_linkage,
351353
None => {
352354
match trans_item {
355+
TransItem::Fn(..) |
353356
TransItem::Static(..) => llvm::ExternalLinkage,
354357
TransItem::DropGlue(..) => unreachable!(),
355-
// Is there any benefit to using ExternalLinkage?:
356-
TransItem::Fn(ref instance) => {
357-
if instance.substs.types().next().is_none() {
358-
// This is a non-generic functions, we always
359-
// make it visible externally on the chance that
360-
// it might be used in another codegen unit.
361-
// Later on base::internalize_symbols() will
362-
// assign "internal" linkage to those symbols
363-
// that are not referenced from other codegen
364-
// units (and are not publicly visible).
365-
llvm::ExternalLinkage
366-
} else {
367-
// In the current setup, generic functions cannot
368-
// be roots.
369-
unreachable!()
370-
}
371-
}
372358
}
373359
}
374360
};
@@ -448,29 +434,14 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit
448434
if let Some(linkage) = codegen_unit.items.get(&trans_item) {
449435
// This is a root, just copy it over
450436
new_codegen_unit.items.insert(trans_item, *linkage);
451-
} else if initial_partitioning.roots.contains(&trans_item) {
452-
// This item will be instantiated in some other codegen unit,
453-
// so we just add it here with AvailableExternallyLinkage
454-
// FIXME(mw): I have not seen it happening yet but having
455-
// available_externally here could potentially lead
456-
// to the same problem with exception handling tables
457-
// as in the case below.
458-
new_codegen_unit.items.insert(trans_item,
459-
llvm::AvailableExternallyLinkage);
460-
} else if trans_item.is_from_extern_crate() && !trans_item.is_generic_fn() {
461-
// FIXME(mw): It would be nice if we could mark these as
462-
// `AvailableExternallyLinkage`, since they should have
463-
// been instantiated in the extern crate. But this
464-
// sometimes leads to crashes on Windows because LLVM
465-
// does not handle exception handling table instantiation
466-
// reliably in that case.
467-
new_codegen_unit.items.insert(trans_item, llvm::InternalLinkage);
468437
} else {
469-
// We can't be sure if this will also be instantiated
470-
// somewhere else, so we add an instance here with
471-
// InternalLinkage so we don't get any conflicts.
472-
new_codegen_unit.items.insert(trans_item,
473-
llvm::InternalLinkage);
438+
if initial_partitioning.roots.contains(&trans_item) {
439+
bug!("GloballyShared trans-item inlined into other CGU: \
440+
{:?}", trans_item);
441+
}
442+
443+
// This is a cgu-private copy
444+
new_codegen_unit.items.insert(trans_item, llvm::InternalLinkage);
474445
}
475446
}
476447

src/librustc_trans/trans_item.rs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ pub enum TransItem<'tcx> {
4545
Static(NodeId)
4646
}
4747

48+
/// Describes how a translation item will be instantiated in object files.
49+
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
50+
pub enum InstantiationMode {
51+
/// There will be exactly one instance of the given TransItem. It will have
52+
/// external linkage so that it can be linked to from other codegen units.
53+
GloballyShared,
54+
55+
/// Each codegen unit containing a reference to the given TransItem will
56+
/// have its own private copy of the function (with internal linkage).
57+
LocalCopy,
58+
}
59+
4860
impl<'a, 'tcx> TransItem<'tcx> {
4961

5062
pub fn define(&self, ccx: &CrateContext<'a, 'tcx>) {
@@ -231,22 +243,21 @@ impl<'a, 'tcx> TransItem<'tcx> {
231243
}
232244
}
233245

234-
/// True if the translation item should only be translated to LLVM IR if
235-
/// it is referenced somewhere (like inline functions, for example).
236-
pub fn is_instantiated_only_on_demand(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool {
237-
if self.explicit_linkage(tcx).is_some() {
238-
return false;
239-
}
240-
246+
pub fn instantiation_mode(&self,
247+
tcx: TyCtxt<'a, 'tcx, 'tcx>)
248+
-> InstantiationMode {
241249
match *self {
242250
TransItem::Fn(ref instance) => {
243-
!instance.def.is_local() ||
244-
instance.substs.types().next().is_some() ||
245-
common::is_closure(tcx, instance.def) ||
246-
attr::requests_inline(&tcx.get_attrs(instance.def)[..])
251+
if self.explicit_linkage(tcx).is_none() &&
252+
(common::is_closure(tcx, instance.def) ||
253+
attr::requests_inline(&tcx.get_attrs(instance.def)[..])) {
254+
InstantiationMode::LocalCopy
255+
} else {
256+
InstantiationMode::GloballyShared
257+
}
247258
}
248-
TransItem::DropGlue(..) => true,
249-
TransItem::Static(..) => false,
259+
TransItem::DropGlue(..) => InstantiationMode::LocalCopy,
260+
TransItem::Static(..) => InstantiationMode::GloballyShared,
250261
}
251262
}
252263

@@ -260,18 +271,6 @@ impl<'a, 'tcx> TransItem<'tcx> {
260271
}
261272
}
262273

263-
/// Returns true if there has to be a local copy of this TransItem in every
264-
/// codegen unit that references it (as with inlined functions, for example)
265-
pub fn needs_local_copy(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool {
266-
// Currently everything that is instantiated only on demand is done so
267-
// with "internal" linkage, so we need a copy to be present in every
268-
// codegen unit.
269-
// This is coincidental: We could also instantiate something only if it
270-
// is referenced (e.g. a regular, private function) but place it in its
271-
// own codegen unit with "external" linkage.
272-
self.is_instantiated_only_on_demand(tcx)
273-
}
274-
275274
pub fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option<llvm::Linkage> {
276275
let def_id = match *self {
277276
TransItem::Fn(ref instance) => instance.def,

src/test/codegen-units/partitioning/extern-generic.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ mod mod3 {
5757
}
5858

5959
// Make sure the two generic functions from the extern crate get instantiated
60-
// privately in every module they are use in.
61-
//~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ extern_generic[Internal] extern_generic-mod1[Internal] extern_generic-mod2[Internal] extern_generic-mod1-mod1[Internal]
62-
//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ extern_generic[Internal] extern_generic-mod1[Internal] extern_generic-mod2[Internal] extern_generic-mod1-mod1[Internal]
60+
// once for the current crate
61+
//~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External]
62+
//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External]
6363

6464
//~ TRANS_ITEM drop-glue i8

src/test/codegen-units/partitioning/local-generic.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
#![allow(dead_code)]
1717
#![crate_type="lib"]
1818

19-
//~ TRANS_ITEM fn local_generic::generic[0]<u32> @@ local_generic[Internal]
20-
//~ TRANS_ITEM fn local_generic::generic[0]<u64> @@ local_generic-mod1[Internal]
21-
//~ TRANS_ITEM fn local_generic::generic[0]<char> @@ local_generic-mod1-mod1[Internal]
22-
//~ TRANS_ITEM fn local_generic::generic[0]<&str> @@ local_generic-mod2[Internal]
19+
//~ TRANS_ITEM fn local_generic::generic[0]<u32> @@ local_generic.volatile[External]
20+
//~ TRANS_ITEM fn local_generic::generic[0]<u64> @@ local_generic.volatile[External]
21+
//~ TRANS_ITEM fn local_generic::generic[0]<char> @@ local_generic.volatile[External]
22+
//~ TRANS_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External]
2323
pub fn generic<T>(x: T) -> T { x }
2424

2525
//~ TRANS_ITEM fn local_generic::user[0] @@ local_generic[External]

src/test/codegen-units/partitioning/vtable-through-const.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,19 @@ fn main() {
7474
// Since Trait1::do_something() is instantiated via its default implementation,
7575
// it is considered a generic and is instantiated here only because it is
7676
// referenced in this module.
77-
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0]<u32> @@ vtable_through_const[Internal]
77+
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0]<u32> @@ vtable_through_const-mod1.volatile[External]
7878

7979
// Although it is never used, Trait1::do_something_else() has to be
8080
// instantiated locally here too, otherwise the <&u32 as &Trait1> vtable
8181
// could not be fully constructed.
82-
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0]<u32> @@ vtable_through_const[Internal]
82+
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0]<u32> @@ vtable_through_const-mod1.volatile[External]
8383
mod1::TRAIT1_REF.do_something();
8484

8585
// Same as above
86-
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0]<u8> @@ vtable_through_const[Internal]
87-
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0]<u8> @@ vtable_through_const[Internal]
86+
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0]<u8> @@ vtable_through_const-mod1.volatile[External]
87+
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0]<u8> @@ vtable_through_const-mod1.volatile[External]
8888
mod1::TRAIT1_GEN_REF.do_something(0u8);
8989

90-
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0]<char> @@ vtable_through_const[Internal]
90+
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0]<char> @@ vtable_through_const-mod1.volatile[External]
9191
mod1::ID_CHAR('x');
9292
}

0 commit comments

Comments
 (0)