]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #48779 - michaelwoerister:share-generics4, r=alexcrichton
authorbors <bors@rust-lang.org>
Fri, 6 Apr 2018 15:01:27 +0000 (15:01 +0000)
committerbors <bors@rust-lang.org>
Fri, 6 Apr 2018 15:01:27 +0000 (15:01 +0000)
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.

25 files changed:
src/librustc/dep_graph/dep_node.rs
src/librustc/ich/impls_ty.rs
src/librustc/middle/exported_symbols.rs
src/librustc/session/config.rs
src/librustc/ty/context.rs
src/librustc/ty/maps/config.rs
src/librustc/ty/maps/mod.rs
src/librustc/ty/maps/plumbing.rs
src/librustc_metadata/cstore_impl.rs
src/librustc_metadata/decoder.rs
src/librustc_metadata/encoder.rs
src/librustc_metadata/schema.rs
src/librustc_mir/monomorphize/collector.rs
src/librustc_mir/monomorphize/partitioning.rs
src/librustc_trans/back/symbol_export.rs
src/librustc_trans/callee.rs
src/librustc_trans/lib.rs
src/librustc_trans_utils/symbol_names.rs
src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs [new file with mode: 0644]
src/test/codegen-units/partitioning/extern-generic.rs
src/test/codegen-units/partitioning/shared-generics.rs [new file with mode: 0644]
src/test/codegen/local-generics-in-exe-internalized.rs [new file with mode: 0644]
src/test/run-make-fulldeps/symbol-visibility/Makefile
src/test/run-make-fulldeps/symbol-visibility/a_rust_dylib.rs
src/test/run-make-fulldeps/symbol-visibility/an_rlib.rs

index d1f3736556c5dc28eaff476814ae693bc0dc412e..7c5318a96f5ac5773422972984e7afd37179579b 100644 (file)
@@ -561,6 +561,7 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
     [] ImplParent(DefId),
     [] TraitOfItem(DefId),
     [] IsReachableNonGeneric(DefId),
+    [] IsUnreachableLocalDefinition(DefId),
     [] IsMirAvailable(DefId),
     [] ItemAttrs(DefId),
     [] TransFnAttrs(DefId),
@@ -648,8 +649,6 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
 
     [] InstanceDefSizeEstimate { instance_def: InstanceDef<'tcx> },
 
-    [] GetSymbolExportLevel(DefId),
-
     [] WasmCustomSections(CrateNum),
 
     [input] Features,
@@ -657,6 +656,9 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
     [] ProgramClausesFor(DefId),
     [] WasmImportModuleMap(CrateNum),
     [] ForeignModules(CrateNum),
+
+    [] UpstreamMonomorphizations(CrateNum),
+    [] UpstreamMonomorphizationsFor(DefId),
 );
 
 trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
index da1a2592f14db64a88b9c5b7f7ac44e64222a86a..f86913490258e51c11d72f1fba3fe1b08836fd68 100644 (file)
@@ -53,8 +53,21 @@ fn hash_stable<W: StableHasherResult>(&self,
     }
 }
 
-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>) {
@@ -67,6 +80,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
     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),
index b1418792490fcc3d40c832f62aec98ca9c050080..01783eb0ff65f8b90cf561c59d790c5994822d64 100644 (file)
@@ -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
@@ -40,56 +45,89 @@ pub fn is_below_threshold(self, threshold: SymbolExportLevel) -> bool {
 }
 
 #[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);
+            }
+        }
+    }
+}
index 8cb87e7e080b8c005fc3f3fdb02854a87081a442..a07370e1e42a7649f53787ab85c38bc2844cfb0c 100644 (file)
@@ -1304,6 +1304,8 @@ fn parse_edition(slot: &mut Edition, v: Option<&str>) -> bool {
           "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 {
index 3326af21bd6049e2d9577b9645cfa6aafd46106c..69b33efdb3542c02bace3f32fb00ac3560ef3498 100644 (file)
@@ -14,7 +14,8 @@
 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};
@@ -1499,6 +1500,40 @@ pub fn emit_end_regions(self) -> bool {
             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> {
index a08cd57b1f7e24639d4e1128419f4240f6f9428e..16866636cd90aacae94f00970462b272dabc0409 100644 (file)
@@ -131,6 +131,12 @@ fn describe(tcx: TyCtxt, def_id: DefId) -> String {
     }
 }
 
+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)
index a992b8acb8b214e3eb426f8fddc6ecca1822c2a4..5a23a3b952a423c392f4aa8ce274d965b966285a 100644 (file)
     //
     // 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>>,
 
     [] 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,
index bc5a14c96f004a4c66cc671aa79aed8a71ea7df2..65571aa6a692db4d10a0b8f63102a62f2ae20b62 100644 (file)
@@ -1003,6 +1003,9 @@ macro_rules! force {
         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!()); }
