From 906523efea60f4d58d6ce58bc61589d4ff3d7b4e Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 19 Oct 2017 11:01:31 +0200 Subject: [PATCH] Move collector to rustc_trans_utils --- src/Cargo.lock | 1 + src/librustc_trans/back/symbol_names.rs | 2 +- src/librustc_trans/base.rs | 30 +- src/librustc_trans/common.rs | 17 - src/librustc_trans/lib.rs | 5 +- src/librustc_trans/partitioning.rs | 2 +- src/librustc_trans/trans_item.rs | 417 +--------------- src/librustc_trans_utils/Cargo.toml | 1 + .../collector.rs | 3 +- src/librustc_trans_utils/common.rs | 78 +++ src/librustc_trans_utils/lib.rs | 5 + .../monomorphize.rs | 0 src/librustc_trans_utils/trans_item.rs | 464 ++++++++++++++++++ 13 files changed, 565 insertions(+), 460 deletions(-) rename src/{librustc_trans => librustc_trans_utils}/collector.rs (99%) create mode 100644 src/librustc_trans_utils/common.rs rename src/{librustc_trans => librustc_trans_utils}/monomorphize.rs (100%) create mode 100644 src/librustc_trans_utils/trans_item.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 328ce353e2a..0989c430df0 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1787,6 +1787,7 @@ dependencies = [ "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", + "rustc_data_structures 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index 66a27f1c4a9..0ebfe4daad1 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -98,7 +98,7 @@ //! DefPaths which are much more robust in the face of changes to the code base. use monomorphize::Instance; -use trans_item::{TransItemExt, InstantiationMode}; +use trans_item::{BaseTransItemExt, InstantiationMode}; use rustc::middle::weak_lang_items; use rustc::middle::trans::TransItem; diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 6b53b5b6411..5a3adde8d1d 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -70,7 +70,7 @@ use monomorphize::{self, Instance}; use partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; use symbol_names_test; use time_graph; -use trans_item::{TransItem, TransItemExt, DefPathBasedNames}; +use trans_item::{TransItem, BaseTransItemExt, TransItemExt, DefPathBasedNames}; use type_::Type; use type_of; use value::Value; @@ -93,6 +93,7 @@ use syntax::ast; use mir::lvalue::Alignment; pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr}; +pub use rustc_trans_utils::trans_item::linkage_by_name; pub struct StatRecorder<'a, 'tcx: 'a> { ccx: &'a CrateContext<'a, 'tcx>, @@ -618,33 +619,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance mir::trans_mir(ccx, lldecl, &mir, instance, sig); } -pub fn linkage_by_name(name: &str) -> Option { - use rustc::middle::trans::Linkage::*; - - // Use the names from src/llvm/docs/LangRef.rst here. Most types are only - // applicable to variable declarations and may not really make sense for - // Rust code in the first place but whitelist them anyway and trust that - // the user knows what s/he's doing. Who knows, unanticipated use cases - // may pop up in the future. - // - // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported - // and don't have to be, LLVM treats them as no-ops. - match name { - "appending" => Some(Appending), - "available_externally" => Some(AvailableExternally), - "common" => Some(Common), - "extern_weak" => Some(ExternalWeak), - "external" => Some(External), - "internal" => Some(Internal), - "linkonce" => Some(LinkOnceAny), - "linkonce_odr" => Some(LinkOnceODR), - "private" => Some(Private), - "weak" => Some(WeakAny), - "weak_odr" => Some(WeakODR), - _ => None, - } -} - pub fn set_link_section(ccx: &CrateContext, llval: ValueRef, attrs: &[ast::Attribute]) { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 52607904f73..e3856cabcf9 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -36,7 +36,6 @@ use libc::{c_uint, c_char}; use std::iter; use syntax::abi::Abi; -use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::{Span, DUMMY_SP}; @@ -552,22 +551,6 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn requests_inline<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: &ty::Instance<'tcx> -) -> bool { - if is_inline_instance(tcx, instance) { - return true - } - if let ty::InstanceDef::DropGlue(..) = instance.def { - // Drop glue wants to be instantiated at every translation - // unit, but without an #[inline] hint. We should make this - // available to normal end-users. - return true - } - attr::requests_inline(&instance.def.attrs(tcx)[..]) -} - pub fn is_inline_instance<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &ty::Instance<'tcx> diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 2b1c62c7f1c..88ec3a65d35 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -83,6 +83,9 @@ use rustc::ty::maps::Providers; use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc_trans_utils::collector; +use rustc_trans_utils::monomorphize; + mod diagnostics; pub mod back { @@ -124,7 +127,6 @@ mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64; mod callee; -mod collector; mod common; mod consts; mod context; @@ -137,7 +139,6 @@ mod machine; mod metadata; mod meth; mod mir; -mod monomorphize; mod partitioning; mod symbol_names_test; mod time_graph; diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 386806e4c9c..6980ba8a525 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -114,7 +114,7 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet}; use std::collections::hash_map::Entry; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; -use trans_item::{TransItem, TransItemExt, InstantiationMode}; +use trans_item::{TransItem, BaseTransItemExt, TransItemExt, InstantiationMode}; pub use rustc::middle::trans::CodegenUnit; diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 060f02ee23e..db1af8cdefb 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -24,50 +24,21 @@ use declare; use llvm; use monomorphize::Instance; use rustc::hir; -use rustc::hir::def_id::DefId; use rustc::middle::trans::{Linkage, Visibility}; -use rustc::session::config::OptLevel; -use rustc::traits; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::subst::{Subst, Substs}; +use rustc::ty::{self, TyCtxt, TypeFoldable}; use syntax::ast; -use syntax::attr::{self, InlineAttr}; +use syntax::attr; use syntax_pos::Span; use syntax_pos::symbol::Symbol; use type_of; -use std::fmt::{self, Write}; -use std::iter; +use std::fmt; pub use rustc::middle::trans::TransItem; -/// Describes how a translation item will be instantiated in object files. -#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] -pub enum InstantiationMode { - /// There will be exactly one instance of the given TransItem. It will have - /// external linkage so that it can be linked to from other codegen units. - GloballyShared { - /// In some compilation scenarios we may decide to take functions that - /// are typically `LocalCopy` and instead move them to `GloballyShared` - /// to avoid translating them a bunch of times. In this situation, - /// however, our local copy may conflict with other crates also - /// inlining the same function. - /// - /// This flag indicates that this situation is occuring, and informs - /// symbol name calculation that some extra mangling is needed to - /// avoid conflicts. Note that this may eventually go away entirely if - /// ThinLTO enables us to *always* have a globally shared instance of a - /// function within one crate's compilation. - may_conflict: bool, - }, - - /// Each codegen unit containing a reference to the given TransItem will - /// have its own private copy of the function (with internal linkage). - LocalCopy, -} - -pub trait TransItemExt<'a, 'tcx>: fmt::Debug { - fn as_trans_item(&self) -> &TransItem<'tcx>; +pub use rustc_trans_utils::trans_item::*; +pub use rustc_trans_utils::trans_item::TransItemExt as BaseTransItemExt; +pub trait TransItemExt<'a, 'tcx>: fmt::Debug + BaseTransItemExt<'a, 'tcx> { fn define(&self, ccx: &CrateContext<'a, 'tcx>) { debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", self.to_string(ccx.tcx()), @@ -165,53 +136,6 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { }.map(|node_id| tcx.hir.span(node_id)) } - fn instantiation_mode(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> InstantiationMode { - let inline_in_all_cgus = - tcx.sess.opts.debugging_opts.inline_in_all_cgus.unwrap_or_else(|| { - tcx.sess.opts.optimize != OptLevel::No - }); - - match *self.as_trans_item() { - TransItem::Fn(ref instance) => { - // If this function isn't inlined or otherwise has explicit - // linkage, then we'll be creating a globally shared version. - if self.explicit_linkage(tcx).is_some() || - !common::requests_inline(tcx, instance) - { - return InstantiationMode::GloballyShared { may_conflict: false } - } - - // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU - if inline_in_all_cgus { - return InstantiationMode::LocalCopy - } - - // Finally, if this is `#[inline(always)]` we're sure to respect - // that with an inline copy per CGU, but otherwise we'll be - // creating one copy of this `#[inline]` function which may - // conflict with upstream crates as it could be an exported - // symbol. - let attrs = instance.def.attrs(tcx); - match attr::find_inline_attr(Some(tcx.sess.diagnostic()), &attrs) { - InlineAttr::Always => InstantiationMode::LocalCopy, - _ => { - InstantiationMode::GloballyShared { may_conflict: true } - } - } - } - TransItem::Static(..) => { - InstantiationMode::GloballyShared { may_conflict: false } - } - TransItem::GlobalAsm(..) => { - InstantiationMode::GloballyShared { may_conflict: false } - } - } - } - fn is_generic_fn(&self) -> bool { match *self.as_trans_item() { TransItem::Fn(ref instance) => { @@ -222,97 +146,6 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { } } - fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { - let def_id = match *self.as_trans_item() { - TransItem::Fn(ref instance) => instance.def_id(), - TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), - TransItem::GlobalAsm(..) => return None, - }; - - let attributes = tcx.get_attrs(def_id); - if let Some(name) = attr::first_attr_value_str_by_name(&attributes, "linkage") { - if let Some(linkage) = base::linkage_by_name(&name.as_str()) { - Some(linkage) - } else { - let span = tcx.hir.span_if_local(def_id); - if let Some(span) = span { - tcx.sess.span_fatal(span, "invalid linkage specified") - } else { - tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) - } - } - } else { - None - } - } - - /// Returns whether this instance is instantiable - whether it has no unsatisfied - /// predicates. - /// - /// In order to translate an item, all of its predicates must hold, because - /// otherwise the item does not make sense. Type-checking ensures that - /// the predicates of every item that is *used by* a valid item *do* - /// hold, so we can rely on that. - /// - /// However, we translate collector roots (reachable items) and functions - /// in vtables when they are seen, even if they are not used, and so they - /// might not be instantiable. For example, a programmer can define this - /// public function: - /// - /// pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone { - /// <&mut () as Clone>::clone(&s); - /// } - /// - /// That function can't be translated, because the method `<&mut () as Clone>::clone` - /// does not exist. Luckily for us, that function can't ever be used, - /// because that would require for `&'a mut (): Clone` to hold, so we - /// can just not emit any code, or even a linker reference for it. - /// - /// Similarly, if a vtable method has such a signature, and therefore can't - /// be used, we can just not emit it and have a placeholder (a null pointer, - /// which will never be accessed) in its place. - fn is_instantiable(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { - debug!("is_instantiable({:?})", self); - let (def_id, substs) = match *self.as_trans_item() { - TransItem::Fn(ref instance) => (instance.def_id(), instance.substs), - TransItem::Static(node_id) => (tcx.hir.local_def_id(node_id), Substs::empty()), - // global asm never has predicates - TransItem::GlobalAsm(..) => return true - }; - - let predicates = tcx.predicates_of(def_id).predicates.subst(tcx, substs); - traits::normalize_and_test_predicates(tcx, predicates) - } - - fn to_string(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { - let hir_map = &tcx.hir; - - return match *self.as_trans_item() { - TransItem::Fn(instance) => { - to_string_internal(tcx, "fn ", instance) - }, - TransItem::Static(node_id) => { - let def_id = hir_map.local_def_id(node_id); - let instance = Instance::new(def_id, tcx.intern_substs(&[])); - to_string_internal(tcx, "static ", instance) - }, - TransItem::GlobalAsm(..) => { - "global_asm".to_string() - } - }; - - fn to_string_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - prefix: &str, - instance: Instance<'tcx>) - -> String { - let mut result = String::with_capacity(32); - result.push_str(prefix); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_instance_as_string(instance, &mut result); - result - } - } - fn to_raw_string(&self) -> String { match *self.as_trans_item() { TransItem::Fn(instance) => { @@ -330,11 +163,7 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { } } -impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> { - fn as_trans_item(&self) -> &TransItem<'tcx> { - self - } -} +impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> {} fn predefine_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, node_id: ast::NodeId, @@ -402,235 +231,3 @@ fn predefine_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.instances().borrow_mut().insert(instance, lldecl); } -//=----------------------------------------------------------------------------- -// TransItem String Keys -//=----------------------------------------------------------------------------- - -// The code below allows for producing a unique string key for a trans item. -// These keys are used by the handwritten auto-tests, so they need to be -// predictable and human-readable. -// -// Note: A lot of this could looks very similar to what's already in the -// ppaux module. It would be good to refactor things so we only have one -// parameterizable implementation for printing types. - -/// Same as `unique_type_name()` but with the result pushed onto the given -/// `output` parameter. -pub struct DefPathBasedNames<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool, -} - -impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool) - -> Self { - DefPathBasedNames { - tcx, - omit_disambiguators, - omit_local_crate_name, - } - } - - pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String) { - match t.sty { - ty::TyBool => output.push_str("bool"), - ty::TyChar => output.push_str("char"), - ty::TyStr => output.push_str("str"), - ty::TyNever => output.push_str("!"), - ty::TyInt(ast::IntTy::Is) => output.push_str("isize"), - ty::TyInt(ast::IntTy::I8) => output.push_str("i8"), - ty::TyInt(ast::IntTy::I16) => output.push_str("i16"), - ty::TyInt(ast::IntTy::I32) => output.push_str("i32"), - ty::TyInt(ast::IntTy::I64) => output.push_str("i64"), - ty::TyInt(ast::IntTy::I128) => output.push_str("i128"), - ty::TyUint(ast::UintTy::Us) => output.push_str("usize"), - ty::TyUint(ast::UintTy::U8) => output.push_str("u8"), - ty::TyUint(ast::UintTy::U16) => output.push_str("u16"), - ty::TyUint(ast::UintTy::U32) => output.push_str("u32"), - ty::TyUint(ast::UintTy::U64) => output.push_str("u64"), - ty::TyUint(ast::UintTy::U128) => output.push_str("u128"), - ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"), - ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"), - ty::TyAdt(adt_def, substs) => { - self.push_def_path(adt_def.did, output); - self.push_type_params(substs, iter::empty(), output); - }, - ty::TyTuple(component_types, _) => { - output.push('('); - for &component_type in component_types { - self.push_type_name(component_type, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { - output.push('*'); - match mutbl { - hir::MutImmutable => output.push_str("const "), - hir::MutMutable => output.push_str("mut "), - } - - self.push_type_name(inner_type, output); - }, - ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { - output.push('&'); - if mutbl == hir::MutMutable { - output.push_str("mut "); - } - - self.push_type_name(inner_type, output); - }, - ty::TyArray(inner_type, len) => { - output.push('['); - self.push_type_name(inner_type, output); - write!(output, "; {}", - len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); - output.push(']'); - }, - ty::TySlice(inner_type) => { - output.push('['); - self.push_type_name(inner_type, output); - output.push(']'); - }, - ty::TyDynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - self.push_def_path(principal.def_id(), output); - self.push_type_params(principal.skip_binder().substs, - trait_data.projection_bounds(), - output); - } - }, - ty::TyFnDef(..) | - ty::TyFnPtr(_) => { - let sig = t.fn_sig(self.tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } - - let abi = sig.abi(); - if abi != ::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); - - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - self.push_type_name(parameter_type, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_nil() { - output.push_str(" -> "); - self.push_type_name(sig.output(), output); - } - }, - ty::TyGenerator(def_id, ref closure_substs, _) | - ty::TyClosure(def_id, ref closure_substs) => { - self.push_def_path(def_id, output); - let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); - let substs = closure_substs.substs.truncate_to(self.tcx, generics); - self.push_type_params(substs, iter::empty(), output); - } - ty::TyError | - ty::TyInfer(_) | - ty::TyProjection(..) | - ty::TyParam(_) | - ty::TyAnon(..) => { - bug!("DefPathBasedNames: Trying to create type name for \ - unexpected type: {:?}", t); - } - } - } - - pub fn push_def_path(&self, - def_id: DefId, - output: &mut String) { - let def_path = self.tcx.def_path(def_id); - - // some_crate:: - if !(self.omit_local_crate_name && def_id.is_local()) { - output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); - output.push_str("::"); - } - - // foo::bar::ItemName:: - for part in self.tcx.def_path(def_id).data { - if self.omit_disambiguators { - write!(output, "{}::", part.data.as_interned_str()).unwrap(); - } else { - write!(output, "{}[{}]::", - part.data.as_interned_str(), - part.disambiguator).unwrap(); - } - } - - // remove final "::" - output.pop(); - output.pop(); - } - - fn push_type_params(&self, - substs: &Substs<'tcx>, - projections: I, - output: &mut String) - where I: Iterator> - { - let mut projections = projections.peekable(); - if substs.types().next().is_none() && projections.peek().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - self.push_type_name(type_parameter, output); - output.push_str(", "); - } - - for projection in projections { - let projection = projection.skip_binder(); - let name = &self.tcx.associated_item(projection.item_def_id).name.as_str(); - output.push_str(name); - output.push_str("="); - self.push_type_name(projection.ty, output); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } - - pub fn push_instance_as_string(&self, - instance: Instance<'tcx>, - output: &mut String) { - self.push_def_path(instance.def_id(), output); - self.push_type_params(instance.substs, iter::empty(), output); - } -} diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml index bedbea00688..7d9d7cea933 100644 --- a/src/librustc_trans_utils/Cargo.toml +++ b/src/librustc_trans_utils/Cargo.toml @@ -19,3 +19,4 @@ syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } +rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans_utils/collector.rs similarity index 99% rename from src/librustc_trans/collector.rs rename to src/librustc_trans_utils/collector.rs index 33a2e96ee66..c87d86262ef 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans_utils/collector.rs @@ -195,6 +195,7 @@ use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; use rustc::middle::lang_items::{ExchangeMallocFnLangItem}; +use rustc::middle::trans::TransItem; use rustc::traits; use rustc::ty::subst::Substs; use rustc::ty::{self, TypeFoldable, Ty, TyCtxt}; @@ -206,7 +207,7 @@ use common::{def_ty, instance_ty, type_is_sized}; use monomorphize::{self, Instance}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; -use trans_item::{TransItem, TransItemExt, DefPathBasedNames, InstantiationMode}; +use trans_item::{TransItemExt, DefPathBasedNames, InstantiationMode}; use rustc_data_structures::bitvec::BitVector; diff --git a/src/librustc_trans_utils/common.rs b/src/librustc_trans_utils/common.rs new file mode 100644 index 00000000000..634e37220e2 --- /dev/null +++ b/src/librustc_trans_utils/common.rs @@ -0,0 +1,78 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types, non_snake_case)] + +//! Code that is useful in various trans modules. + +use rustc::hir::def_id::DefId; +use rustc::hir::map::DefPathData; +use rustc::traits; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::Substs; + +use syntax::attr; +use syntax_pos::DUMMY_SP; + +pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_sized(tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP) +} + +pub fn requests_inline<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + if is_inline_instance(tcx, instance) { + return true + } + if let ty::InstanceDef::DropGlue(..) = instance.def { + // Drop glue wants to be instantiated at every translation + // unit, but without an #[inline] hint. We should make this + // available to normal end-users. + return true + } + attr::requests_inline(&instance.def.attrs(tcx)[..]) +} + +pub fn is_inline_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, + _ => return true + }; + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => true, + _ => false + } +} + +/// Given a DefId and some Substs, produces the monomorphic item type. +pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> Ty<'tcx> +{ + let ty = tcx.type_of(def_id); + tcx.trans_apply_param_substs(substs, &ty) +} + +/// Return the substituted type of an instance. +pub fn instance_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx>) + -> Ty<'tcx> +{ + let ty = instance.def.def_ty(tcx); + tcx.trans_apply_param_substs(instance.substs, &ty) +} diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 6873befd2bf..bc8f8d5a6da 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -38,6 +38,7 @@ extern crate log; #[macro_use] extern crate rustc; extern crate rustc_back; +extern crate rustc_data_structures; extern crate syntax; extern crate syntax_pos; @@ -49,7 +50,11 @@ use rustc::util::nodemap::NodeSet; use syntax::attr; +mod common; pub mod link; +pub mod collector; +pub mod trans_item; +pub mod monomorphize; pub mod trans_crate; /// check for the #[rustc_error] annotation, which forces an diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans_utils/monomorphize.rs similarity index 100% rename from src/librustc_trans/monomorphize.rs rename to src/librustc_trans_utils/monomorphize.rs diff --git a/src/librustc_trans_utils/trans_item.rs b/src/librustc_trans_utils/trans_item.rs new file mode 100644 index 00000000000..0ada39d7d27 --- /dev/null +++ b/src/librustc_trans_utils/trans_item.rs @@ -0,0 +1,464 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use common; +use monomorphize::Instance; +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::middle::trans::Linkage; +use rustc::session::config::OptLevel; +use rustc::traits; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::{Subst, Substs}; +use syntax::ast; +use syntax::attr::{self, InlineAttr}; +use std::fmt::{self, Write}; +use std::iter; + +pub use rustc::middle::trans::TransItem; + +pub fn linkage_by_name(name: &str) -> Option { + use rustc::middle::trans::Linkage::*; + + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but whitelist them anyway and trust that + // the user knows what s/he's doing. Who knows, unanticipated use cases + // may pop up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Some(Appending), + "available_externally" => Some(AvailableExternally), + "common" => Some(Common), + "extern_weak" => Some(ExternalWeak), + "external" => Some(External), + "internal" => Some(Internal), + "linkonce" => Some(LinkOnceAny), + "linkonce_odr" => Some(LinkOnceODR), + "private" => Some(Private), + "weak" => Some(WeakAny), + "weak_odr" => Some(WeakODR), + _ => None, + } +} + +/// Describes how a translation item will be instantiated in object files. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub enum InstantiationMode { + /// There will be exactly one instance of the given TransItem. It will have + /// external linkage so that it can be linked to from other codegen units. + GloballyShared { + /// In some compilation scenarios we may decide to take functions that + /// are typically `LocalCopy` and instead move them to `GloballyShared` + /// to avoid translating them a bunch of times. In this situation, + /// however, our local copy may conflict with other crates also + /// inlining the same function. + /// + /// This flag indicates that this situation is occuring, and informs + /// symbol name calculation that some extra mangling is needed to + /// avoid conflicts. Note that this may eventually go away entirely if + /// ThinLTO enables us to *always* have a globally shared instance of a + /// function within one crate's compilation. + may_conflict: bool, + }, + + /// Each codegen unit containing a reference to the given TransItem will + /// have its own private copy of the function (with internal linkage). + LocalCopy, +} + +pub trait TransItemExt<'a, 'tcx>: fmt::Debug { + fn as_trans_item(&self) -> &TransItem<'tcx>; + + fn instantiation_mode(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> InstantiationMode { + let inline_in_all_cgus = + tcx.sess.opts.debugging_opts.inline_in_all_cgus.unwrap_or_else(|| { + tcx.sess.opts.optimize != OptLevel::No + }); + + match *self.as_trans_item() { + TransItem::Fn(ref instance) => { + // If this function isn't inlined or otherwise has explicit + // linkage, then we'll be creating a globally shared version. + if self.explicit_linkage(tcx).is_some() || + !common::requests_inline(tcx, instance) + { + return InstantiationMode::GloballyShared { may_conflict: false } + } + + // At this point we don't have explicit linkage and we're an + // inlined function. If we're inlining into all CGUs then we'll + // be creating a local copy per CGU + if inline_in_all_cgus { + return InstantiationMode::LocalCopy + } + + // Finally, if this is `#[inline(always)]` we're sure to respect + // that with an inline copy per CGU, but otherwise we'll be + // creating one copy of this `#[inline]` function which may + // conflict with upstream crates as it could be an exported + // symbol. + let attrs = instance.def.attrs(tcx); + match attr::find_inline_attr(Some(tcx.sess.diagnostic()), &attrs) { + InlineAttr::Always => InstantiationMode::LocalCopy, + _ => { + InstantiationMode::GloballyShared { may_conflict: true } + } + } + } + TransItem::Static(..) => { + InstantiationMode::GloballyShared { may_conflict: false } + } + TransItem::GlobalAsm(..) => { + InstantiationMode::GloballyShared { may_conflict: false } + } + } + } + + fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { + let def_id = match *self.as_trans_item() { + TransItem::Fn(ref instance) => instance.def_id(), + TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), + TransItem::GlobalAsm(..) => return None, + }; + + let attributes = tcx.get_attrs(def_id); + if let Some(name) = attr::first_attr_value_str_by_name(&attributes, "linkage") { + if let Some(linkage) = linkage_by_name(&name.as_str()) { + Some(linkage) + } else { + let span = tcx.hir.span_if_local(def_id); + if let Some(span) = span { + tcx.sess.span_fatal(span, "invalid linkage specified") + } else { + tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) + } + } + } else { + None + } + } + + /// Returns whether this instance is instantiable - whether it has no unsatisfied + /// predicates. + /// + /// In order to translate an item, all of its predicates must hold, because + /// otherwise the item does not make sense. Type-checking ensures that + /// the predicates of every item that is *used by* a valid item *do* + /// hold, so we can rely on that. + /// + /// However, we translate collector roots (reachable items) and functions + /// in vtables when they are seen, even if they are not used, and so they + /// might not be instantiable. For example, a programmer can define this + /// public function: + /// + /// pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone { + /// <&mut () as Clone>::clone(&s); + /// } + /// + /// That function can't be translated, because the method `<&mut () as Clone>::clone` + /// does not exist. Luckily for us, that function can't ever be used, + /// because that would require for `&'a mut (): Clone` to hold, so we + /// can just not emit any code, or even a linker reference for it. + /// + /// Similarly, if a vtable method has such a signature, and therefore can't + /// be used, we can just not emit it and have a placeholder (a null pointer, + /// which will never be accessed) in its place. + fn is_instantiable(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { + debug!("is_instantiable({:?})", self); + let (def_id, substs) = match *self.as_trans_item() { + TransItem::Fn(ref instance) => (instance.def_id(), instance.substs), + TransItem::Static(node_id) => (tcx.hir.local_def_id(node_id), Substs::empty()), + // global asm never has predicates + TransItem::GlobalAsm(..) => return true + }; + + let predicates = tcx.predicates_of(def_id).predicates.subst(tcx, substs); + traits::normalize_and_test_predicates(tcx, predicates) + } + + fn to_string(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { + let hir_map = &tcx.hir; + + return match *self.as_trans_item() { + TransItem::Fn(instance) => { + to_string_internal(tcx, "fn ", instance) + }, + TransItem::Static(node_id) => { + let def_id = hir_map.local_def_id(node_id); + let instance = Instance::new(def_id, tcx.intern_substs(&[])); + to_string_internal(tcx, "static ", instance) + }, + TransItem::GlobalAsm(..) => { + "global_asm".to_string() + } + }; + + fn to_string_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + prefix: &str, + instance: Instance<'tcx>) + -> String { + let mut result = String::with_capacity(32); + result.push_str(prefix); + let printer = DefPathBasedNames::new(tcx, false, false); + printer.push_instance_as_string(instance, &mut result); + result + } + } +} + +impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> { + fn as_trans_item(&self) -> &TransItem<'tcx> { + self + } +} + +//=----------------------------------------------------------------------------- +// TransItem String Keys +//=----------------------------------------------------------------------------- + +// The code below allows for producing a unique string key for a trans item. +// These keys are used by the handwritten auto-tests, so they need to be +// predictable and human-readable. +// +// Note: A lot of this could looks very similar to what's already in the +// ppaux module. It would be good to refactor things so we only have one +// parameterizable implementation for printing types. + +/// Same as `unique_type_name()` but with the result pushed onto the given +/// `output` parameter. +pub struct DefPathBasedNames<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + omit_disambiguators: bool, + omit_local_crate_name: bool, +} + +impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + omit_disambiguators: bool, + omit_local_crate_name: bool) + -> Self { + DefPathBasedNames { + tcx, + omit_disambiguators, + omit_local_crate_name, + } + } + + pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String) { + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyNever => output.push_str("!"), + ty::TyInt(ast::IntTy::Is) => output.push_str("isize"), + ty::TyInt(ast::IntTy::I8) => output.push_str("i8"), + ty::TyInt(ast::IntTy::I16) => output.push_str("i16"), + ty::TyInt(ast::IntTy::I32) => output.push_str("i32"), + ty::TyInt(ast::IntTy::I64) => output.push_str("i64"), + ty::TyInt(ast::IntTy::I128) => output.push_str("i128"), + ty::TyUint(ast::UintTy::Us) => output.push_str("usize"), + ty::TyUint(ast::UintTy::U8) => output.push_str("u8"), + ty::TyUint(ast::UintTy::U16) => output.push_str("u16"), + ty::TyUint(ast::UintTy::U32) => output.push_str("u32"), + ty::TyUint(ast::UintTy::U64) => output.push_str("u64"), + ty::TyUint(ast::UintTy::U128) => output.push_str("u128"), + ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"), + ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"), + ty::TyAdt(adt_def, substs) => { + self.push_def_path(adt_def.did, output); + self.push_type_params(substs, iter::empty(), output); + }, + ty::TyTuple(component_types, _) => { + output.push('('); + for &component_type in component_types { + self.push_type_name(component_type, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + self.push_type_name(inner_type, output); + }, + ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + self.push_type_name(inner_type, output); + }, + ty::TyArray(inner_type, len) => { + output.push('['); + self.push_type_name(inner_type, output); + write!(output, "; {}", + len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); + output.push(']'); + }, + ty::TySlice(inner_type) => { + output.push('['); + self.push_type_name(inner_type, output); + output.push(']'); + }, + ty::TyDynamic(ref trait_data, ..) => { + if let Some(principal) = trait_data.principal() { + self.push_def_path(principal.def_id(), output); + self.push_type_params(principal.skip_binder().substs, + trait_data.projection_bounds(), + output); + } + }, + ty::TyFnDef(..) | + ty::TyFnPtr(_) => { + let sig = t.fn_sig(self.tcx); + if sig.unsafety() == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + let abi = sig.abi(); + if abi != ::syntax::abi::Abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); + + if !sig.inputs().is_empty() { + for ¶meter_type in sig.inputs() { + self.push_type_name(parameter_type, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs().is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + if !sig.output().is_nil() { + output.push_str(" -> "); + self.push_type_name(sig.output(), output); + } + }, + ty::TyGenerator(def_id, ref closure_substs, _) | + ty::TyClosure(def_id, ref closure_substs) => { + self.push_def_path(def_id, output); + let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); + let substs = closure_substs.substs.truncate_to(self.tcx, generics); + self.push_type_params(substs, iter::empty(), output); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyParam(_) | + ty::TyAnon(..) => { + bug!("DefPathBasedNames: Trying to create type name for \ + unexpected type: {:?}", t); + } + } + } + + pub fn push_def_path(&self, + def_id: DefId, + output: &mut String) { + let def_path = self.tcx.def_path(def_id); + + // some_crate:: + if !(self.omit_local_crate_name && def_id.is_local()) { + output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); + output.push_str("::"); + } + + // foo::bar::ItemName:: + for part in self.tcx.def_path(def_id).data { + if self.omit_disambiguators { + write!(output, "{}::", part.data.as_interned_str()).unwrap(); + } else { + write!(output, "{}[{}]::", + part.data.as_interned_str(), + part.disambiguator).unwrap(); + } + } + + // remove final "::" + output.pop(); + output.pop(); + } + + fn push_type_params(&self, + substs: &Substs<'tcx>, + projections: I, + output: &mut String) + where I: Iterator> + { + let mut projections = projections.peekable(); + if substs.types().next().is_none() && projections.peek().is_none() { + return; + } + + output.push('<'); + + for type_parameter in substs.types() { + self.push_type_name(type_parameter, output); + output.push_str(", "); + } + + for projection in projections { + let projection = projection.skip_binder(); + let name = &self.tcx.associated_item(projection.item_def_id).name.as_str(); + output.push_str(name); + output.push_str("="); + self.push_type_name(projection.ty, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } + + pub fn push_instance_as_string(&self, + instance: Instance<'tcx>, + output: &mut String) { + self.push_def_path(instance.def_id(), output); + self.push_type_params(instance.substs, iter::empty(), output); + } +}