Clean up handling of symbol export information.

This commit is contained in:
Michael Woerister 2018-02-23 16:25:03 +01:00
parent e5ee01143b
commit 33d5da1ee4
8 changed files with 231 additions and 209 deletions

View File

@ -32,7 +32,6 @@ use ich;
use ty::{self, TyCtxt};
use session::{Session, CrateDisambiguator};
use session::search_paths::PathKind;
use util::nodemap::NodeSet;
use std::any::Any;
use std::collections::BTreeMap;
@ -258,8 +257,7 @@ pub trait CrateStore {
// utility functions
fn encode_metadata<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
link_meta: &LinkMeta,
reachable: &NodeSet)
link_meta: &LinkMeta)
-> EncodedMetadata;
fn metadata_encoding_version(&self) -> &[u8];
}
@ -342,8 +340,7 @@ impl CrateStore for DummyCrateStore {
fn extern_mod_stmt_cnum_untracked(&self, emod_id: ast::NodeId) -> Option<CrateNum> { None }
fn encode_metadata<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
link_meta: &LinkMeta,
reachable: &NodeSet)
link_meta: &LinkMeta)
-> EncodedMetadata {
bug!("encode_metadata")
}

View File

@ -46,7 +46,7 @@ use ty::layout::{LayoutDetails, TargetDataLayout};
use ty::maps;
use ty::steal::Steal;
use ty::BindingMode;
use util::nodemap::{NodeMap, NodeSet, DefIdSet, ItemLocalMap};
use util::nodemap::{NodeMap, DefIdSet, ItemLocalMap};
use util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::accumulate_vec::AccumulateVec;
use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
@ -1417,10 +1417,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
pub fn encode_metadata(self, link_meta: &LinkMeta, reachable: &NodeSet)
pub fn encode_metadata(self, link_meta: &LinkMeta)
-> EncodedMetadata
{
self.cstore.encode_metadata(self, link_meta, reachable)
self.cstore.encode_metadata(self, link_meta)
}
}

View File

@ -27,7 +27,7 @@ use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
use rustc::hir::map::{DefKey, DefPath, DefPathHash};
use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir::map::definitions::DefPathTable;
use rustc::util::nodemap::{NodeSet, DefIdMap};
use rustc::util::nodemap::DefIdMap;
use std::any::Any;
use rustc_data_structures::sync::Lrc;
@ -517,11 +517,10 @@ impl CrateStore for cstore::CStore {
fn encode_metadata<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
link_meta: &LinkMeta,
reachable: &NodeSet)
link_meta: &LinkMeta)
-> EncodedMetadata
{
encoder::encode_metadata(tcx, link_meta, reachable)
encoder::encode_metadata(tcx, link_meta)
}
fn metadata_encoding_version(&self) -> &[u8]

View File

