Skip to content

Commit c80edc3

Browse files
committed
Auto merge of #48779 - michaelwoerister:share-generics4, r=alexcrichton
Allow for re-using monomorphizations in upstream crates. Followup to #48611. This implementation is pretty much finished modulo failing tests if there are any. Not quite ready for review yet though. ### DESCRIPTION This PR introduces a `share-generics` mode for RLIBs and Rust dylibs. When a crate is compiled in this mode, two things will happen: - before instantiating a monomorphization in the current crate, the compiler will look for that monomorphization in all upstream crates and link to it, if possible. - monomorphizations are not internalized during partitioning. Instead they are added to the list of symbols exported from the crate. This results in less code being translated and LLVMed. However, there are also downsides: - it will impede optimization somewhat, since fewer functions can be internalized, and - Rust dylibs will have bigger symbol tables since they'll also export monomorphizations. Consequently, this PR only enables the `shared-generics` mode for opt-levels `No`, `Less`, `Size`, and `MinSize`, and for when incremental compilation is activated. `-O2` and `-O3` will still generate generic functions per-crate. Another thing to note is that this has a somewhat similar effect as MIR-only RLIBs, in that monomorphizations are shared, but it is less effective because it cannot share monomorphizations between sibling crates: ``` A <--- defines `fn foo<T>() { .. }` / \ / \ B C <--- both call `foo<u32>()` \ / \ / D <--- calls `foo<u32>()` too ``` With `share-generics`, both `B` and `C` have to instantiate `foo<u32>` and only `D` can re-use it (from either `B` or `C`). With MIR-only RLIBs, `B` and `C` would not instantiate anything, and in `D` we would then only instantiate `foo<u32>` once. On the other hand, when there are many leaf crates in the graph (e.g. when compiling many individual test binaries) then the `share-generics` approach will often be more effective. ### TODO - [x] Add codegen test that makes sure monomorphizations can be internalized in non-Rust binaries. - [x] Add codegen-units test that makes sure we share generics. - [x] Add run-make test that makes sure we don't export any monomorphizations from non-Rust binaries. - [x] Review for reproducible-builds implications.
2 parents eb8d08d + 6f1a70d commit c80edc3

File tree

25 files changed

+677
-181
lines changed

25 files changed

+677
-181
lines changed

