Skip to content

Commit 7678d50

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 2253216 + 61991a5 commit 7678d50

File tree

25 files changed

+675
-181
lines changed

25 files changed

+675
-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),
@@ -648,15 +649,16 @@ define_dep_nodes!( <'tcx>
648649

649650
[] InstanceDefSizeEstimate { instance_def: InstanceDef<'tcx> },
650651

651-
[] GetSymbolExportLevel(DefId),
652-
653652
[] WasmCustomSections(CrateNum),
654653

655654
[input] Features,
656655

657656
[] ProgramClausesFor(DefId),
658657
[] WasmImportModuleMap(CrateNum),
659658
[] ForeignModules(CrateNum),
659+
660+
[] UpstreamMonomorphizations(CrateNum),
661+
[] UpstreamMonomorphizationsFor(DefId),
660662
);
661663

662664
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
@@ -1304,6 +1304,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
13041304
"embed LLVM bitcode in object files"),
13051305
strip_debuginfo_if_disabled: Option<bool> = (None, parse_opt_bool, [TRACKED],
13061306
"tell the linker to strip debuginfo when building without debuginfo enabled."),
1307+
share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
1308+
"make the current crate share its generic instantiations"),
13071309
}
13081310

13091311
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};
@@ -1499,6 +1500,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14991500
self.sess.opts.debugging_opts.mir_emit_validate > 0 ||
15001501
self.use_mir()
15011502
}
1503+
1504+
#[inline]
1505+
pub fn share_generics(self) -> bool {
1506+
match self.sess.opts.debugging_opts.share_generics {
1507+
Some(setting) => setting,
1508+
None => {
1509+
self.sess.opts.incremental.is_some() ||
1510+
match self.sess.opts.optimize {
1511+
OptLevel::No |
1512+
OptLevel::Less |
1513+
OptLevel::Size |
1514+
OptLevel::SizeMin => true,
1515+
OptLevel::Default |
1516+
OptLevel::Aggressive => false,
1517+
}
1518+
}
1519+
}
1520+
}
1521+
1522+
#[inline]
1523+
pub fn local_crate_exports_generics(self) -> bool {
1524+
debug_assert!(self.share_generics());
1525+
1526+
self.sess.crate_types.borrow().iter().any(|crate_type| {
1527+
match crate_type {
1528+
CrateTypeExecutable |
1529+
CrateTypeStaticlib |
1530+
CrateTypeProcMacro |
1531+
CrateTypeCdylib => false,
1532+
CrateTypeRlib |
1533+
CrateTypeDylib => true,
1534+
}
1535+
})
1536+
}
15021537
}
15031538

15041539
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
@@ -319,9 +319,15 @@ define_maps! { <'tcx>
319319
//
320320
// Does not include external symbols that don't have a corresponding DefId,
321321
// like the compiler-generated `main` function and so on.
322-
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum) -> Lrc<DefIdSet>,
322+
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum)
323+
-> Lrc<DefIdMap<SymbolExportLevel>>,
323324
[] fn is_reachable_non_generic: IsReachableNonGeneric(DefId) -> bool,
325+
[] fn is_unreachable_local_definition: IsUnreachableLocalDefinition(DefId) -> bool,
324326

327+
[] fn upstream_monomorphizations: UpstreamMonomorphizations(CrateNum)
328+
-> Lrc<DefIdMap<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>>,
329+
[] fn upstream_monomorphizations_for: UpstreamMonomorphizationsFor(DefId)
330+
-> Option<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>,
325331

326332
[] fn native_libraries: NativeLibraries(CrateNum) -> Lrc<Vec<NativeLibrary>>,
327333

@@ -381,11 +387,10 @@ define_maps! { <'tcx>
381387
[] fn all_crate_nums: all_crate_nums_node(CrateNum) -> Lrc<Vec<CrateNum>>,
382388

383389
[] fn exported_symbols: ExportedSymbols(CrateNum)
384-
-> Arc<Vec<(ExportedSymbol, SymbolExportLevel)>>,
390+
-> Arc<Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)>>,
385391
[] fn collect_and_partition_translation_items:
386392
collect_and_partition_translation_items_node(CrateNum)
387393
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
388-
[] fn symbol_export_level: GetSymbolExportLevel(DefId) -> SymbolExportLevel,
389394
[] fn is_translated_item: IsTranslatedItem(DefId) -> bool,
390395
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
391396
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,

