Skip to content

Commit

Permalink
Auto merge of #48779 - michaelwoerister:share-generics4, r=alexcrichton
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Apr 6, 2018
2 parents 2253216 + 61991a5 commit 7678d50
Show file tree
Hide file tree
Showing 25 changed files with 675 additions and 181 deletions.
6 changes: 4 additions & 2 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ define_dep_nodes!( <'tcx>
[] ImplParent(DefId),
[] TraitOfItem(DefId),
[] IsReachableNonGeneric(DefId),
[] IsUnreachableLocalDefinition(DefId),
[] IsMirAvailable(DefId),
[] ItemAttrs(DefId),
[] TransFnAttrs(DefId),
Expand Down Expand Up @@ -648,15 +649,16 @@ define_dep_nodes!( <'tcx>
[] InstanceDefSizeEstimate { instance_def: InstanceDef<'tcx> },

[] GetSymbolExportLevel(DefId),

[] WasmCustomSections(CrateNum),

[input] Features,

[] ProgramClausesFor(DefId),
[] WasmImportModuleMap(CrateNum),
[] ForeignModules(CrateNum),

[] UpstreamMonomorphizations(CrateNum),
[] UpstreamMonomorphizationsFor(DefId),
);

trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
Expand Down
18 changes: 16 additions & 2 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,21 @@ for &'gcx ty::Slice<T>
}
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::subst::Kind<'gcx> {
impl<'a, 'gcx, T> ToStableHashKey<StableHashingContext<'a>> for &'gcx ty::Slice<T>
where T: HashStable<StableHashingContext<'a>>
{
type KeyType = Fingerprint;

#[inline]
fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
let mut hasher = StableHasher::new();
let mut hcx: StableHashingContext<'a> = hcx.clone();
self.hash_stable(&mut hcx, &mut hasher);
hasher.finish()
}
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
Expand All @@ -67,6 +80,7 @@ for ty::subst::UnpackedKind<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher),
ty::subst::UnpackedKind::Type(ty) => ty.hash_stable(hcx, hasher),
Expand Down
88 changes: 63 additions & 25 deletions src/librustc/middle/exported_symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
// except according to those terms.

use hir::def_id::{DefId, LOCAL_CRATE};
use ich::StableHashingContext;
use rustc_data_structures::stable_hasher::{StableHasher, HashStable,
StableHasherResult};
use std::cmp;
use std::mem;
use ty;
use ty::subst::Substs;

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

#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
pub enum ExportedSymbol {
pub enum ExportedSymbol<'tcx> {
NonGeneric(DefId),
Generic(DefId, &'tcx Substs<'tcx>),
NoDefId(ty::SymbolName),
}