@@ -1087,13 +1090,19 @@ macro_rules! force {
 
         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
index f63edf07fa8ba5b6615079f4c1739e7e18864378..51088563c7b9c0d0951ef8260577f0ece00a84c9 100644 (file)
@@ -186,9 +186,9 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) }
         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
                 }
@@ -268,7 +268,7 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) }
             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()) }
index e938d5c1a97fe4e9d1ff6e0ad4c44e9b448ddd0a..42e208ded49fd0f1f0782836dc3d219a8e5d700c 100644 (file)
@@ -1065,11 +1065,13 @@ pub fn get_fn_arg_names(&self, id: DefIndex) -> Vec<ast::Name> {
         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> {
index 0da6fc5b9eda19267d012e54052a123a9f0b7f57..1b208a512e2a46c6a977b5c272aef67f8a7e95a7 100644 (file)
@@ -1444,13 +1444,12 @@ fn encode_impls(&mut self, _: ()) -> LazySeq<TraitImpls> {
     // 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 {
@@ -1460,7 +1459,12 @@ fn encode_exported_symbols(&mut self,
                     _ => true,
                 }
             })
-            .cloned())
+            .cloned());
+
+        EncodedExportedSymbols {
+            len: lazy_seq.len,
+            position: lazy_seq.position,
+        }
     }
 
     fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
index a7ee0e7e9a961c7eb5b2eb461532d88dcbbf5588..e3986bb7d91f94c5bdbde85cef7b828b75cf6d58 100644 (file)
@@ -15,7 +15,6 @@
 use rustc::hir::def::{self, CtorKind};
 use rustc::hir::def_id::{DefIndex, DefId, CrateNum};
 use rustc::ich::StableHashingContext;
-use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
 use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary, ForeignModule};
 use rustc::middle::lang_items;
 use rustc::mir;
@@ -206,7 +205,7 @@ pub struct CrateRoot {
     pub codemap: LazySeq<syntax_pos::FileMap>,
     pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
     pub impls: LazySeq<TraitImpls>,
-    pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>,
+    pub exported_symbols: EncodedExportedSymbols,
     pub wasm_custom_sections: LazySeq<DefIndex>,
 
     pub index: LazySeq<index::Index>,
@@ -531,3 +530,9 @@ pub struct GeneratorData<'tcx> {
 // Tags used for encoding Spans:
 pub const TAG_VALID_SPAN: u8 = 0;
 pub const TAG_INVALID_SPAN: u8 = 1;
+
+#[derive(RustcEncodable, RustcDecodable)]
+pub struct EncodedExportedSymbols {
+    pub position: usize,
+    pub len: usize,
+}
index 446ef6bd32876b63dce95e1b156cf90a05b5cb54..ef018237dbad9c21fe26c4a89dcc260dd015ee36 100644 (file)
@@ -569,7 +569,9 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
                     ty::TyClosure(def_id, substs) => {
                         let instance = monomorphize::resolve_closure(
                             self.tcx, def_id, substs, ty::ClosureKind::FnOnce);
-                        self.output.push(create_fn_mono_item(instance));
+                        if should_monomorphize_locally(self.tcx, &instance) {
+                            self.output.push(create_fn_mono_item(instance));
+                        }
                     }
                     _ => bug!(),
                 }
@@ -731,14 +733,16 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance:
         ty::InstanceDef::Intrinsic(_) |
         ty::InstanceDef::CloneShim(..) => return true
     };
-    match tcx.hir.get_if_local(def_id) {
+
+    return match tcx.hir.get_if_local(def_id) {
         Some(hir_map::NodeForeignItem(..)) => {
             false // foreign items are linked against, not translated.
         }
         Some(_) => true,
         None => {
             if tcx.is_reachable_non_generic(def_id) ||
-                tcx.is_foreign_item(def_id)
+                tcx.is_foreign_item(def_id) ||
+                is_available_upstream_generic(tcx, def_id, instance.substs)
             {
                 // We can link to the item in question, no instance needed
                 // in this crate
@@ -750,6 +754,33 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance:
                 true
             }
         }
+    };
+
+    fn is_available_upstream_generic<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                               def_id: DefId,
+                                               substs: &'tcx Substs<'tcx>)
+                                               -> bool {
+        debug_assert!(!def_id.is_local());
+
+        // If we are not in share generics mode, we don't link to upstream
+        // monomorphizations but always instantiate our own internal versions
+        // instead.
+        if !tcx.share_generics() {
+            return false
+        }
+
+        // If this instance has no type parameters, it cannot be a shared
+        // monomorphization. Non-generic instances are already handled above
+        // by `is_reachable_non_generic()`
+        if substs.types().next().is_none() {
+            return false
+        }
+
+        // Take a look at the available monomorphizations listed in the metadata
+        // of upstream crates.
+        tcx.upstream_monomorphizations_for(def_id)
+           .map(|set| set.contains_key(substs))
+           .unwrap_or(false)
     }
 }
 
