Make sure that all upstream generics get re-exported from Rust dylibs.

This commit is contained in:
Michael Woerister 2020-01-16 13:21:10 +01:00
parent 900811e430
commit 31095d7e37
6 changed files with 143 additions and 51 deletions

View File

@ -32,7 +32,9 @@ pub enum ExportedSymbol<'tcx> {
} }
impl<'tcx> ExportedSymbol<'tcx> { impl<'tcx> ExportedSymbol<'tcx> {
pub fn symbol_name(&self, tcx: TyCtxt<'tcx>) -> ty::SymbolName { /// This is the symbol name of an instance if it is instantiated in the
/// local crate.
pub fn symbol_name_for_local_instance(&self, tcx: TyCtxt<'tcx>) -> ty::SymbolName {
match *self { match *self {
ExportedSymbol::NonGeneric(def_id) => tcx.symbol_name(ty::Instance::mono(tcx, def_id)), ExportedSymbol::NonGeneric(def_id) => tcx.symbol_name(ty::Instance::mono(tcx, def_id)),
ExportedSymbol::Generic(def_id, substs) => { ExportedSymbol::Generic(def_id, substs) => {
@ -50,9 +52,22 @@ impl<'tcx> ExportedSymbol<'tcx> {
} }
ExportedSymbol::Generic(..) | ExportedSymbol::NoDefId(_) => cmp::Ordering::Less, ExportedSymbol::Generic(..) | ExportedSymbol::NoDefId(_) => cmp::Ordering::Less,
}, },
ExportedSymbol::Generic(..) => match *other { ExportedSymbol::Generic(self_def_id, self_substs) => match *other {
ExportedSymbol::NonGeneric(_) => cmp::Ordering::Greater, ExportedSymbol::NonGeneric(_) => cmp::Ordering::Greater,
ExportedSymbol::Generic(..) => self.symbol_name(tcx).cmp(&other.symbol_name(tcx)), ExportedSymbol::Generic(other_def_id, other_substs) => {
// We compare the symbol names because they are cached as query
// results which makes them relatively cheap to access repeatedly.
//
// It might be even faster to build a local cache of stable IDs
// for sorting. Exported symbols are really only sorted once
// in order to make the `exported_symbols` query result stable.
let self_symbol_name =
tcx.symbol_name(ty::Instance::new(self_def_id, self_substs));
let other_symbol_name =
tcx.symbol_name(ty::Instance::new(other_def_id, other_substs));
self_symbol_name.cmp(&other_symbol_name)
}
ExportedSymbol::NoDefId(_) => cmp::Ordering::Less, ExportedSymbol::NoDefId(_) => cmp::Ordering::Less,
}, },
ExportedSymbol::NoDefId(self_symbol_name) => match *other { ExportedSymbol::NoDefId(self_symbol_name) => match *other {

View File

@ -557,6 +557,9 @@ rustc_queries! {
desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) }
} }
/// The `symbol_name` query provides the symbol name for calling a
/// given instance from the local crate. In particular, it will also
/// look up the correct symbol name of instances from upstream crates.
query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName { query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName {
no_force no_force
desc { "computing the symbol for `{}`", key } desc { "computing the symbol for `{}`", key }
@ -971,6 +974,11 @@ rustc_queries! {
} }
Linking { Linking {
/// The list of symbols exported from the given crate.
///
/// - All names contained in `exported_symbols(cnum)` are guaranteed to
/// correspond to a publicly visible symbol in `cnum` machine code.
/// - The `exported_symbols` sets of different crates do not intersect.
query exported_symbols(_: CrateNum) query exported_symbols(_: CrateNum)
-> Arc<Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)>> { -> Arc<Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)>> {
desc { "exported_symbols" } desc { "exported_symbols" }

View File

@ -1103,7 +1103,11 @@ fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() {
if level.is_below_threshold(export_threshold) { if level.is_below_threshold(export_threshold) {
symbols.push(symbol.symbol_name(tcx).to_string()); symbols.push(symbol_export::symbol_name_for_instance_in_crate(
tcx,
symbol,
LOCAL_CRATE,
));
} }
} }
@ -1124,12 +1128,7 @@ fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
continue; continue;
} }
// FIXME rust-lang/rust#64319, rust-lang/rust#64872: symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
// We want to block export of generics from dylibs,
// but we must fix rust-lang/rust#65890 before we can
// do that robustly.
symbols.push(symbol.symbol_name(tcx).to_string());
} }
} }
} }

