about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/trans/base.rs56
-rw-r--r--src/librustc/middle/trans/builder.rs1
-rw-r--r--src/librustc/middle/trans/consts.rs9
-rw-r--r--src/librustc/middle/trans/context.rs36
-rw-r--r--src/librustc/middle/trans/expr.rs19
-rw-r--r--src/librustc/middle/trans/foreign.rs12
-rw-r--r--src/librustc/middle/trans/inline.rs65
-rw-r--r--src/librustc/middle/trans/meth.rs1
8 files changed, 149 insertions, 50 deletions
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<u8> {
+pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
     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<Option<ValueRef>>,
 
     intrinsics: RefCell<HashMap<&'static str, ValueRef>>,
+
+    /// 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<uint>,
 }
 
 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<HashMap<&'static str, ValueRef>> {
         &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<ValueRef> {
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 {
+            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, _) => {
-                    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);
+                    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)
                                  &param_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,
                              &param_substs::empty(),
                              method.id,
                              []);
+                    update_linkage(ccx, llfn, method.id);
                 }
                 let mut v = TransItemVisitor {
                     ccx: ccx,