]> git.lizzy.rs Git - rust.git/commitdiff
translate into multiple llvm contexts
authorStuart Pernsteiner <spernsteiner@mozilla.com>
Mon, 21 Jul 2014 23:42:34 +0000 (16:42 -0700)
committerStuart Pernsteiner <spernsteiner@mozilla.com>
Fri, 5 Sep 2014 16:18:57 +0000 (09:18 -0700)
Rotate between compilation units while translating.  The "worker threads"
commit added support for multiple compilation units, but only translated into
one, leaving the rest empty.  With this commit, `trans` rotates between various
compilation units while translating, using a simple stragtegy: upon entering a
module, switch to translating into whichever compilation unit currently
contains the fewest LLVM instructions.

Most of the actual changes here involve getting symbol linkage right, so that
items translated into different compilation units will link together properly
at the end.

src/librustc/middle/trans/base.rs
src/librustc/middle/trans/builder.rs
src/librustc/middle/trans/consts.rs
src/librustc/middle/trans/context.rs
src/librustc/middle/trans/expr.rs
src/librustc/middle/trans/foreign.rs
src/librustc/middle/trans/inline.rs
src/librustc/middle/trans/meth.rs

index c1b92f71878f14f565c1d98a225ec3f832c4b966..81f76a82a546c04df32669a06cfab315887a98fe 100644 (file)
@@ -2124,8 +2124,17 @@ fn visit_item(&mut self, i: &ast::Item, _:()) {
     }
 }
 
+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();
index 61a79f4a8eeba54175f8a8f6a2faa1fdd5429cbf..322a6a3cc909eb1272a1d29e4618926b046c5388 100644 (file)
@@ -50,6 +50,7 @@ pub fn count_insn(&self, category: &str) {
                                                 .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();
index 2571ae1fe72e1bd65fd2a9e14530fa7babc968f4..bd5132ea4273660ea8f128ecb07e76b8c1957b5d 100644 (file)
@@ -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);
         }
index a25070e89ee059d958ef5a45ace098e9ef784679..4184c49b9050863f94ee9b855e54556028557106 100644 (file)
@@ -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 @@ pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a> {
         }
     }
 
+    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 @@ fn new(shared: &SharedCrateContext,
                 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 @@ pub fn local(&self) -> &'b LocalCrateContext {
     }
 
 
+    /// 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 @@ pub fn llcx(&self) -> ContextRef {
         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 @@ pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
     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> {
index fcb0ac9d9306f2b98a2814cabe62378aadc80677..61c27292a3767c33c02b3d2840d734e1da93bd54 100644 (file)
@@ -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 get_val<'a>(bcx: &'a Block<'a>, did: ast::DefId, const_ty: ty::t)
                        -> 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 get_val<'a>(bcx: &'a Block<'a>, did: ast::DefId, const_ty: ty::t)
                     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) => {
index 240c109e17bd32ee49d0226721c6f1297a8e1008..8ed45f89c29e48dcc00577dcc1bbd085f6e64355 100644 (file)
@@ -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_*`.
                     }
                 }
             }
index e8a56f4a9265d29ee3494aa670d1b658f20e9d2e..0713b2b535c07d077011adee70dc50a89332e36e 100644 (file)
@@ -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)
                 }
index 9c587d08f01c1a1b62d2cdefe2beebb573c3be9b..92d8db0e4eafaff0d250302a5753c3fefb8c516d 100644 (file)
@@ -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,