index 3789342b3891dd9b7d0a91b9f12f0f7b61826fb7..da4cb4ec789041d219ce33d3c0084cba30b0a5b0 100644 (file)
@@ -302,6 +302,13 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let is_incremental_build = tcx.sess.opts.incremental.is_some();
     let mut internalization_candidates = FxHashSet();
 
+    // Determine if monomorphizations instantiated in this crate will be made
+    // available to downstream crates. This depends on whether we are in
+    // share-generics mode and whether the current crate can even have
+    // downstream crates.
+    let export_generics = tcx.share_generics() &&
+                          tcx.local_crate_exports_generics();
+
     for trans_item in trans_items {
         match trans_item.instantiation_mode(tcx) {
             InstantiationMode::GloballyShared { .. } => {}
@@ -325,13 +332,27 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                             .or_insert_with(make_codegen_unit);
 
         let mut can_be_internalized = true;
-        let default_visibility = |id: DefId| {
-            if tcx.sess.target.target.options.default_hidden_visibility &&
-                tcx.symbol_export_level(id) != SymbolExportLevel::C
-            {
-                Visibility::Hidden
-            } else {
+        let default_visibility = |id: DefId, is_generic: bool| {
+            if !tcx.sess.target.target.options.default_hidden_visibility {
+                return Visibility::Default
+            }
+
+            // Generic functions never have export level C
+            if is_generic {
+                return Visibility::Hidden
+            }
+
+            // Things with export level C don't get instantiated in downstream
+            // crates
+            if !id.is_local() {
+                return Visibility::Hidden
+            }
+
+            if let Some(&SymbolExportLevel::C) = tcx.reachable_non_generics(id.krate)
+                                                    .get(&id) {
                 Visibility::Default
+            } else {
+                Visibility::Hidden
             }
         };
         let (linkage, mut visibility) = match trans_item.explicit_linkage(tcx) {
@@ -341,6 +362,11 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     MonoItem::Fn(ref instance) => {
                         let visibility = match instance.def {
                             InstanceDef::Item(def_id) => {
+                                let is_generic = instance.substs
+                                                         .types()
+                                                         .next()
+                                                         .is_some();
+
                                 // The `start_fn` lang item is actually a
                                 // monomorphized instance of a function in the
                                 // standard library, used for the `main`
@@ -363,14 +389,46 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     can_be_internalized = false;
                                     Visibility::Hidden
                                 } else if def_id.is_local() {
-                                    if tcx.is_reachable_non_generic(def_id) {
+                                    if is_generic {
+                                        if export_generics {
+                                            if tcx.is_unreachable_local_definition(def_id) {
+                                                // This instance cannot be used
+                                                // from another crate.
+                                                Visibility::Hidden
+                                            } else {
+                                                // This instance might be useful in
+                                                // a downstream crate.
+                                                can_be_internalized = false;
+                                                default_visibility(def_id, true)
+                                            }
+                                        } else {
+                                            // We are not exporting generics or
+                                            // the definition is not reachable
+                                            // for downstream crates, we can
+                                            // internalize its instantiations.
+                                            Visibility::Hidden
+                                        }
+                                    } else {
+                                        // This isn't a generic function.
+                                        if tcx.is_reachable_non_generic(def_id) {
+                                            can_be_internalized = false;
+                                            debug_assert!(!is_generic);
+                                            default_visibility(def_id, false)
+                                        } else {
+                                            Visibility::Hidden
+                                        }
+                                    }
+                                } else {
+                                    // This is an upstream DefId.
+                                    if export_generics && is_generic {
+                                        // If it is a upstream monomorphization
+                                        // and we export generics, we must make
+                                        // it available to downstream crates.
                                         can_be_internalized = false;
-                                        default_visibility(def_id)
+                                        default_visibility(def_id, true)
                                     } else {
                                         Visibility::Hidden
                                     }
-                                } else {
-                                    Visibility::Hidden
                                 }
                             }
                             InstanceDef::FnPtrShim(..) |
@@ -387,7 +445,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     MonoItem::Static(def_id) => {
                         let visibility = if tcx.is_reachable_non_generic(def_id) {
                             can_be_internalized = false;
-                            default_visibility(def_id)
+                            default_visibility(def_id, false)
                         } else {
                             Visibility::Hidden
                         };
@@ -397,7 +455,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         let def_id = tcx.hir.local_def_id(node_id);
                         let visibility = if tcx.is_reachable_non_generic(def_id) {
                             can_be_internalized = false;
-                            default_visibility(def_id)
+                            default_visibility(def_id, false)
                         } else {
                             Visibility::Hidden
                         };
index d205e6ca4eda6ed5b78e5afd8cfeeab40af71b07..acd2a7657307c4db2d1a6d2d69c1a53336bcb06c 100644 (file)
 use monomorphize::Instance;
 use rustc::hir;
 use rustc::hir::TransFnAttrFlags;
-use rustc::hir::def_id::CrateNum;
-use rustc::hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
+use rustc::ich::Fingerprint;
 use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name};
 use rustc::session::config;
 use rustc::ty::{TyCtxt, SymbolName};
 use rustc::ty::maps::Providers;
-use rustc::util::nodemap::{FxHashMap, DefIdSet};
+use rustc::ty::subst::Substs;
+use rustc::util::nodemap::{FxHashMap, DefIdMap};
 use rustc_allocator::ALLOCATOR_METHODS;
+use rustc_data_structures::indexed_vec::IndexVec;
+use std::collections::hash_map::Entry::*;
 
 pub type ExportedSymbols = FxHashMap<
     CrateNum,
@@ -56,51 +59,12 @@ pub fn crates_export_threshold(crate_types: &[config::CrateType])
 
 fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                              cnum: CrateNum)
-                                             -> Lrc<DefIdSet>
+                                             -> Lrc<DefIdMap<SymbolExportLevel>>
 {
     assert_eq!(cnum, LOCAL_CRATE);
 
     if !tcx.sess.opts.output_types.should_trans() {
-        return Lrc::new(DefIdSet())
-    }
-
-    let export_threshold = threshold(tcx);
-
-    // We already collect all potentially reachable non-generic items for
-    // `exported_symbols`. Now we just filter them down to what is actually
-    // exported for the given crate we are compiling.
-    let reachable_non_generics = tcx
-        .exported_symbols(LOCAL_CRATE)
-        .iter()
-        .filter_map(|&(exported_symbol, level)| {
-            if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
-                if level.is_below_threshold(export_threshold) {
-                    return Some(def_id)
-                }
-            }
-
-            None
-        })
-        .collect();
-
-    Lrc::new(reachable_non_generics)
-}
-
-fn is_reachable_non_generic_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                               def_id: DefId)
-                                               -> bool {
-    tcx.reachable_non_generics(def_id.krate).contains(&def_id)
-}
-
-fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                             cnum: CrateNum)
-                                             -> Arc<Vec<(ExportedSymbol,
-                                                         SymbolExportLevel)>>
-{
-    assert_eq!(cnum, LOCAL_CRATE);
-
-    if !tcx.sess.opts.output_types.should_trans() {
-        return Arc::new(vec![])
+        return Lrc::new(DefIdMap())
     }
 
     // Check to see if this crate is a "special runtime crate". These
@@ -113,7 +77,7 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) ||
         tcx.is_compiler_builtins(LOCAL_CRATE);
 
-    let reachable_non_generics: DefIdSet = tcx.reachable_set(LOCAL_CRATE).0
+    let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0
         .iter()
         .filter_map(|&node_id| {
             // We want to ignore some FFI functions that are not exposed from
@@ -166,11 +130,7 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 _ => None
             }
         })
-        .collect();
-
-    let mut symbols: Vec<_> = reachable_non_generics
-        .iter()
-        .map(|&def_id| {
+        .map(|def_id| {
             let export_level = if special_runtime_crate {
                 let name = tcx.symbol_name(Instance::mono(tcx, def_id));
                 // We can probably do better here by just ensuring that
@@ -188,24 +148,63 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     SymbolExportLevel::Rust
                 }
             } else {
-                tcx.symbol_export_level(def_id)
+                symbol_export_level(tcx, def_id)
             };
             debug!("EXPORTED SYMBOL (local): {} ({:?})",
                    tcx.symbol_name(Instance::mono(tcx, def_id)),
                    export_level);
-            (ExportedSymbol::NonGeneric(def_id), export_level)
+            (def_id, export_level)
         })
         .collect();
 
     if let Some(id) = tcx.sess.derive_registrar_fn.get() {
         let def_id = tcx.hir.local_def_id(id);
-        symbols.push((ExportedSymbol::NonGeneric(def_id), SymbolExportLevel::C));
+        reachable_non_generics.insert(def_id, SymbolExportLevel::C);
     }
 
     if let Some(id) = tcx.sess.plugin_registrar_fn.get() {
         let def_id = tcx.hir.local_def_id(id);
-        symbols.push((ExportedSymbol::NonGeneric(def_id), SymbolExportLevel::C));
+        reachable_non_generics.insert(def_id, SymbolExportLevel::C);
+    }
+
+    Lrc::new(reachable_non_generics)
+}
+
+fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                                     def_id: DefId)
+                                                     -> bool {
+    let export_threshold = threshold(tcx);
+
+    if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
+        level.is_below_threshold(export_threshold)
+    } else {
+        false
     }
+}
+
+fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                                      def_id: DefId)
+                                                      -> bool {
+    tcx.reachable_non_generics(def_id.krate).contains_key(&def_id)
+}
+
+fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                             cnum: CrateNum)
+                                             -> Arc<Vec<(ExportedSymbol<'tcx>,
+                                                         SymbolExportLevel)>>
+{
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    if !tcx.sess.opts.output_types.should_trans() {
+        return Arc::new(vec![])
+    }
+
+    let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE)
+                                 .iter()
+                                 .map(|(&def_id, &level)| {
+                                    (ExportedSymbol::NonGeneric(def_id), level)
+                                 })
+                                 .collect();
 
     if let Some(_) = *tcx.sess.entry_fn.borrow() {
         let symbol_name = "main".to_string();
@@ -244,6 +243,46 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         symbols.push((exported_symbol, SymbolExportLevel::Rust));
     }
 
