translate into multiple llvm contexts

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.
This commit is contained in:
Stuart Pernsteiner 2014-07-21 16:42:34 -07:00
parent 2e7bc0f808
commit da9606247d
8 changed files with 150 additions and 51 deletions

View File

@ -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();

View File

@ -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();

View 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);
}

View 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 @@ 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> {

View 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 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) => {

View 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_*`.
}
}
}

View 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 {
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)
&param_substs::empty(),
mth.id,
[]);
// Use InternalLinkage so LLVM can optimize more
// aggressively.
SetLinkage(llfn, InternalLinkage);
}
local_def(mth.id)
}

View 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,