diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index c1b92f71878..81f76a82a54 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2124,8 +2124,17 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> { } } +pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: ast::NodeId) { + if ccx.reachable().contains(&id) || ccx.sess().opts.cg.codegen_units > 1 { + llvm::SetLinkage(llval, llvm::ExternalLinkage); + } else { + llvm::SetLinkage(llval, llvm::InternalLinkage); + } +} + pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { let _icx = push_ctxt("trans_item"); + match item.node { ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => { if !generics.is_type_parameterized() { @@ -2148,6 +2157,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { item.id, item.attrs.as_slice()); } + update_linkage(ccx, llfn, item.id); } // Be sure to travel more than just one layer deep to catch nested @@ -2163,7 +2173,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { item.id); } ast::ItemMod(ref m) => { - trans_mod(ccx, m); + trans_mod(&ccx.rotate(), m); } ast::ItemEnum(ref enum_definition, _) => { enum_variant_size_lint(ccx, enum_definition, item.span, item.id); @@ -2173,6 +2183,10 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { let mut v = TransItemVisitor{ ccx: ccx }; v.visit_expr(&**expr, ()); consts::trans_const(ccx, m, item.id); + + let g = get_item_val(ccx, item.id); + update_linkage(ccx, g, item.id); + // Do static_assert checking. It can't really be done much earlier // because we need to get the value of the bool out of LLVM if attr::contains_name(item.attrs.as_slice(), "static_assert") { @@ -2221,10 +2235,6 @@ fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::N llfn: ValueRef) { ccx.item_symbols().borrow_mut().insert(node_id, sym); - if !ccx.reachable().contains(&node_id) { - llvm::SetLinkage(llfn, llvm::InternalLinkage); - } - // The stack exhaustion lang item shouldn't have a split stack because // otherwise it would continue to be exhausted (bad), and both it and the // eh_personality functions need to be externally linkable. @@ -2592,7 +2602,6 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { None => {} } - let mut foreign = false; let item = ccx.tcx().map.get(id); let val = match item { ast_map::NodeItem(i) => { @@ -2620,10 +2629,6 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { llvm::LLVMAddGlobal(ccx.llmod(), llty, buf) }); - if !ccx.reachable().contains(&id) { - llvm::SetLinkage(g, llvm::InternalLinkage); - } - // Apply the `unnamed_addr` attribute if // requested if !ast_util::static_has_significant_address( @@ -2714,8 +2719,6 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { } ast_map::NodeForeignItem(ni) => { - foreign = true; - match ni.node { ast::ForeignItemFn(..) => { let abi = ccx.tcx().map.get_foreign_abi(id); @@ -2787,12 +2790,14 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { } }; - // foreign items (extern fns and extern statics) don't have internal - // linkage b/c that doesn't quite make sense. Otherwise items can - // have internal linkage if they're not reachable. - if !foreign && !ccx.reachable().contains(&id) { - llvm::SetLinkage(val, llvm::InternalLinkage); - } + // All LLVM globals and functions are initially created as external-linkage + // declarations. If `trans_item`/`trans_fn` later turns the declaration + // into a definition, it adjusts the linkage then (using `update_linkage`). + // + // The exception is foreign items, which have their linkage set inside the + // call to `foreign::register_*` above. We don't touch the linkage after + // that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the + // other item translation functions do). ccx.item_vals().borrow_mut().insert(id, val); val @@ -2815,7 +2820,8 @@ pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef { } } -pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeInlinedItem<'r>) +pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r SharedCrateContext, + ie: encoder::EncodeInlinedItem<'r>) -> encoder::EncodeParams<'r> { encoder::EncodeParams { diag: cx.sess().diagnostic(), @@ -2830,7 +2836,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI } } -pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec { +pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { use flate; let any_library = cx.sess().crate_types.borrow().iter().any(|ty| { @@ -2905,7 +2911,7 @@ pub fn trans_crate(krate: ast::Crate, link_meta.clone(), reachable); - let metadata = { + { let ccx = shared_ccx.get_ccx(0); // First, verify intrinsics. @@ -2916,15 +2922,17 @@ pub fn trans_crate(krate: ast::Crate, let _icx = push_ctxt("text"); trans_mod(&ccx, &krate.module); } + } + for ccx in shared_ccx.iter() { glue::emit_tydescs(&ccx); if ccx.sess().opts.debuginfo != NoDebugInfo { debuginfo::finalize(&ccx); } + } - // Translate the metadata. - write_metadata(&ccx, &krate) - }; + // Translate the metadata. + let metadata = write_metadata(&shared_ccx, &krate); if shared_ccx.sess().trans_stats() { let stats = shared_ccx.stats(); diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index 61a79f4a8ee..322a6a3cc90 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -50,6 +50,7 @@ impl<'a> Builder<'a> { .n_llvm_insns .get() + 1); } + self.ccx.count_llvm_insn(); if self.ccx.sess().count_llvm_insns() { base::with_insn_ctxt(|v| { let mut h = self.ccx.stats().llvm_insns.borrow_mut(); diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 2571ae1fe72..bd5132ea427 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -692,6 +692,15 @@ pub fn trans_const(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) { // constant's initializer to determine its LLVM type. let v = ccx.const_values().borrow().get_copy(&id); llvm::LLVMSetInitializer(g, v); + + // `get_item_val` left `g` with external linkage, but we just set an + // initializer for it. But we don't know yet if `g` should really be + // defined in this compilation unit, so we set its linkage to + // `AvailableExternallyLinkage`. (It's still a definition, but acts + // like a declaration for most purposes.) If `g` really should be + // declared here, then `trans_item` will fix up the linkage later on. + llvm::SetLinkage(g, llvm::AvailableExternallyLinkage); + if m != ast::MutMutable { llvm::LLVMSetGlobalConstant(g, True); } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index a25070e89ee..4184c49b905 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -141,6 +141,11 @@ pub struct LocalCrateContext { eh_personality: RefCell>, intrinsics: RefCell>, + + /// Number of LLVM instructions translated into this `LocalCrateContext`. + /// This is used to perform some basic load-balancing to keep all LLVM + /// contexts around the same size. + n_llvm_insns: Cell, } pub struct CrateContext<'a> { @@ -261,6 +266,18 @@ impl SharedCrateContext { } } + fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a> { + let local_ccx = + self.local_ccxs + .iter() + .min_by(|&local_ccx| local_ccx.n_llvm_insns.get()) + .unwrap(); + CrateContext { + shared: self, + local: local_ccx, + } + } + pub fn metadata_llmod(&self) -> ModuleRef { self.metadata_llmod @@ -364,6 +381,7 @@ impl LocalCrateContext { dbg_cx: dbg_cx, eh_personality: RefCell::new(None), intrinsics: RefCell::new(HashMap::new()), + n_llvm_insns: Cell::new(0u), }; local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared)); @@ -415,6 +433,12 @@ impl<'b> CrateContext<'b> { } + /// Get a (possibly) different `CrateContext` from the same + /// `SharedCrateContext`. + pub fn rotate(&self) -> CrateContext<'b> { + self.shared.get_smallest_ccx() + } + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt { &self.shared.tcx } @@ -467,14 +491,6 @@ impl<'b> CrateContext<'b> { self.local.llcx } - pub fn metadata_llmod(&self) -> ModuleRef { - self.shared.metadata_llmod - } - - pub fn metadata_llcx(&self) -> ContextRef { - self.shared.metadata_llcx - } - pub fn td<'a>(&'a self) -> &'a TargetData { &self.local.td } @@ -619,6 +635,10 @@ impl<'b> CrateContext<'b> { fn intrinsics<'a>(&'a self) -> &'a RefCell> { &self.local.intrinsics } + + pub fn count_llvm_insn(&self) { + self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1); + } } fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index fcb0ac9d930..61c27292a37 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -817,13 +817,28 @@ fn trans_def<'a>(bcx: &'a Block<'a>, trans_def_fn_unadjusted(bcx, ref_expr, def) } def::DefStatic(did, _) => { + // There are three things that may happen here: + // 1) If the static item is defined in this crate, it will be + // translated using `get_item_val`, and we return a pointer to + // the result. + // 2) If the static item is defined in another crate, but is + // marked inlineable, then it will be inlined into this crate + // and then translated with `get_item_val`. Again, we return a + // pointer to the result. + // 3) If the static item is defined in another crate and is not + // marked inlineable, then we add (or reuse) a declaration of + // an external global, and return a pointer to that. let const_ty = expr_ty(bcx, ref_expr); fn get_did(ccx: &CrateContext, did: ast::DefId) -> ast::DefId { if did.krate != ast::LOCAL_CRATE { + // Case 2 or 3. Which one we're in is determined by + // whether the DefId produced by `maybe_instantiate_inline` + // is in the LOCAL_CRATE or not. inline::maybe_instantiate_inline(ccx, did) } else { + // Case 1. did } } @@ -832,6 +847,9 @@ fn trans_def<'a>(bcx: &'a Block<'a>, -> ValueRef { // For external constants, we don't inline. if did.krate == ast::LOCAL_CRATE { + // Case 1 or 2. (The inlining in case 2 produces a new + // DefId in LOCAL_CRATE.) + // The LLVM global has the type of its initializer, // which may not be equal to the enum's type for // non-C-like enums. @@ -839,6 +857,7 @@ fn trans_def<'a>(bcx: &'a Block<'a>, let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); PointerCast(bcx, val, pty) } else { + // Case 3. match bcx.ccx().extern_const_values().borrow().find(&did) { None => {} // Continue. Some(llval) => { diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 240c109e17b..8ed45f89c29 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -159,11 +159,18 @@ pub fn register_static(ccx: &CrateContext, } }; unsafe { + // Declare a symbol `foo` with the desired linkage. let g1 = ident.get().with_c_str(|buf| { llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(), buf) }); llvm::SetLinkage(g1, linkage); + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. let mut real_name = "_rust_extern_with_linkage_".to_string(); real_name.push_str(ident.get()); let g2 = real_name.with_c_str(|buf| { @@ -175,6 +182,7 @@ pub fn register_static(ccx: &CrateContext, } } None => unsafe { + // Generate an external declaration. ident.get().with_c_str(|buf| { llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) }) @@ -490,6 +498,10 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { register_foreign_item_fn(ccx, abi, ty, lname.get().as_slice(), Some(foreign_item.span)); + // Unlike for other items, we shouldn't call + // `base::update_linkage` here. Foreign items have + // special linkage requirements, which are handled + // inside `foreign::register_*`. } } } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index e8a56f4a926..0713b2b535c 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::{AvailableExternallyLinkage, SetLinkage}; +use llvm::{AvailableExternallyLinkage, InternalLinkage, SetLinkage}; use metadata::csearch; use middle::astencode; use middle::trans::base::{push_ctxt, trans_item, get_item_val, trans_fn}; @@ -53,26 +53,52 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); trans_item(ccx, &*item); - // We're bringing an external global into this crate, but we don't - // want to create two copies of the global. If we do this, then if - // you take the address of the global in two separate crates you get - // two different addresses. This is bad for things like conditions, - // but it could possibly have other adverse side effects. We still - // want to achieve the optimizations related to this global, - // however, so we use the available_externally linkage which llvm - // provides - match item.node { - ast::ItemStatic(_, mutbl, _) => { - let g = get_item_val(ccx, item.id); - // see the comment in get_item_val() as to why this check is - // performed here. - if ast_util::static_has_significant_address( - mutbl, - item.attrs.as_slice()) { - SetLinkage(g, AvailableExternallyLinkage); + let linkage = match item.node { + ast::ItemFn(_, _, _, ref generics, _) => { + if generics.is_type_parameterized() { + // Generics have no symbol, so they can't be given any + // linkage. + None + } else { + if ccx.sess().opts.cg.codegen_units == 1 { + // We could use AvailableExternallyLinkage here, + // but InternalLinkage allows LLVM to optimize more + // aggressively (at the cost of sometimes + // duplicating code). + Some(InternalLinkage) + } else { + // With multiple compilation units, duplicated code + // is more of a problem. Also, `codegen_units > 1` + // means the user is okay with losing some + // performance. + Some(AvailableExternallyLinkage) + } } } - _ => {} + ast::ItemStatic(_, mutbl, _) => { + if !ast_util::static_has_significant_address(mutbl, item.attrs.as_slice()) { + // Inlined static items use internal linkage when + // possible, so that LLVM will coalesce globals with + // identical initializers. (It only does this for + // globals with unnamed_addr and either internal or + // private linkage.) + Some(InternalLinkage) + } else { + // The address is significant, so we can't create an + // internal copy of the static. (The copy would have a + // different address from the original.) + Some(AvailableExternallyLinkage) + } + } + _ => unreachable!(), + }; + + match linkage { + Some(linkage) => { + let g = get_item_val(ccx, item.id); + SetLinkage(g, linkage); + } + None => {} } local_def(item.id) @@ -147,6 +173,9 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) ¶m_substs::empty(), mth.id, []); + // Use InternalLinkage so LLVM can optimize more + // aggressively. + SetLinkage(llfn, InternalLinkage); } local_def(mth.id) } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 9c587d08f01..92d8db0e4ea 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -85,6 +85,7 @@ pub fn trans_impl(ccx: &CrateContext, ¶m_substs::empty(), method.id, []); + update_linkage(ccx, llfn, method.id); } let mut v = TransItemVisitor { ccx: ccx,