+    if tcx.share_generics() && tcx.local_crate_exports_generics() {
+        use rustc::mir::mono::{Linkage, Visibility, MonoItem};
+        use rustc::ty::InstanceDef;
+
+        // Normally, we require that shared monomorphizations are not hidden,
+        // because if we want to re-use a monomorphization from a Rust dylib, it
+        // needs to be exported.
+        // However, on platforms that don't allow for Rust dylibs, having
+        // external linkage is enough for monomorphization to be linked to.
+        let need_visibility = tcx.sess.target.target.options.dynamic_linking &&
+                              !tcx.sess.target.target.options.only_cdylib;
+
+        let (_, cgus) = tcx.collect_and_partition_translation_items(LOCAL_CRATE);
+
+        for (mono_item, &(linkage, visibility)) in cgus.iter()
+                                                       .flat_map(|cgu| cgu.items().iter()) {
+            if linkage != Linkage::External {
+                // We can only re-use things with external linkage, otherwise
+                // we'll get a linker error
+                continue
+            }
+
+            if need_visibility && visibility == Visibility::Hidden {
+                // If we potentially share things from Rust dylibs, they must
+                // not be hidden
+                continue
+            }
+
+            if let &MonoItem::Fn(Instance {
+                def: InstanceDef::Item(def_id),
+                substs,
+            }) = mono_item {
+                if substs.types().next().is_some() {
+                    symbols.push((ExportedSymbol::Generic(def_id, substs),
+                                  SymbolExportLevel::Rust));
+                }
+            }
+        }
+    }
+
     // Sort so we get a stable incr. comp. hash.
     symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| {
         symbol1.compare_stable(tcx, symbol2)
@@ -252,19 +291,93 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     Arc::new(symbols)
 }
 