View File

@ -8,6 +8,7 @@ use rustc::ty::query::Providers;
use rustc::ty::subst::SubstsRef; use rustc::ty::subst::SubstsRef;
use rustc::ty::Instance; use rustc::ty::Instance;
use rustc::ty::{SymbolName, TyCtxt}; use rustc::ty::{SymbolName, TyCtxt};
use rustc_codegen_utils::symbol_names;
use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
@ -358,3 +359,32 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
SymbolExportLevel::Rust SymbolExportLevel::Rust
} }
} }
/// This is the symbol name of the given instance instantiated in a specific crate.
pub fn symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
instantiating_crate: CrateNum,
) -> String {
// If this is something instantiated in the local crate then we might
// already have cached the name as a query result.
if instantiating_crate == LOCAL_CRATE {
return symbol.symbol_name_for_local_instance(tcx).to_string();
}
// This is something instantiated in an upstream crate, so we have to use
// the slower (because uncached) version of computing the symbol name.
match symbol {
ExportedSymbol::NonGeneric(def_id) => symbol_names::symbol_name_for_instance_in_crate(
tcx,
Instance::mono(tcx, def_id),
instantiating_crate,
),
ExportedSymbol::Generic(def_id, substs) => symbol_names::symbol_name_for_instance_in_crate(
tcx,
Instance::new(def_id, substs),
instantiating_crate,
),
ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
}
}

View File

@ -2,7 +2,7 @@ use super::command::Command;
use super::link::{self, get_linker, remove}; use super::link::{self, get_linker, remove};
use super::linker::LinkerInfo; use super::linker::LinkerInfo;
use super::lto::{self, SerializedModule}; use super::lto::{self, SerializedModule};
use super::symbol_export::ExportedSymbols; use super::symbol_export::{symbol_name_for_instance_in_crate, ExportedSymbols};
use crate::{ use crate::{
CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind,
RLIB_BYTECODE_EXTENSION, RLIB_BYTECODE_EXTENSION,
@ -956,7 +956,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
let symbols = tcx let symbols = tcx
.exported_symbols(cnum) .exported_symbols(cnum)
.iter() .iter()
.map(|&(s, lvl)| (s.symbol_name(tcx).to_string(), lvl)) .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl))
.collect(); .collect();
Arc::new(symbols) Arc::new(symbols)
}; };

View File