src/librustc/dep_graph/dep_node.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ define_dep_nodes!( <'tcx>
561561
[] ImplParent(DefId),
562562
[] TraitOfItem(DefId),
563563
[] IsReachableNonGeneric(DefId),
564+
[] IsUnreachableLocalDefinition(DefId),
564565
[] IsMirAvailable(DefId),
565566
[] ItemAttrs(DefId),
566567
[] TransFnAttrs(DefId),
@@ -645,11 +646,12 @@ define_dep_nodes!( <'tcx>
645646

646647
[] InstanceDefSizeEstimate { instance_def: InstanceDef<'tcx> },
647648

648-
[] GetSymbolExportLevel(DefId),
649-
650649
[input] Features,
651650

652651
[] ProgramClausesFor(DefId),
652+
653+
[] UpstreamMonomorphizations(CrateNum),
654+
[] UpstreamMonomorphizationsFor(DefId),
653655
);
654656

655657
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {

src/librustc/ich/impls_ty.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,21 @@ for &'gcx ty::Slice<T>
5353
}
5454
}
5555

56-
impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
57-
for ty::subst::Kind<'gcx> {
56+
impl<'a, 'gcx, T> ToStableHashKey<StableHashingContext<'a>> for &'gcx ty::Slice<T>
57+
where T: HashStable<StableHashingContext<'a>>
58+
{
59+
type KeyType = Fingerprint;
60+
61+
#[inline]
62+
fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
63+
let mut hasher = StableHasher::new();
64+
let mut hcx: StableHashingContext<'a> = hcx.clone();
65+
self.hash_stable(&mut hcx, &mut hasher);
66+
hasher.finish()
67+
}
68+
}
69+
70+
impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'gcx> {
5871
fn hash_stable<W: StableHasherResult>(&self,
5972
hcx: &mut StableHashingContext<'a>,
6073
hasher: &mut StableHasher<W>) {
@@ -67,6 +80,7 @@ for ty::subst::UnpackedKind<'gcx> {
6780
fn hash_stable<W: StableHasherResult>(&self,
6881
hcx: &mut StableHashingContext<'a>,
6982
hasher: &mut StableHasher<W>) {
83+
mem::discriminant(self).hash_stable(hcx, hasher);
7084
match self {
7185
ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher),
7286
ty::subst::UnpackedKind::Type(ty) => ty.hash_stable(hcx, hasher),

src/librustc/middle/exported_symbols.rs

+63-25
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
// except according to those terms.
1010

1111
use hir::def_id::{DefId, LOCAL_CRATE};
12+
use ich::StableHashingContext;
13+
use rustc_data_structures::stable_hasher::{StableHasher, HashStable,
14+
StableHasherResult};
1215
use std::cmp;
16+
use std::mem;
1317
use ty;
18+
use ty::subst::Substs;
1419

1520
/// The SymbolExportLevel of a symbols specifies from which kinds of crates
1621
/// the symbol will be exported. `C` symbols will be exported from any
@@ -40,56 +45,89 @@ impl SymbolExportLevel {
4045
}
4146

4247
#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
43-
pub enum ExportedSymbol {
48+
pub enum ExportedSymbol<'tcx> {
4449
NonGeneric(DefId),
50+
Generic(DefId, &'tcx Substs<'tcx>),
4551
NoDefId(ty::SymbolName),
4652
}
4753

48-
impl ExportedSymbol {
49-
pub fn symbol_name(&self, tcx: ty::TyCtxt) -> ty::SymbolName {
54+
impl<'tcx> ExportedSymbol<'tcx> {
55+
pub fn symbol_name(&self,
56+
tcx: ty::TyCtxt<'_, 'tcx, '_>)
57+
-> ty::SymbolName {
5058
match *self {
5159
ExportedSymbol::NonGeneric(def_id) => {
5260
tcx.symbol_name(ty::Instance::mono(tcx, def_id))
5361
}
62+
ExportedSymbol::Generic(def_id, substs) => {
63+
tcx.symbol_name(ty::Instance::new(def_id, substs))
64+
}
5465
ExportedSymbol::NoDefId(symbol_name) => {
5566
symbol_name
5667
}
5768
}
5869
}
5970

60-
pub fn compare_stable(&self, tcx: ty::TyCtxt, other: &ExportedSymbol) -> cmp::Ordering {
71+
pub fn compare_stable(&self,
72+
tcx: ty::TyCtxt<'_, 'tcx, '_>,
73+
other: &ExportedSymbol<'tcx>)
74+
-> cmp::Ordering {
6175
match *self {
62-
ExportedSymbol::NonGeneric(self_def_id) => {
63-
match *other {
64-
ExportedSymbol::NonGeneric(other_def_id) => {
65-
tcx.def_path_hash(self_def_id).cmp(&tcx.def_path_hash(other_def_id))
66-
}
67-
ExportedSymbol::NoDefId(_) => {
68-
cmp::Ordering::Less
69-
}
76+
ExportedSymbol::NonGeneric(self_def_id) => match *other {
77+
ExportedSymbol::NonGeneric(other_def_id) => {
78+
tcx.def_path_hash(self_def_id).cmp(&tcx.def_path_hash(other_def_id))
79+
}
80+
ExportedSymbol::Generic(..) |
81+
ExportedSymbol::NoDefId(_) => {
82+
cmp::Ordering::Less
83+
}
84+
}
85+
ExportedSymbol::Generic(..) => match *other {
86+
ExportedSymbol::NonGeneric(_) => {
87+
cmp::Ordering::Greater
88+
}
89+
ExportedSymbol::Generic(..) => {
90+
self.symbol_name(tcx).cmp(&other.symbol_name(tcx))
91+
}
92+
ExportedSymbol::NoDefId(_) => {
93+
cmp::Ordering::Less
7094
}
7195
}
72-
ExportedSymbol::NoDefId(self_symbol_name) => {
73-
match *other {
74-
ExportedSymbol::NonGeneric(_) => {
75-
cmp::Ordering::Greater
76-
}
77-
ExportedSymbol::NoDefId(ref other_symbol_name) => {
78-
self_symbol_name.cmp(other_symbol_name)
79-
}
96+
ExportedSymbol::NoDefId(self_symbol_name) => match *other {
97+
ExportedSymbol::NonGeneric(_) |
98+
ExportedSymbol::Generic(..) => {
99+
cmp::Ordering::Greater
100+
}
101+
ExportedSymbol::NoDefId(ref other_symbol_name) => {
102+
self_symbol_name.cmp(other_symbol_name)
80103
}
81104
}
82105
}
83106
}
84107
}
85108

86-
impl_stable_hash_for!(enum self::ExportedSymbol {
87-
NonGeneric(def_id),
88-
NoDefId(symbol_name)
89-
});
90-
91109
pub fn metadata_symbol_name(tcx: ty::TyCtxt) -> String {
92110
format!("rust_metadata_{}_{}",
93111
tcx.original_crate_name(LOCAL_CRATE),
94112
tcx.crate_disambiguator(LOCAL_CRATE).to_fingerprint().to_hex())
95113
}
114+
115+
impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ExportedSymbol<'gcx> {
116+
fn hash_stable<W: StableHasherResult>(&self,
117+
hcx: &mut StableHashingContext<'a>,
118+
hasher: &mut StableHasher<W>) {
119+
mem::discriminant(self).hash_stable(hcx, hasher);
120+
match *self {
121+
ExportedSymbol::NonGeneric(def_id) => {
122+
def_id.hash_stable(hcx, hasher);
123+
}
124+
ExportedSymbol::Generic(def_id, substs) => {
125+
def_id.hash_stable(hcx, hasher);
126+
substs.hash_stable(hcx, hasher);
127+
}
128+
ExportedSymbol::NoDefId(symbol_name) => {
129+
symbol_name.hash_stable(hcx, hasher);
130+
}
131+
}
132+
}
133+
}

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
12901290
"format compiler diagnostics in a way that's better suitable for UI testing"),
12911291
embed_bitcode: bool = (false, parse_bool, [TRACKED],
12921292
"embed LLVM bitcode in object files"),
1293+
share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
1294+
"make the current crate share its generic instantiations"),
12931295
}
12941296

12951297
pub fn default_lib_output() -> CrateType {

src/librustc/ty/context.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use dep_graph::DepGraph;
1414
use dep_graph::{DepNode, DepConstructor};
1515
use errors::DiagnosticBuilder;
1616
use session::Session;
17-
use session::config::{BorrowckMode, OutputFilenames};
17+
use session::config::{BorrowckMode, OutputFilenames, OptLevel};
18+
use session::config::CrateType::*;
1819
use middle;
1920
use hir::{TraitCandidate, HirId, ItemLocalId};
2021
use hir::def::{Def, Export};
@@ -1473,6 +1474,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14731474
self.sess.opts.debugging_opts.mir_emit_validate > 0 ||
14741475
self.use_mir()
14751476
}
1477+
1478+
#[inline]
1479+
pub fn share_generics(self) -> bool {
1480+
match self.sess.opts.debugging_opts.share_generics {
1481+
Some(setting) => setting,
1482+
None => {
1483+
self.sess.opts.incremental.is_some() ||
1484+
match self.sess.opts.optimize {
1485+
OptLevel::No |
1486+
OptLevel::Less |
1487+
OptLevel::Size |
1488+
OptLevel::SizeMin => true,
1489+
OptLevel::Default |
1490+
OptLevel::Aggressive => false,
1491+
}
1492+
}
1493+
}
1494+
}
1495+
1496+
#[inline]
1497+
pub fn local_crate_exports_generics(self) -> bool {
1498+
debug_assert!(self.share_generics());
1499+
1500+
self.sess.crate_types.borrow().iter().any(|crate_type| {
1501+
match crate_type {
1502+
CrateTypeExecutable |
1503+
CrateTypeStaticlib |
1504+
CrateTypeProcMacro |
1505+
CrateTypeCdylib => false,
1506+
CrateTypeRlib |
1507+
CrateTypeDylib => true,
1508+
}
1509+
})
1510+
}
14761511
}
14771512

14781513
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {

src/librustc/ty/maps/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::coherent_trait<'tcx> {
131131
}
132132
}
133133

134+
impl<'tcx> QueryDescription<'tcx> for queries::upstream_monomorphizations<'tcx> {
135+
fn describe(_: TyCtxt, k: CrateNum) -> String {
136+
format!("collecting available upstream monomorphizations `{:?}`", k)
137+
}
138+
}
139+
134140
impl<'tcx> QueryDescription<'tcx> for queries::crate_inherent_impls<'tcx> {
135141
fn describe(_: TyCtxt, k: CrateNum) -> String {
136142
format!("all inherent impls defined in crate `{:?}`", k)

src/librustc/ty/maps/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,15 @@ define_maps! { <'tcx>
311311
//
312312
// Does not include external symbols that don't have a corresponding DefId,
313313
// like the compiler-generated `main` function and so on.
314-
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum) -> Lrc<DefIdSet>,
314+
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum)
315+
-> Lrc<DefIdMap<SymbolExportLevel>>,
315316
[] fn is_reachable_non_generic: IsReachableNonGeneric(DefId) -> bool,
317+
[] fn is_unreachable_local_definition: IsUnreachableLocalDefinition(DefId) -> bool,
316318

319+
[] fn upstream_monomorphizations: UpstreamMonomorphizations(CrateNum)
320+
-> Lrc<DefIdMap<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>>,
321+
[] fn upstream_monomorphizations_for: UpstreamMonomorphizationsFor(DefId)
322+
-> Option<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>,
317323

318324
[] fn native_libraries: NativeLibraries(CrateNum) -> Lrc<Vec<NativeLibrary>>,
319325
[] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option<DefId>,
@@ -367,11 +373,10 @@ define_maps! { <'tcx>
367373
[] fn all_crate_nums: all_crate_nums_node(CrateNum) -> Lrc<Vec<CrateNum>>,
368374

369375
[] fn exported_symbols: ExportedSymbols(CrateNum)
370-
-> Arc<Vec<(ExportedSymbol, SymbolExportLevel)>>,
376+
-> Arc<Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)>>,
371377
[] fn collect_and_partition_translation_items:
372378
collect_and_partition_translation_items_node(CrateNum)
373379
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
374-
[] fn symbol_export_level: GetSymbolExportLevel(DefId) -> SymbolExportLevel,
375380
[] fn is_translated_item: IsTranslatedItem(DefId) -> bool,
376381
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
377382
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,

src/librustc/ty/maps/plumbing.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
854854
DepKind::ImplParent => { force!(impl_parent, def_id!()); }
855855
DepKind::TraitOfItem => { force!(trait_of_item, def_id!()); }
856856
DepKind::IsReachableNonGeneric => { force!(is_reachable_non_generic, def_id!()); }
857+
DepKind::IsUnreachableLocalDefinition => {
858+
force!(is_unreachable_local_definition, def_id!());
859+
}
857860
DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); }
858861
DepKind::ItemAttrs => { force!(item_attrs, def_id!()); }
859862
DepKind::TransFnAttrs => { force!(trans_fn_attrs, def_id!()); }
@@ -933,10 +936,16 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
933936

934937
DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); }
935938