+fn upstream_monomorphizations_provider<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    cnum: CrateNum)
+    -> Lrc<DefIdMap<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>>
+{
+    debug_assert!(cnum == LOCAL_CRATE);
+
+    let cnums = tcx.all_crate_nums(LOCAL_CRATE);
+
+    let mut instances = DefIdMap();
+
+    let cnum_stable_ids: IndexVec<CrateNum, Fingerprint> = {
+        let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO,
+                                                        cnums.len() + 1);
+
+        for &cnum in cnums.iter() {
+            cnum_stable_ids[cnum] = tcx.def_path_hash(DefId {
+                krate: cnum,
+                index: CRATE_DEF_INDEX,
+            }).0;
+        }
+
+        cnum_stable_ids
+    };
+
+    for &cnum in cnums.iter() {
+        for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
+            if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol {
+                let substs_map = instances.entry(def_id)
+                                          .or_insert_with(|| FxHashMap());
+
+                match substs_map.entry(substs) {
+                    Occupied(mut e) => {
+                        // If there are multiple monomorphizations available,
+                        // we select one deterministically.
+                        let other_cnum = *e.get();
+                        if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] {
+                            e.insert(cnum);
+                        }
+                    }
+                    Vacant(e) => {
+                        e.insert(cnum);
+                    }
+                }
+            }
+        }
+    }
+
+    Lrc::new(instances.into_iter()
+                      .map(|(key, value)| (key, Lrc::new(value)))
+                      .collect())
+}
+
+fn upstream_monomorphizations_for_provider<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    def_id: DefId)
+    -> Option<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>
+{
+    debug_assert!(!def_id.is_local());
+    tcx.upstream_monomorphizations(LOCAL_CRATE)
+       .get(&def_id)
+       .cloned()
+}
+
+fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool {
+    if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
+        !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id)
+    } else {
+        bug!("is_unreachable_local_definition called with non-local DefId: {:?}",
+              def_id)
+    }
+}
+
 pub fn provide(providers: &mut Providers) {
     providers.reachable_non_generics = reachable_non_generics_provider;
-    providers.is_reachable_non_generic = is_reachable_non_generic_provider;
+    providers.is_reachable_non_generic = is_reachable_non_generic_provider_local;
     providers.exported_symbols = exported_symbols_provider_local;
-    providers.symbol_export_level = symbol_export_level_provider;
+    providers.upstream_monomorphizations = upstream_monomorphizations_provider;
+    providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
 }
 
 pub fn provide_extern(providers: &mut Providers) {
-    providers.is_reachable_non_generic = is_reachable_non_generic_provider;
-    providers.symbol_export_level = symbol_export_level_provider;
+    providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
+    providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider;
 }
 
