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:
parent
2e7bc0f808
commit
da9606247d
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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) => {
|
||||
|
@ -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_*`.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user