src/librustc/ty/maps/plumbing.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
10031003
DepKind::ImplParent => { force!(impl_parent, def_id!()); }
10041004
DepKind::TraitOfItem => { force!(trait_of_item, def_id!()); }
10051005
DepKind::IsReachableNonGeneric => { force!(is_reachable_non_generic, def_id!()); }
1006+
DepKind::IsUnreachableLocalDefinition => {
1007+
force!(is_unreachable_local_definition, def_id!());
1008+
}
10061009
DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); }
10071010
DepKind::ItemAttrs => { force!(item_attrs, def_id!()); }
10081011
DepKind::TransFnAttrs => { force!(trans_fn_attrs, def_id!()); }
@@ -1087,13 +1090,19 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
10871090

10881091
DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); }
10891092

1090-
DepKind::GetSymbolExportLevel => { force!(symbol_export_level, def_id!()); }
10911093
DepKind::Features => { force!(features_query, LOCAL_CRATE); }
10921094

10931095
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
10941096
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
10951097
DepKind::WasmImportModuleMap => { force!(wasm_import_module_map, krate!()); }
10961098
DepKind::ForeignModules => { force!(foreign_modules, krate!()); }
1099+
1100+
DepKind::UpstreamMonomorphizations => {
1101+
force!(upstream_monomorphizations, krate!());
1102+
}
1103+
DepKind::UpstreamMonomorphizationsFor => {
1104+
force!(upstream_monomorphizations_for, def_id!());
1105+
}
10971106
}
10981107

10991108
true

src/librustc_metadata/cstore_impl.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
186186
let reachable_non_generics = tcx
187187
.exported_symbols(cdata.cnum)
188188
.iter()
189-
.filter_map(|&(exported_symbol, _)| {
189+
.filter_map(|&(exported_symbol, export_level)| {
190190
if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
191-
return Some(def_id)
191+
return Some((def_id, export_level))
192192
} else {
193193
None
194194
}
@@ -268,7 +268,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
268268
return Arc::new(Vec::new())
269269
}
270270

271-
Arc::new(cdata.exported_symbols())
271+
Arc::new(cdata.exported_symbols(tcx))
272272
}
273273

274274
wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }

src/librustc_metadata/decoder.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1065,11 +1065,13 @@ impl<'a, 'tcx> CrateMetadata {
10651065
arg_names.decode(self).collect()
10661066
}
10671067

1068-
pub fn exported_symbols(&self) -> Vec<(ExportedSymbol, SymbolExportLevel)> {
1069-
self.root
1070-
.exported_symbols
1071-
.decode(self)
1072-
.collect()
1068+
pub fn exported_symbols(&self,
1069+
tcx: TyCtxt<'a, 'tcx, 'tcx>)
1070+
-> Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)> {
1071+
let lazy_seq: LazySeq<(ExportedSymbol<'tcx>, SymbolExportLevel)> =
1072+
LazySeq::with_position_and_length(self.root.exported_symbols.position,
1073+
self.root.exported_symbols.len);
1074+
lazy_seq.decode((self, tcx)).collect()
10731075
}
10741076

10751077
pub fn wasm_custom_sections(&self) -> Vec<DefId> {

src/librustc_metadata/encoder.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1444,13 +1444,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
14441444
// definition (as that's not defined in this crate).
14451445
fn encode_exported_symbols(&mut self,
14461446
exported_symbols: &[(ExportedSymbol, SymbolExportLevel)])
1447-
-> LazySeq<(ExportedSymbol, SymbolExportLevel)> {
1448-
1447+
-> EncodedExportedSymbols {
14491448
// The metadata symbol name is special. It should not show up in
14501449
// downstream crates.
14511450
let metadata_symbol_name = SymbolName::new(&metadata_symbol_name(self.tcx));
14521451

1453-
self.lazy_seq(exported_symbols
1452+
let lazy_seq = self.lazy_seq(exported_symbols
14541453
.iter()
14551454
.filter(|&&(ref exported_symbol, _)| {
14561455
match *exported_symbol {
@@ -1460,7 +1459,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
14601459
_ => true,
14611460
}
14621461
})
1463-
.cloned())
1462+
.cloned());
1463+
1464+
EncodedExportedSymbols {
1465+
len: lazy_seq.len,
1466+
position: lazy_seq.position,
1467+
}
14641468
}
14651469

14661470
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {

0 commit comments

Comments
 (0)