-fn symbol_export_level_provider(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel {
+fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel {
     // We export anything that's not mangled at the "C" layer as it probably has
     // to do with ABI concerns. We do not, however, apply such treatment to
     // special symbols in the standard library for various plumbing between
index 1dcf349e23bd882028b1cd37a63f0828e48c1712..2c503bdab30a9b68745c2641635c3be58bfa2f4a 100644 (file)
@@ -118,44 +118,84 @@ pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         // This is sort of subtle. Inside our codegen unit we started off
         // compilation by predefining all our own `TransItem` instances. That
         // is, everything we're translating ourselves is already defined. That
-        // means that anything we're actually translating ourselves will have
-        // hit the above branch in `get_declared_value`. As a result, we're
-        // guaranteed here that we're declaring a symbol that won't get defined,
-        // or in other words we're referencing a foreign value.
+        // means that anything we're actually translating in this codegen unit
+        // will have hit the above branch in `get_declared_value`. As a result,
+        // we're guaranteed here that we're declaring a symbol that won't get
+        // defined, or in other words we're referencing a value from another
+        // codegen unit or even another crate.
         //
         // So because this is a foreign value we blanket apply an external
         // linkage directive because it's coming from a different object file.
         // The visibility here is where it gets tricky. This symbol could be
         // referencing some foreign crate or foreign library (an `extern`
         // block) in which case we want to leave the default visibility. We may
-        // also, though, have multiple codegen units.
-        //
-        // In the situation of multiple codegen units this function may be
-        // referencing a function from another codegen unit. If we're
-        // indeed referencing a symbol in another codegen unit then we're in one
-        // of two cases:
-        //
-        //  * This is a symbol defined in a foreign crate and we're just
-        //    monomorphizing in another codegen unit. In this case this symbols
-        //    is for sure not exported, so both codegen units will be using
-        //    hidden visibility. Hence, we apply a hidden visibility here.
-        //
-        //  * This is a symbol defined in our local crate. If the symbol in the
-        //    other codegen unit is also not exported then like with the foreign
-        //    case we apply a hidden visibility. If the symbol is exported from
-        //    the foreign object file, however, then we leave this at the
-        //    default visibility as we'll just import it naturally.
+        // also, though, have multiple codegen units. It could be a
+        // monomorphization, in which case its expected visibility depends on
+        // whether we are sharing generics or not. The important thing here is
+        // that the visibility we apply to the declaration is the same one that
+        // has been applied to the definition (wherever that definition may be).
         unsafe {
             llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);
 
-            if cx.tcx.is_translated_item(instance_def_id) {
-                if instance_def_id.is_local() {
-                    if !cx.tcx.is_reachable_non_generic(instance_def_id) {
-                        llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
+            let is_generic = instance.substs.types().next().is_some();
+
+            if is_generic {
+                // This is a monomorphization. Its expected visibility depends
+                // on whether we are in share-generics mode.
+
+                if cx.tcx.share_generics() {
+                    // We are in share_generics mode.
+
+                    if instance_def_id.is_local() {
+                        // This is a definition from the current crate. If the
+                        // definition is unreachable for downstream crates or
+                        // the current crate does not re-export generics, the
+                        // definition of the instance will have been declared
+                        // as `hidden`.
+                        if cx.tcx.is_unreachable_local_definition(instance_def_id) ||
+                           !cx.tcx.local_crate_exports_generics() {
+                            llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
+                        }
+                    } else {
+                        // This is a monomorphization of a generic function
+                        // defined in an upstream crate.
+                        if cx.tcx.upstream_monomorphizations_for(instance_def_id)
+                                 .map(|set| set.contains_key(instance.substs))
+                                 .unwrap_or(false) {
+                            // This is instantiated in another crate. It cannot
+                            // be `hidden`.
+                        } else {
+                            // This is a local instantiation of an upstream definition.
+                            // If the current crate does not re-export it
+                            // (because it is a C library or an executable), it
+                            // will have been declared `hidden`.
+                            if !cx.tcx.local_crate_exports_generics() {
+                                llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
+                            }
+                        }
                     }
                 } else {
+                    // When not sharing generics, all instances are in the same
+                    // crate and have hidden visibility
                     llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                 }
+            } else {
+                // This is a non-generic function
+                if cx.tcx.is_translated_item(instance_def_id) {
+                    // This is a function that is instantiated in the local crate
+
+                    if instance_def_id.is_local() {
+                        // This is function that is defined in the local crate.
+                        // If it is not reachable, it is hidden.
+                        if !cx.tcx.is_reachable_non_generic(instance_def_id) {
+                            llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
+                        }
+                    } else {
+                        // This is a function from an upstream crate that has
+                        // been instantiated here. These are always hidden.
+                        llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
+                    }
+                }
             }
         }
 
index e8a1eb3071a2959b7033c6015b1b10cb04ab500b..f8f2fdd9320d2a5fd061a7137664f1b9200eb465 100644 (file)
@@ -33,6 +33,7 @@
 #![cfg_attr(stage0, feature(conservative_impl_trait))]
 #![feature(optin_builtin_traits)]
 #![feature(inclusive_range_fields)]
+#![feature(underscore_lifetimes)]
 
 use rustc::dep_graph::WorkProduct;
 use syntax_pos::symbol::Symbol;
index f9f93730255e6e302409d59615671653c1030cbf..af174f7ce8516f2eef0b536ce3313951a73ee4f4 100644 (file)
 use rustc::middle::weak_lang_items;
 use rustc_mir::monomorphize::Instance;
 use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode};
-use rustc::hir::def_id::DefId;
+use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::hir::map as hir_map;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::fold::TypeVisitor;
@@ -170,32 +170,45 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         assert!(!substs.needs_subst());
         substs.visit_with(&mut hasher);
 
-        let mut avoid_cross_crate_conflicts = false;
-
-        // 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() {
-            avoid_cross_crate_conflicts = true;
-        }
-
-        // If we're dealing with an instance of a function that's inlined from
-        // another crate but we're marking it as globally shared to our
-        // compliation (aka we're not making an internal copy in each of our
-        // codegen units) then this symbol may become an exported (but hidden
-        // visibility) symbol. This means that multiple crates may do the same
-        // and we want to be sure to avoid any symbol conflicts here.
-        match MonoItem::Fn(instance).instantiation_mode(tcx) {
-            InstantiationMode::GloballyShared { may_conflict: true } => {
-                avoid_cross_crate_conflicts = true;
-            }
-            _ => {}
-        }
+        let is_generic = substs.types().next().is_some();
+        let avoid_cross_crate_conflicts =
+            // 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.
+            is_generic ||
+
+            // If we're dealing with an instance of a function that's inlined from
+            // another crate but we're marking it as globally shared to our
+            // compliation (aka we're not making an internal copy in each of our
+            // codegen units) then this symbol may become an exported (but hidden
+            // visibility) symbol. This means that multiple crates may do the same
+            // and we want to be sure to avoid any symbol conflicts here.
+            match MonoItem::Fn(instance).instantiation_mode(tcx) {
+                InstantiationMode::GloballyShared { may_conflict: true } => true,
+                _ => false,
+            };
 
         if avoid_cross_crate_conflicts {
-            hasher.hash(tcx.crate_name.as_str());
-            hasher.hash(tcx.sess.local_crate_disambiguator());
+            let instantiating_crate = if is_generic {
+                if !def_id.is_local() && tcx.share_generics() {
+                    // If we are re-using a monomorphization from another crate,
+                    // we have to compute the symbol hash accordingly.
+                    let upstream_monomorphizations =
+                        tcx.upstream_monomorphizations_for(def_id);
+
+                    upstream_monomorphizations.and_then(|monos| monos.get(&substs)
+                                                                     .cloned())
+                                              .unwrap_or(LOCAL_CRATE)
+                } else {
+                    LOCAL_CRATE
+                }
+            } else {
+                LOCAL_CRATE
+            };
+
+            hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]);
+            hasher.hash(&tcx.crate_disambiguator(instantiating_crate));
         }
     });
 
