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.
[] ImplParent(DefId),
[] TraitOfItem(DefId),
[] IsReachableNonGeneric(DefId),
+ [] IsUnreachableLocalDefinition(DefId),
[] IsMirAvailable(DefId),
[] ItemAttrs(DefId),
[] TransFnAttrs(DefId),
[] 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 {
}
}
-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>) {
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),
// 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
}
#[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);
+ }
+ }
+ }
+}
"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 {
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};
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> {
}
}
+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)
//
// 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,
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!()); }
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
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
}
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()) }
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> {
// 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 {
_ => true,
}
})
- .cloned())
+ .cloned());
+
+ EncodedExportedSymbols {
+ len: lazy_seq.len,
+ position: lazy_seq.position,
+ }
}
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
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;
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>,
// 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,
+}
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!(),
}
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
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)
}
}
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 { .. } => {}
.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) {
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`
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(..) |
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
};
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
};
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,
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
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
_ => 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
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();
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)
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
// 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);
+ }
+ }
}
}
#![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;
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;
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));
}
});
--- /dev/null
+// 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)
+}
// 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"]
// 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]
--- /dev/null
+// 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
--- /dev/null
+// 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);
+}
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" ]
# 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" ]
[ "$$($(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
// 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 }
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
+}