}
}
+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() {
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
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);
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") {
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.
None => {}
}
- let mut foreign = false;
let item = ccx.tcx().map.get(id);
let val = match item {
ast_map::NodeItem(i) => {
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(
}
ast_map::NodeForeignItem(ni) => {
- foreign = true;
-
match ni.node {
ast::ForeignItemFn(..) => {
let abi = ccx.tcx().map.get_foreign_abi(id);
}
};
- // 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
}
}
-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(),
}
}
-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| {
link_meta.clone(),
reachable);
- let metadata = {
+ {
let ccx = shared_ccx.get_ccx(0);
// First, verify intrinsics.
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();
.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();
// 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);
}
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> {
}
}
+ 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
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));
}
+ /// 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
}
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
}
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> {
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
}
}
-> 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.
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) => {
}
};
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| {
}
}
None => unsafe {
+ // Generate an external declaration.
ident.get().with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf)
})
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_*`.
}
}
}
// 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};
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)
¶m_substs::empty(),
mth.id,
[]);
+ // Use InternalLinkage so LLVM can optimize more
+ // aggressively.
+ SetLinkage(llfn, InternalLinkage);
}
local_def(mth.id)
}
¶m_substs::empty(),
method.id,
[]);
+ update_linkage(ccx, llfn, method.id);
}
let mut v = TransItemVisitor {
ccx: ccx,