diff --git a/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs b/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs
new file mode 100644 (file)
index 0000000..b742da8
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags:-Zshare-generics=yes
+
+#![crate_type="rlib"]
+
+pub fn generic_fn<T>(x: T, y: T) -> (T, T) {
+    (x, y)
+}
+
+pub fn use_generic_fn_f32() -> (f32, f32) {
+    generic_fn(0.0f32, 1.0f32)
+}
index bdb31265e2fb2e14692fad0d841def04eaac321e..140b43c85d548d93d61cbedfb7b8e156cbd5c0f3 100644 (file)
@@ -11,7 +11,7 @@
 // ignore-tidy-linelength
 // We specify -Z incremental here because we want to test the partitioning for
 // incremental compilation
-// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/extern-generic
+// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y
 
 #![allow(dead_code)]
 #![crate_type="lib"]
@@ -59,4 +59,4 @@ fn non_user() {}
 // Make sure the two generic functions from the extern crate get instantiated
 // once for the current crate
 //~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External]
-//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[Internal]
+//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External]
diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs
new file mode 100644 (file)
index 0000000..d352609
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+// compile-flags:-Zprint-trans-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe
+
+#![crate_type="rlib"]
+
+// aux-build:shared_generics_aux.rs
+extern crate shared_generics_aux;
+
+//~ TRANS_ITEM fn shared_generics::foo[0]
+pub fn foo() {
+
+    //~ TRANS_ITEM fn shared_generics_aux::generic_fn[0]<u16> @@ shared_generics_aux.volatile[External]
+    let _ = shared_generics_aux::generic_fn(0u16, 1u16);
+
+    // This should not generate a monomorphization because it's already
+    // available in `shared_generics_aux`.
+    let _ = shared_generics_aux::generic_fn(0.0f32, 3.0f32);
+}
+
+// TRANS_ITEM drop-glue i8
diff --git a/src/test/codegen/local-generics-in-exe-internalized.rs b/src/test/codegen/local-generics-in-exe-internalized.rs
new file mode 100644 (file)
index 0000000..162a025
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes -Zshare-generics=yes
+
+// Check that local generics are internalized if they are in the same CGU
+
+// CHECK: define internal {{.*}} @_ZN34local_generics_in_exe_internalized3foo{{.*}}
+pub fn foo<T>(x: T, y: T) -> (T, T) {
+    (x, y)
+}
+
+fn main() {
+    let _ = foo(0u8, 1u8);
+}
index f1ada814bdb804648c8e3d40a279d844ef4673b1..17d470063fc934f6ce8fb418f00adf9863e4f1e5 100644 (file)
@@ -23,11 +23,11 @@ COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.dylib
 endif
 
 all:
-       $(RUSTC) an_rlib.rs
-       $(RUSTC) a_cdylib.rs
-       $(RUSTC) a_rust_dylib.rs
-       $(RUSTC) an_executable.rs
-       $(RUSTC) a_cdylib.rs --crate-name combined_rlib_dylib --crate-type=rlib,cdylib
+       $(RUSTC) -Zshare-generics=no an_rlib.rs
+       $(RUSTC) -Zshare-generics=no a_cdylib.rs
+       $(RUSTC) -Zshare-generics=no a_rust_dylib.rs
+       $(RUSTC) -Zshare-generics=no an_executable.rs
+       $(RUSTC) -Zshare-generics=no a_cdylib.rs --crate-name combined_rlib_dylib --crate-type=rlib,cdylib
 
        # Check that a cdylib exports its public #[no_mangle] functions
        [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ]
@@ -39,10 +39,15 @@ all:
        # Check that a Rust dylib exports its monomorphic functions
        [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
        [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ]
+       # Check that a Rust dylib does not export generics if -Zshare-generics=no
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rust_dylib.*E)" -eq "0" ]
+
 
        # Check that a Rust dylib exports the monomorphic functions from its dependencies
        [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
        [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ]
+       # Check that a Rust dylib does not export generics if -Zshare-generics=no
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rlib.*E)" -eq "0" ]
 
        # Check that an executable does not export any dynamic symbols
        [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ]
@@ -57,4 +62,31 @@ all:
        [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
        # Check that a cdylib DOES NOT export any public Rust functions
        [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ]
+
+
+       $(RUSTC) -Zshare-generics=yes an_rlib.rs
+       $(RUSTC) -Zshare-generics=yes a_cdylib.rs
+       $(RUSTC) -Zshare-generics=yes a_rust_dylib.rs
+       $(RUSTC) -Zshare-generics=yes an_executable.rs
+
+       # Check that a cdylib exports its public #[no_mangle] functions
+       [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ]
+       # Check that a cdylib exports the public #[no_mangle] functions of dependencies
+       [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
+       # Check that a cdylib DOES NOT export any public Rust functions
+       [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ]
+
+       # Check that a Rust dylib exports its monomorphic functions, including generics this time
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ]
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rust_dylib.*E)" -eq "1" ]
+
+       # Check that a Rust dylib exports the monomorphic functions from its dependencies
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ]
+       [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rlib.*E)" -eq "1" ]
+
+       # Check that an executable does not export any dynamic symbols
+       [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ]
+       [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_rust_function_from_exe)" -eq "0" ]
 endif
index b826211c9a42afab0b1aefc0161b1e37ace56d88..99e748ec2efc88e8659f64ed2eb97ef08c52efe1 100644 (file)
@@ -17,4 +17,9 @@ pub fn public_rust_function_from_rust_dylib() {}
 
 // This should be exported
 #[no_mangle]
-pub extern "C" fn public_c_function_from_rust_dylib() {}
+pub extern "C" fn public_c_function_from_rust_dylib() {
+    let _ = public_generic_function_from_rust_dylib(1u16);
+}
+
+// This should be exported if -Zshare-generics=yes
+pub fn public_generic_function_from_rust_dylib<T>(x: T) -> T { x }
index cd19500d14021f0e253fb75067c5caf4b20084a0..a1d73afd30b110684d51086d761cfa619e6d9665 100644 (file)
 pub fn public_rust_function_from_rlib() {}
 
 #[no_mangle]
-pub extern "C" fn public_c_function_from_rlib() {}
+pub extern "C" fn public_c_function_from_rlib() {
+    let _ = public_generic_function_from_rlib(0u64);
+}
+
+pub fn public_generic_function_from_rlib<T>(x: T) -> T {
+    x
+}