936-
DepKind::GetSymbolExportLevel => { force!(symbol_export_level, def_id!()); }
937939
DepKind::Features => { force!(features_query, LOCAL_CRATE); }
938940

939941
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
942+
943+
DepKind::UpstreamMonomorphizations => {
944+
force!(upstream_monomorphizations, krate!());
945+
}
946+
DepKind::UpstreamMonomorphizationsFor => {
947+
force!(upstream_monomorphizations_for, def_id!());
948+
}
940949
}
941950

942951
true

src/librustc_metadata/cstore_impl.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
185185
let reachable_non_generics = tcx
186186
.exported_symbols(cdata.cnum)
187187
.iter()
188-
.filter_map(|&(exported_symbol, _)| {
188+
.filter_map(|&(exported_symbol, export_level)| {
189189
if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
190-
return Some(def_id)
190+
return Some((def_id, export_level))
191191
} else {
192192
None
193193
}
@@ -269,7 +269,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
269269
return Arc::new(Vec::new())
270270
}
271271

272-
Arc::new(cdata.exported_symbols())
272+
Arc::new(cdata.exported_symbols(tcx))
273273
}
274274
}
275275

src/librustc_metadata/decoder.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1056,11 +1056,13 @@ impl<'a, 'tcx> CrateMetadata {
10561056
arg_names.decode(self).collect()
10571057
}
10581058