impl ExportedSymbol {
pub fn symbol_name(&self, tcx: ty::TyCtxt) -> ty::SymbolName {
impl<'tcx> ExportedSymbol<'tcx> {
pub fn symbol_name(&self,
tcx: ty::TyCtxt<'_, 'tcx, '_>)
-> ty::SymbolName {
match *self {
ExportedSymbol::NonGeneric(def_id) => {
tcx.symbol_name(ty::Instance::mono(tcx, def_id))
}
ExportedSymbol::Generic(def_id, substs) => {
tcx.symbol_name(ty::Instance::new(def_id, substs))
}
ExportedSymbol::NoDefId(symbol_name) => {
symbol_name
}
}
}

pub fn compare_stable(&self, tcx: ty::TyCtxt, other: &ExportedSymbol) -> cmp::Ordering {
pub fn compare_stable(&self,
tcx: ty::TyCtxt<'_, 'tcx, '_>,
other: &ExportedSymbol<'tcx>)
-> cmp::Ordering {
match *self {
ExportedSymbol::NonGeneric(self_def_id) => {
match *other {
ExportedSymbol::NonGeneric(other_def_id) => {
tcx.def_path_hash(self_def_id).cmp(&tcx.def_path_hash(other_def_id))
}
ExportedSymbol::NoDefId(_) => {
cmp::Ordering::Less
}
ExportedSymbol::NonGeneric(self_def_id) => match *other {
ExportedSymbol::NonGeneric(other_def_id) => {
tcx.def_path_hash(self_def_id).cmp(&tcx.def_path_hash(other_def_id))
}
ExportedSymbol::Generic(..) |
ExportedSymbol::NoDefId(_) => {
cmp::Ordering::Less
}
}
ExportedSymbol::Generic(..) => match *other {
ExportedSymbol::NonGeneric(_) => {
cmp::Ordering::Greater
}
ExportedSymbol::Generic(..) => {
self.symbol_name(tcx).cmp(&other.symbol_name(tcx))
}
ExportedSymbol::NoDefId(_) => {
cmp::Ordering::Less
}
}
ExportedSymbol::NoDefId(self_symbol_name) => {
match *other {
ExportedSymbol::NonGeneric(_) => {
cmp::Ordering::Greater
}
ExportedSymbol::NoDefId(ref other_symbol_name) => {
self_symbol_name.cmp(other_symbol_name)
}
ExportedSymbol::NoDefId(self_symbol_name) => match *other {
ExportedSymbol::NonGeneric(_) |
ExportedSymbol::Generic(..) => {
cmp::Ordering::Greater
}
ExportedSymbol::NoDefId(ref other_symbol_name) => {
self_symbol_name.cmp(other_symbol_name)
}
}
}
}
}

impl_stable_hash_for!(enum self::ExportedSymbol {
NonGeneric(def_id),
NoDefId(symbol_name)
});

pub fn metadata_symbol_name(tcx: ty::TyCtxt) -> String {
format!("rust_metadata_{}_{}",
tcx.original_crate_name(LOCAL_CRATE),
tcx.crate_disambiguator(LOCAL_CRATE).to_fingerprint().to_hex())
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ExportedSymbol<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
ExportedSymbol::NonGeneric(def_id) => {
def_id.hash_stable(hcx, hasher);
}
ExportedSymbol::Generic(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
ExportedSymbol::NoDefId(symbol_name) => {
symbol_name.hash_stable(hcx, hasher);
}
}
}
}
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"embed LLVM bitcode in object files"),
strip_debuginfo_if_disabled: Option<bool> = (None, parse_opt_bool, [TRACKED],
"tell the linker to strip debuginfo when building without debuginfo enabled."),
share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
"make the current crate share its generic instantiations"),
}

pub fn default_lib_output() -> CrateType {
Expand Down
37 changes: 36 additions & 1 deletion src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use dep_graph::DepGraph;
use dep_graph::{DepNode, DepConstructor};
use errors::DiagnosticBuilder;
use session::Session;
use session::config::{BorrowckMode, OutputFilenames};
use session::config::{BorrowckMode, OutputFilenames, OptLevel};
use session::config::CrateType::*;
use middle;
use hir::{TraitCandidate, HirId, ItemLocalId};
use hir::def::{Def, Export};
Expand Down Expand Up @@ -1499,6 +1500,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.sess.opts.debugging_opts.mir_emit_validate > 0 ||
self.use_mir()
}

#[inline]
pub fn share_generics(self) -> bool {
match self.sess.opts.debugging_opts.share_generics {
Some(setting) => setting,
None => {
self.sess.opts.incremental.is_some() ||
match self.sess.opts.optimize {
OptLevel::No |
OptLevel::Less |
OptLevel::Size |
OptLevel::SizeMin => true,
OptLevel::Default |
OptLevel::Aggressive => false,
}
}
}
}

#[inline]
pub fn local_crate_exports_generics(self) -> bool {
debug_assert!(self.share_generics());

self.sess.crate_types.borrow().iter().any(|crate_type| {
match crate_type {
CrateTypeExecutable |
CrateTypeStaticlib |
CrateTypeProcMacro |
CrateTypeCdylib => false,
CrateTypeRlib |
CrateTypeDylib => true,
}
})
}
}

impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/maps/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::coherent_trait<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::upstream_monomorphizations<'tcx> {
fn describe(_: TyCtxt, k: CrateNum) -> String {
format!("collecting available upstream monomorphizations `{:?}`", k)
}
}

impl<'tcx> QueryDescription<'tcx> for queries::crate_inherent_impls<'tcx> {
fn describe(_: TyCtxt, k: CrateNum) -> String {
format!("all inherent impls defined in crate `{:?}`", k)
Expand Down
11 changes: 8 additions & 3 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,15 @@ define_maps! { <'tcx>
//
// Does not include external symbols that don't have a corresponding DefId,
// like the compiler-generated `main` function and so on.
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum) -> Lrc<DefIdSet>,
[] fn reachable_non_generics: ReachableNonGenerics(CrateNum)
-> Lrc<DefIdMap<SymbolExportLevel>>,
[] fn is_reachable_non_generic: IsReachableNonGeneric(DefId) -> bool,
[] fn is_unreachable_local_definition: IsUnreachableLocalDefinition(DefId) -> bool,

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

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

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

[] fn exported_symbols: ExportedSymbols(CrateNum)
-> Arc<Vec<(ExportedSymbol, SymbolExportLevel)>>,
-> Arc<Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)>>,
[] fn collect_and_partition_translation_items:
collect_and_partition_translation_items_node(CrateNum)
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
[] fn symbol_export_level: GetSymbolExportLevel(DefId) -> SymbolExportLevel,
[] fn is_translated_item: IsTranslatedItem(DefId) -> bool,
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,
Expand Down
11 changes: 10 additions & 1 deletion src/librustc/ty/maps/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::ImplParent => { force!(impl_parent, def_id!()); }
DepKind::TraitOfItem => { force!(trait_of_item, def_id!()); }
DepKind::IsReachableNonGeneric => { force!(is_reachable_non_generic, def_id!()); }
DepKind::IsUnreachableLocalDefinition => {
force!(is_unreachable_local_definition, def_id!());
}
DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); }
DepKind::ItemAttrs => { force!(item_attrs, def_id!()); }
DepKind::TransFnAttrs => { force!(trans_fn_attrs, def_id!()); }
Expand Down Expand Up @@ -1087,13 +1090,19 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,

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

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

DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
DepKind::WasmImportModuleMap => { force!(wasm_import_module_map, krate!()); }
DepKind::ForeignModules => { force!(foreign_modules, krate!()); }

DepKind::UpstreamMonomorphizations => {
force!(upstream_monomorphizations, krate!());
}
DepKind::UpstreamMonomorphizationsFor => {
force!(upstream_monomorphizations_for, def_id!());
}
}