@ -27,7 +27,7 @@ use rustc::ty::{self, Ty, TyCtxt, ReprOptions};
use rustc::ty::codec::{self as ty_codec, TyEncoder};
use rustc::session::config::{self, CrateTypeProcMacro};
use rustc::util::nodemap::{FxHashMap, NodeSet};
use rustc::util::nodemap::{FxHashMap, DefIdSet};
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_serialize::{Encodable, Encoder, SpecializedEncoder, opaque};
@ -53,7 +53,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
opaque: opaque::Encoder<'a>,
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
link_meta: &'a LinkMeta,
reachable_non_generics: &'a NodeSet,
lazy_state: LazyState,
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
@ -395,9 +394,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// Encode exported symbols info.
i = self.position();
let reachable_non_generics = self.tcx.reachable_non_generics(LOCAL_CRATE);
let reachable_non_generics = self.tracked(
IsolatedEncoder::encode_reachable_non_generics,
self.reachable_non_generics);
&reachable_non_generics);
let reachable_non_generics_bytes = self.position() - i;
// Encode and index the items.
@ -1389,11 +1389,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
// symbol associated with them (they weren't translated) or if they're an FFI
// definition (as that's not defined in this crate).
fn encode_reachable_non_generics(&mut self,
reachable_non_generics: &NodeSet)
reachable_non_generics: &DefIdSet)
-> LazySeq<DefIndex> {
let tcx = self.tcx;
self.lazy_seq(reachable_non_generics.iter()
.map(|&id| tcx.hir.local_def_id(id).index))
self.lazy_seq(reachable_non_generics.iter().map(|def_id| {
debug_assert!(def_id.is_local());
def_id.index
}))
}
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {
@ -1666,8 +1667,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'a, 'tcx> {
// generated regardless of trailing bytes that end up in it.
pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
link_meta: &LinkMeta,
reachable_non_generics: &NodeSet)
link_meta: &LinkMeta)
-> EncodedMetadata
{
let mut cursor = Cursor::new(vec![]);
@ -1681,7 +1681,6 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
opaque: opaque::Encoder::new(&mut cursor),
tcx,
link_meta,
reachable_non_generics,
lazy_state: LazyState::NoNode,
type_shorthands: Default::default(),
predicate_shorthands: Default::default(),

View File

@ -11,15 +11,15 @@
use rustc_data_structures::sync::Lrc;
use std::sync::Arc;
use base;
use monomorphize::Instance;
use rustc::hir;
use rustc::hir::def_id::CrateNum;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::middle::exported_symbols::SymbolExportLevel;
use rustc::session::config;
use rustc::ty::TyCtxt;
use rustc::ty::maps::Providers;
use rustc::util::nodemap::FxHashMap;
use rustc::util::nodemap::{FxHashMap, DefIdSet};
use rustc_allocator::ALLOCATOR_METHODS;
use syntax::attr;
@ -60,145 +60,228 @@ pub fn crates_export_threshold(crate_types: &[config::CrateType])
}
}
pub fn provide(providers: &mut Providers) {
providers.reachable_non_generics = |tcx, cnum| {
let export_threshold = threshold(tcx);
Lrc::new(tcx.exported_symbols(cnum)
.iter()
.filter_map(|&(_, id, level)| {
id.and_then(|id| {
if level.is_below_threshold(export_threshold) {
Some(id)
fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
cnum: CrateNum)
-> Lrc<DefIdSet>
{
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(|&(_, opt_def_id, level)| {
if let Some(def_id) = opt_def_id {
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<(String,
Option<DefId>,
SymbolExportLevel)>>
{
assert_eq!(cnum, LOCAL_CRATE);
if !tcx.sess.opts.output_types.should_trans() {
return Arc::new(vec![])
}
let mut reachable_non_generics: DefIdSet = tcx.reachable_set(LOCAL_CRATE).0
.iter()
.filter_map(|&node_id| {
// We want to ignore some FFI functions that are not exposed from
// this crate. Reachable FFI functions can be lumped into two
// categories:
//
// 1. Those that are included statically via a static library
// 2. Those included otherwise (e.g. dynamically or via a framework)
//
// Although our LLVM module is not literally emitting code for the
// statically included symbols, it's an export of our library which
// needs to be passed on to the linker and encoded in the metadata.
//
// As a result, if this id is an FFI item (foreign item) then we only
// let it through if it's included statically.
match tcx.hir.get(node_id) {
hir::map::NodeForeignItem(..) => {
let def_id = tcx.hir.local_def_id(node_id);
if tcx.is_statically_included_foreign_item(def_id) {
Some(def_id)
} else {
None
}
})
})
.collect())
};
}
providers.is_reachable_non_generic = |tcx, id| {
tcx.reachable_non_generics(id.krate).contains(&id)
};
// Only consider nodes that actually have exported symbols.
hir::map::NodeItem(&hir::Item {
node: hir::ItemStatic(..),
..
}) |
hir::map::NodeItem(&hir::Item {
node: hir::ItemFn(..), ..
}) |
hir::map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(..),
..
}) => {
let def_id = tcx.hir.local_def_id(node_id);
let generics = tcx.generics_of(def_id);
if (generics.parent_types == 0 && generics.types.is_empty()) &&
// Functions marked with #[inline] are only ever translated
// with "internal" linkage and are never exported.
!Instance::mono(tcx, def_id).def.requires_local(tcx) {
Some(def_id)
} else {
None
}
}
providers.exported_symbols = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
let local_exported_symbols = base::find_exported_symbols(tcx);
let mut local_crate: Vec<_> = local_exported_symbols
.iter()
.map(|&node_id| {
tcx.hir.local_def_id(node_id)
})
.map(|def_id| {
let name = tcx.symbol_name(Instance::mono(tcx, def_id));
let export_level = export_level(tcx, def_id);
debug!("EXPORTED SYMBOL (local): {} ({:?})", name, export_level);
(str::to_owned(&name), Some(def_id), export_level)
})
.collect();
if let Some(_) = *tcx.sess.entry_fn.borrow() {
local_crate.push(("main".to_string(),
None,
SymbolExportLevel::C));
}
if tcx.sess.allocator_kind.get().is_some() {
for method in ALLOCATOR_METHODS {
local_crate.push((format!("__rust_{}", method.name),
None,
SymbolExportLevel::Rust));
_ => None
}
})
.collect();
if let Some(id) = tcx.sess.derive_registrar_fn.get() {
reachable_non_generics.insert(tcx.hir.local_def_id(id));
}
if let Some(id) = tcx.sess.plugin_registrar_fn.get() {
reachable_non_generics.insert(tcx.hir.local_def_id(id));
}
let mut symbols: Vec<_> = reachable_non_generics
.iter()
.map(|&def_id| {
let name = tcx.symbol_name(Instance::mono(tcx, def_id));
let export_level = tcx.symbol_export_level(def_id);
debug!("EXPORTED SYMBOL (local): {} ({:?})", name, export_level);
(str::to_owned(&name), Some(def_id), export_level)
})
.collect();
if let Some(_) = *tcx.sess.entry_fn.borrow() {
symbols.push(("main".to_string(), None, SymbolExportLevel::C));
}
if tcx.sess.allocator_kind.get().is_some() {
for method in ALLOCATOR_METHODS {
symbols.push((format!("__rust_{}", method.name),
None,
SymbolExportLevel::Rust));
}
}
if let Some(id) = tcx.sess.derive_registrar_fn.get() {
let def_id = tcx.hir.local_def_id(id);
let disambiguator = tcx.sess.local_crate_disambiguator();
let registrar = tcx.sess.generate_derive_registrar_symbol(disambiguator);
local_crate.push((registrar, Some(def_id), SymbolExportLevel::C));
}
if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) {
symbols.push((metadata_symbol_name(tcx),
None,
SymbolExportLevel::Rust));
}
if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) {
local_crate.push((metadata_symbol_name(tcx),
None,
SymbolExportLevel::Rust));
}
// Sort so we get a stable incr. comp. hash.
symbols.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| {
name1.cmp(name2)
});
// Sort so we get a stable incr. comp. hash.
local_crate.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| {
name1.cmp(name2)
});
Arc::new(symbols)
}
Arc::new(local_crate)
};
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.exported_symbols = exported_symbols_provider_local;
providers.symbol_export_level = symbol_export_level_provider;
}
providers.symbol_export_level = export_level;
fn exported_symbols_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
cnum: CrateNum)
-> Arc<Vec<(String,
Option<DefId>,
SymbolExportLevel)>>
{
// If this crate is a plugin and/or a custom derive crate, then
// we're not even going to link those in so we skip those crates.
if tcx.plugin_registrar_fn(cnum).is_some() ||
tcx.derive_registrar_fn(cnum).is_some() {
return Arc::new(Vec::new())
}
// Check to see if this crate is a "special runtime crate". These
// crates, implementation details of the standard library, typically
// have a bunch of `pub extern` and `#[no_mangle]` functions as the
// ABI between them. We don't want their symbols to have a `C`
// export level, however, as they're just implementation details.
// Down below we'll hardwire all of the symbols to the `Rust` export
// level instead.
let special_runtime_crate =
tcx.is_panic_runtime(cnum) || tcx.is_compiler_builtins(cnum);
let mut crate_exports: Vec<_> = tcx
.reachable_non_generics(cnum)
.iter()
.map(|&def_id| {
let name = tcx.symbol_name(Instance::mono(tcx, def_id));
let export_level = if special_runtime_crate {
// We can probably do better here by just ensuring that
// it has hidden visibility rather than public
// visibility, as this is primarily here to ensure it's
// not stripped during LTO.
//
// In general though we won't link right if these
// symbols are stripped, and LTO currently strips them.
if &*name == "rust_eh_personality" ||
&*name == "rust_eh_register_frames" ||
&*name == "rust_eh_unregister_frames" {
SymbolExportLevel::C
} else {
SymbolExportLevel::Rust
}
} else {
tcx.symbol_export_level(def_id)
};
debug!("EXPORTED SYMBOL (re-export): {} ({:?})", name, export_level);
(str::to_owned(&name), Some(def_id), export_level)
})
.collect();
// Sort so we get a stable incr. comp. hash.
crate_exports.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| {
name1.cmp(name2)
});
Arc::new(crate_exports)
}
pub fn provide_extern(providers: &mut Providers) {
providers.exported_symbols = |tcx, cnum| {
// If this crate is a plugin and/or a custom derive crate, then
// we're not even going to link those in so we skip those crates.
if tcx.plugin_registrar_fn(cnum).is_some() ||
tcx.derive_registrar_fn(cnum).is_some() {
return Arc::new(Vec::new())
}
// Check to see if this crate is a "special runtime crate". These
// crates, implementation details of the standard library, typically
// have a bunch of `pub extern` and `#[no_mangle]` functions as the
// ABI between them. We don't want their symbols to have a `C`
// export level, however, as they're just implementation details.
// Down below we'll hardwire all of the symbols to the `Rust` export
// level instead.
let special_runtime_crate =
tcx.is_panic_runtime(cnum) || tcx.is_compiler_builtins(cnum);
let mut crate_exports: Vec<_> = tcx
.reachable_non_generics(cnum)
.iter()
.map(|&def_id| {
let name = tcx.symbol_name(Instance::mono(tcx, def_id));
let export_level = if special_runtime_crate {
// We can probably do better here by just ensuring that
// it has hidden visibility rather than public
// visibility, as this is primarily here to ensure it's
// not stripped during LTO.
//
// In general though we won't link right if these
// symbols are stripped, and LTO currently strips them.
if &*name == "rust_eh_personality" ||
&*name == "rust_eh_register_frames" ||
&*name == "rust_eh_unregister_frames" {
SymbolExportLevel::C
} else {
SymbolExportLevel::Rust
}
} else {
export_level(tcx, def_id)
};
debug!("EXPORTED SYMBOL (re-export): {} ({:?})", name, export_level);
(str::to_owned(&name), Some(def_id), export_level)
})
.collect();
// Sort so we get a stable incr. comp. hash.
crate_exports.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| {
name1.cmp(name2)
});
Arc::new(crate_exports)
};
providers.is_reachable_non_generic = |tcx, id| {
tcx.reachable_non_generics(id.krate).contains(&id)
};
providers.symbol_export_level = export_level;
providers.exported_symbols = exported_symbols_provider_extern;
providers.is_reachable_non_generic = is_reachable_non_generic_provider;
providers.symbol_export_level = symbol_export_level_provider;
}
fn export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel {
fn symbol_export_level_provider(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

View File

@ -70,7 +70,7 @@ use time_graph;
use trans_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames};
use type_::Type;
use type_of::LayoutLlvmExt;
use rustc::util::nodemap::{NodeSet, FxHashMap, FxHashSet, DefIdSet};
use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet};
use CrateInfo;
use std::any::Any;
@ -89,7 +89,7 @@ use syntax::ast;
use mir::operand::OperandValue;
pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr};
pub use rustc_trans_utils::check_for_rustc_errors_attr;
pub use rustc_mir::monomorphize::item::linkage_by_name;
pub struct StatRecorder<'a, 'tcx: 'a> {
@ -606,8 +606,7 @@ fn contains_null(s: &str) -> bool {
fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
llmod_id: &str,
link_meta: &LinkMeta,
exported_symbols: &NodeSet)
link_meta: &LinkMeta)
-> (ContextRef, ModuleRef, EncodedMetadata) {
use std::io::Write;
use flate2::Compression;
@ -643,7 +642,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
EncodedMetadata::new());
}
let metadata = tcx.encode_metadata(link_meta, exported_symbols);
let metadata = tcx.encode_metadata(link_meta);
if kind == MetadataKind::Uncompressed {
return (metadata_llcx, metadata_llmod, metadata);
}
@ -718,13 +717,12 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
let link_meta = link::build_link_meta(crate_hash);
let exported_symbol_node_ids = find_exported_symbols(tcx);
// Translate the metadata.
let llmod_id = "metadata";
let (metadata_llcx, metadata_llmod, metadata) =
time(tcx.sess.time_passes(), "write metadata", || {
write_metadata(tcx, llmod_id, &link_meta, &exported_symbol_node_ids)
write_metadata(tcx, llmod_id, &link_meta)
});
let metadata_module = ModuleTranslation {

View File

@ -44,11 +44,7 @@ extern crate rustc_data_structures;
pub extern crate rustc as __rustc;
use rustc::ty::{TyCtxt, Instance};
use rustc::hir;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::hir::map as hir_map;
use rustc::util::nodemap::NodeSet;
use rustc::ty::TyCtxt;
pub mod diagnostics;
pub mod link;
@ -70,53 +66,4 @@ pub fn check_for_rustc_errors_attr(tcx: TyCtxt) {
}
}
/// The context provided lists a set of reachable ids as calculated by
/// middle::reachable, but this contains far more ids and symbols than we're
/// actually exposing from the object file. This function will filter the set in
/// the context to the set of ids which correspond to symbols that are exposed
/// from the object file being generated.
///
/// This list is later used by linkers to determine the set of symbols needed to
/// be exposed from a dynamic library and it's also encoded into the metadata.
pub fn find_exported_symbols<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> NodeSet {
tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| {
// Next, we want to ignore some FFI functions that are not exposed from
// this crate. Reachable FFI functions can be lumped into two
// categories:
//
// 1. Those that are included statically via a static library
// 2. Those included otherwise (e.g. dynamically or via a framework)
//
// Although our LLVM module is not literally emitting code for the
// statically included symbols, it's an export of our library which
// needs to be passed on to the linker and encoded in the metadata.
//
// As a result, if this id is an FFI item (foreign item) then we only
// let it through if it's included statically.
match tcx.hir.get(id) {
hir_map::NodeForeignItem(..) => {
let def_id = tcx.hir.local_def_id(id);
tcx.is_statically_included_foreign_item(def_id)
}
// Only consider nodes that actually have exported symbols.
hir_map::NodeItem(&hir::Item {
node: hir::ItemStatic(..), .. }) |
hir_map::NodeItem(&hir::Item {
node: hir::ItemFn(..), .. }) |
hir_map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(..), .. }) => {
let def_id = tcx.hir.local_def_id(id);
let generics = tcx.generics_of(def_id);
(generics.parent_types == 0 && generics.types.is_empty()) &&
// Functions marked with #[inline] are only ever translated
// with "internal" linkage and are never exported.
!Instance::mono(tcx, def_id).def.requires_local(tcx)
}
_ => false
}
}).collect()
}
__build_diagnostic_array! { librustc_trans_utils, DIAGNOSTICS }

View File

@ -247,8 +247,7 @@ impl TransCrate for MetadataOnlyTransCrate {
tcx.sess.abort_if_errors();
let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE));
let exported_symbols = ::find_exported_symbols(tcx);
let metadata = tcx.encode_metadata(&link_meta, &exported_symbols);
let metadata = tcx.encode_metadata(&link_meta);
box OngoingCrateTranslation {
metadata: metadata,