@ -91,8 +91,9 @@ use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc::mir::mono::{InstantiationMode, MonoItem}; use rustc::mir::mono::{InstantiationMode, MonoItem};
use rustc::session::config::SymbolManglingVersion; use rustc::session::config::SymbolManglingVersion;
use rustc::ty::query::Providers; use rustc::ty::query::Providers;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Instance, TyCtxt}; use rustc::ty::{self, Instance, TyCtxt};
use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::Node; use rustc_hir::Node;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
@ -102,15 +103,70 @@ use log::debug;
mod legacy; mod legacy;
mod v0; mod v0;
pub fn provide(providers: &mut Providers<'_>) { /// This function computes the symbol name for the given `instance` and the
*providers = Providers { /// given instantiating crate. That is, if you know that instance X is
symbol_name: |tcx, instance| ty::SymbolName { name: symbol_name(tcx, instance) }, /// instantiated in crate Y, this is the symbol name this instance would have.
pub fn symbol_name_for_instance_in_crate(
..*providers tcx: TyCtxt<'tcx>,
}; instance: Instance<'tcx>,
instantiating_crate: CrateNum,
) -> String {
compute_symbol_name(tcx, instance, || instantiating_crate)
} }
fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol { pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers { symbol_name: symbol_name_provider, ..*providers };
}
// The `symbol_name` query provides the symbol name for calling a given
// instance from the local crate. In particular, it will also look up the
// correct symbol name of instances from upstream crates.
fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName {
let symbol_name = compute_symbol_name(tcx, instance, || {
// This closure determines the instantiating crate for instances that
// need an instantiating-crate-suffix for their symbol name, in order
// to differentiate between local copies.
//
// For generics we might find re-usable upstream instances. For anything
// else we rely on their being a local copy available.
if is_generic(instance.substs) {
let def_id = instance.def_id();
if !def_id.is_local() && tcx.sess.opts.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(&instance.substs).cloned())
// If there is no instance available upstream, there'll be
// one in the current crate.
.unwrap_or(LOCAL_CRATE)
} else {
// For generic functions defined in the current crate, there
// can be no upstream instances. Also, if we don't share
// generics, we'll instantiate a local copy too.
LOCAL_CRATE
}
} else {
// For non-generic things that need to avoid naming conflicts, we
// always instantiate a copy in the local crate.
LOCAL_CRATE
}
});
ty::SymbolName { name: Symbol::intern(&symbol_name) }
}
/// Computes the symbol name for the given instance. This function will call
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
/// into the symbol name.
fn compute_symbol_name(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
compute_instantiating_crate: impl FnOnce() -> CrateNum,
) -> String {
let def_id = instance.def_id(); let def_id = instance.def_id();
let substs = instance.substs; let substs = instance.substs;
@ -121,11 +177,11 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
if def_id.is_local() { if def_id.is_local() {
if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id) { if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id) {
let disambiguator = tcx.sess.local_crate_disambiguator(); let disambiguator = tcx.sess.local_crate_disambiguator();
return Symbol::intern(&tcx.sess.generate_plugin_registrar_symbol(disambiguator)); return tcx.sess.generate_plugin_registrar_symbol(disambiguator);
} }
if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id) { if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id) {
let disambiguator = tcx.sess.local_crate_disambiguator(); let disambiguator = tcx.sess.local_crate_disambiguator();
return Symbol::intern(&tcx.sess.generate_proc_macro_decls_symbol(disambiguator)); return tcx.sess.generate_proc_macro_decls_symbol(disambiguator);
} }
} }
@ -162,29 +218,28 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
|| !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)
{ {
if let Some(name) = attrs.link_name { if let Some(name) = attrs.link_name {
return name; return name.to_string();
} }
return tcx.item_name(def_id); return tcx.item_name(def_id).to_string();
} }
} }
if let Some(name) = attrs.export_name { if let Some(name) = attrs.export_name {
// Use provided name // Use provided name
return name; return name.to_string();
} }
if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
// Don't mangle // Don't mangle
return tcx.item_name(def_id); return tcx.item_name(def_id).to_string();
} }
let is_generic = substs.non_erasable_generics().next().is_some();
let avoid_cross_crate_conflicts = let avoid_cross_crate_conflicts =
// If this is an instance of a generic function, we also hash in // If this is an instance of a generic function, we also hash in
// the ID of the instantiating crate. This avoids symbol conflicts // the ID of the instantiating crate. This avoids symbol conflicts
// in case the same instances is emitted in two crates of the same // in case the same instances is emitted in two crates of the same
// project. // project.
is_generic || is_generic(substs) ||
// If we're dealing with an instance of a function that's inlined from // 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 // another crate but we're marking it as globally shared to our
@ -197,25 +252,8 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
_ => false, _ => false,
}; };
let instantiating_crate = if avoid_cross_crate_conflicts { let instantiating_crate =
Some(if is_generic { if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None };
if !def_id.is_local() && tcx.sess.opts.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
})
} else {
None
};
// Pick the crate responsible for the symbol mangling version, which has to: // Pick the crate responsible for the symbol mangling version, which has to:
// 1. be stable for each instance, whether it's being defined or imported // 1. be stable for each instance, whether it's being defined or imported
@ -232,10 +270,12 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
tcx.symbol_mangling_version(mangling_version_crate) tcx.symbol_mangling_version(mangling_version_crate)
}; };
let mangled = match mangling_version { match mangling_version {
SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate),
SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate),
}; }
}
Symbol::intern(&mangled)
fn is_generic(substs: SubstsRef<'_>) -> bool {
substs.non_erasable_generics().next().is_some()
} }