true
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
let reachable_non_generics = tcx
.exported_symbols(cdata.cnum)
.iter()
.filter_map(|&(exported_symbol, _)| {
.filter_map(|&(exported_symbol, export_level)| {
if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
return Some(def_id)
return Some((def_id, export_level))
} else {
None
}
Expand Down Expand Up @@ -268,7 +268,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
return Arc::new(Vec::new())
}

Arc::new(cdata.exported_symbols())
Arc::new(cdata.exported_symbols(tcx))
}

wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }
Expand Down
12 changes: 7 additions & 5 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,11 +1065,13 @@ impl<'a, 'tcx> CrateMetadata {
arg_names.decode(self).collect()
}

pub fn exported_symbols(&self) -> Vec<(ExportedSymbol, SymbolExportLevel)> {
self.root
.exported_symbols
.decode(self)
.collect()
pub fn exported_symbols(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)> {
let lazy_seq: LazySeq<(ExportedSymbol<'tcx>, SymbolExportLevel)> =
LazySeq::with_position_and_length(self.root.exported_symbols.position,
self.root.exported_symbols.len);
lazy_seq.decode((self, tcx)).collect()
}

pub fn wasm_custom_sections(&self) -> Vec<DefId> {
Expand Down
12 changes: 8 additions & 4 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,13 +1444,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
// definition (as that's not defined in this crate).
fn encode_exported_symbols(&mut self,
exported_symbols: &[(ExportedSymbol, SymbolExportLevel)])
-> LazySeq<(ExportedSymbol, SymbolExportLevel)> {

-> EncodedExportedSymbols {
// The metadata symbol name is special. It should not show up in
// downstream crates.
let metadata_symbol_name = SymbolName::new(&metadata_symbol_name(self.tcx));

self.lazy_seq(exported_symbols
let lazy_seq = self.lazy_seq(exported_symbols
.iter()
.filter(|&&(ref exported_symbol, _)| {
match *exported_symbol {
Expand All @@ -1460,7 +1459,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
_ => true,
}
})
.cloned())
.cloned());

EncodedExportedSymbols {
len: lazy_seq.len,
position: lazy_seq.position,
}
}

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

0 comments on commit 7678d50

Please sign in to comment.