1059-
pub fn exported_symbols(&self) -> Vec<(ExportedSymbol, SymbolExportLevel)> {
1060-
self.root
1061-
.exported_symbols
1062-
.decode(self)
1063-
.collect()
1059+
pub fn exported_symbols(&self,
1060+
tcx: TyCtxt<'a, 'tcx, 'tcx>)
1061+
-> Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)> {
1062+
let lazy_seq: LazySeq<(ExportedSymbol<'tcx>, SymbolExportLevel)> =
1063+
LazySeq::with_position_and_length(self.root.exported_symbols.position,
1064+
self.root.exported_symbols.len);
1065+
lazy_seq.decode((self, tcx)).collect()
10641066
}
10651067

10661068
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {

src/librustc_metadata/encoder.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1425,13 +1425,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
14251425
// definition (as that's not defined in this crate).
14261426
fn encode_exported_symbols(&mut self,
14271427
exported_symbols: &[(ExportedSymbol, SymbolExportLevel)])
1428-
-> LazySeq<(ExportedSymbol, SymbolExportLevel)> {
1429-
1428+
-> EncodedExportedSymbols {
14301429
// The metadata symbol name is special. It should not show up in
14311430
// downstream crates.
14321431
let metadata_symbol_name = SymbolName::new(&metadata_symbol_name(self.tcx));
14331432

1434-
self.lazy_seq(exported_symbols
1433+
let lazy_seq = self.lazy_seq(exported_symbols
14351434
.iter()
14361435
.filter(|&&(ref exported_symbol, _)| {
14371436
match *exported_symbol {
@@ -1441,7 +1440,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
14411440
_ => true,
14421441
}
14431442
})
1444-
.cloned())
1443+
.cloned());
1444+
1445+
EncodedExportedSymbols {
1446+
len: lazy_seq.len,
1447+
position: lazy_seq.position,
1448+
}
14451449
}
14461450

14471451
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {

0 commit comments

Comments
 (0)