From cf35cb365a4efee53f6372095aaff4798544bf94 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 5 Sep 2014 09:18:53 -0700 Subject: [PATCH 01/14] make CrateContext fields private --- src/librustc/back/link.rs | 8 +- src/librustc/middle/trans/_match.rs | 2 +- src/librustc/middle/trans/adt.rs | 6 +- src/librustc/middle/trans/base.rs | 172 +++++++------- src/librustc/middle/trans/build.rs | 10 +- src/librustc/middle/trans/builder.rs | 34 +-- src/librustc/middle/trans/cabi_mips.rs | 2 +- src/librustc/middle/trans/cleanup.rs | 24 +- src/librustc/middle/trans/closure.rs | 16 +- src/librustc/middle/trans/common.rs | 34 +-- src/librustc/middle/trans/consts.rs | 20 +- src/librustc/middle/trans/context.rs | 262 ++++++++++++++++++---- src/librustc/middle/trans/datum.rs | 2 +- src/librustc/middle/trans/debuginfo.rs | 58 ++--- src/librustc/middle/trans/expr.rs | 24 +- src/librustc/middle/trans/foreign.rs | 71 +++--- src/librustc/middle/trans/glue.rs | 20 +- src/librustc/middle/trans/inline.rs | 28 +-- src/librustc/middle/trans/intrinsic.rs | 8 +- src/librustc/middle/trans/llrepr.rs | 4 +- src/librustc/middle/trans/machine.rs | 18 +- src/librustc/middle/trans/meth.rs | 20 +- src/librustc/middle/trans/monomorphize.rs | 20 +- src/librustc/middle/trans/reflect.rs | 2 +- src/librustc/middle/trans/tvec.rs | 6 +- src/librustc/middle/trans/type_.rs | 34 +-- src/librustc/middle/trans/type_of.rs | 16 +- 27 files changed, 544 insertions(+), 377 deletions(-) diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 4e4a28cc538ce..d9c05f9a76da9 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -715,14 +715,14 @@ fn symbol_hash(tcx: &ty::ctxt, } fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String { - match ccx.type_hashcodes.borrow().find(&t) { + match ccx.type_hashcodes().borrow().find(&t) { Some(h) => return h.to_string(), None => {} } - let mut symbol_hasher = ccx.symbol_hasher.borrow_mut(); - let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, &ccx.link_meta); - ccx.type_hashcodes.borrow_mut().insert(t, hash.clone()); + let mut symbol_hasher = ccx.symbol_hasher().borrow_mut(); + let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, ccx.link_meta()); + ccx.type_hashcodes().borrow_mut().insert(t, hash.clone()); hash } diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 040577e7048e1..1b93e1974419c 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -563,7 +563,7 @@ fn get_branches<'a>(bcx: &'a Block, m: &[Match], col: uint) -> Vec> { } ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => { // This is either an enum variant or a variable binding. - let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id); + let opt_def = ccx.tcx().def_map.borrow().find_copy(&cur.id); match opt_def { Some(def::DefVariant(enum_id, var_id, _)) => { let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id); diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 8a5581e592ae3..21673a67ea8ac 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -150,14 +150,14 @@ pub fn represent_node(bcx: &Block, node: ast::NodeId) -> Rc { /// Decides how to represent a given type. pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc { debug!("Representing: {}", ty_to_string(cx.tcx(), t)); - match cx.adt_reprs.borrow().find(&t) { + match cx.adt_reprs().borrow().find(&t) { Some(repr) => return repr.clone(), None => {} } let repr = Rc::new(represent_type_uncached(cx, t)); debug!("Represented as: {:?}", repr) - cx.adt_reprs.borrow_mut().insert(t, repr.clone()); + cx.adt_reprs().borrow_mut().insert(t, repr.clone()); repr } @@ -423,7 +423,7 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp attempts = choose_shortest; }, attr::ReprPacked => { - cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum"); + cx.tcx().sess.bug("range_to_inttype: found ReprPacked on an enum"); } } for &ity in attempts.iter() { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 76e2266fcb92f..78488654c510c 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -149,7 +149,7 @@ impl<'a> StatRecorder<'a> { } else { 0 }; - let istart = ccx.stats.n_llvm_insns.get(); + let istart = ccx.stats().n_llvm_insns.get(); StatRecorder { ccx: ccx, name: Some(name), @@ -165,13 +165,13 @@ impl<'a> Drop for StatRecorder<'a> { if self.ccx.sess().trans_stats() { let end = time::precise_time_ns(); let elapsed = ((end - self.start) / 1_000_000) as uint; - let iend = self.ccx.stats.n_llvm_insns.get(); - self.ccx.stats.fn_stats.borrow_mut().push((self.name.take().unwrap(), + let iend = self.ccx.stats().n_llvm_insns.get(); + self.ccx.stats().fn_stats.borrow_mut().push((self.name.take().unwrap(), elapsed, iend - self.istart)); - self.ccx.stats.n_fns.set(self.ccx.stats.n_fns.get() + 1); + self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1); // Reset LLVM insn count to avoid compound costs. - self.ccx.stats.n_llvm_insns.set(self.istart); + self.ccx.stats().n_llvm_insns.set(self.istart); } } } @@ -182,7 +182,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, let llfn: ValueRef = name.with_c_str(|buf| { unsafe { - llvm::LLVMGetOrInsertFunction(ccx.llmod, buf, ty.to_ref()) + llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf, ty.to_ref()) } }); @@ -198,7 +198,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, _ => {} } - if ccx.tcx.sess.opts.cg.no_redzone { + if ccx.tcx().sess.opts.cg.no_redzone { unsafe { llvm::LLVMAddFunctionAttribute(llfn, llvm::FunctionIndex as c_uint, @@ -243,7 +243,7 @@ pub fn get_extern_fn(ccx: &CrateContext, } fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef { - match ccx.externs.borrow().find_equiv(&name) { + match ccx.externs().borrow().find_equiv(&name) { Some(n) => return *n, None => () } @@ -254,7 +254,7 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De set_llvm_fn_attrs(attrs.as_slice(), f) }); - ccx.externs.borrow_mut().insert(name.to_string(), f); + ccx.externs().borrow_mut().insert(name.to_string(), f); f } @@ -264,14 +264,14 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext, let unboxed_closure_type = ty::mk_unboxed_closure(ccx.tcx(), closure_id, ty::ReStatic); - let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); let unboxed_closure = unboxed_closures.get(&closure_id); match unboxed_closure.kind { ty::FnUnboxedClosureKind => { - ty::mk_imm_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type) + ty::mk_imm_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type) } ty::FnMutUnboxedClosureKind => { - ty::mk_mut_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type) + ty::mk_mut_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type) } ty::FnOnceUnboxedClosureKind => unboxed_closure_type, } @@ -279,7 +279,7 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext, pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) -> ty::UnboxedClosureKind { - let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); unboxed_closures.get(&closure_id).kind } @@ -292,7 +292,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx))) } ty::ty_unboxed_closure(closure_did, _) => { - let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); let unboxed_closure = unboxed_closures.get(&closure_did); let function_type = unboxed_closure.closure_type.clone(); let self_type = self_type_for_unboxed_closure(ccx, closure_did); @@ -308,7 +308,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { let llfty = type_of_rust_fn(ccx, env, inputs.as_slice(), output, abi); debug!("decl_rust_fn(input count={},type={})", inputs.len(), - ccx.tn.type_to_string(llfty)); + ccx.tn().type_to_string(llfty)); let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output); let attrs = get_fn_llvm_attributes(ccx, fn_ty); @@ -413,15 +413,15 @@ pub fn malloc_raw_dyn_managed<'a>( // Type descriptor and type glue stuff pub fn get_tydesc(ccx: &CrateContext, t: ty::t) -> Rc { - match ccx.tydescs.borrow().find(&t) { + match ccx.tydescs().borrow().find(&t) { Some(inf) => return inf.clone(), _ => { } } - ccx.stats.n_static_tydescs.set(ccx.stats.n_static_tydescs.get() + 1u); + ccx.stats().n_static_tydescs.set(ccx.stats().n_static_tydescs.get() + 1u); let inf = Rc::new(glue::declare_tydesc(ccx, t)); - ccx.tydescs.borrow_mut().insert(t, inf.clone()); + ccx.tydescs().borrow_mut().insert(t, inf.clone()); inf } @@ -492,10 +492,10 @@ pub fn unset_split_stack(f: ValueRef) { // Double-check that we never ask LLVM to declare the same symbol twice. It // silently mangles such symbols, breaking our linkage model. pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) { - if ccx.all_llvm_symbols.borrow().contains(&sym) { + if ccx.all_llvm_symbols().borrow().contains(&sym) { ccx.sess().bug(format!("duplicate LLVM symbol: {}", sym).as_slice()); } - ccx.all_llvm_symbols.borrow_mut().insert(sym); + ccx.all_llvm_symbols().borrow_mut().insert(sym); } @@ -532,7 +532,7 @@ pub fn get_res_dtor(ccx: &CrateContext, let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID, [glue::get_drop_glue_type(ccx, t)], ty::mk_nil()); get_extern_fn(ccx, - &mut *ccx.externs.borrow_mut(), + &mut *ccx.externs().borrow_mut(), name.as_slice(), llvm::CCallConv, llty, @@ -961,8 +961,8 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val } _ => { let llty = type_of(ccx, t); - get_extern_const(&mut *ccx.externs.borrow_mut(), - ccx.llmod, + get_extern_const(&mut *ccx.externs().borrow_mut(), + ccx.llmod(), name.as_slice(), llty) } @@ -1165,7 +1165,7 @@ pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, let memcpy = ccx.get_intrinsic(&key); let src_ptr = PointerCast(cx, src, Type::i8p(ccx)); let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx)); - let size = IntCast(cx, n_bytes, ccx.int_type); + let size = IntCast(cx, n_bytes, ccx.int_type()); let align = C_i32(ccx, align as i32); let volatile = C_bool(ccx, false); Call(cx, memcpy, [dst_ptr, src_ptr, size, align, volatile], None); @@ -1426,7 +1426,7 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext, if id == -1 { "".to_string() } else { - ccx.tcx.map.path_to_string(id).to_string() + ccx.tcx().map.path_to_string(id).to_string() }, id, param_substs.repr(ccx.tcx())); @@ -1786,7 +1786,7 @@ pub fn trans_closure(ccx: &CrateContext, is_unboxed_closure: IsUnboxedClosureFlag, maybe_load_env: <'a>|&'a Block<'a>, ScopeId| -> &'a Block<'a>) { - ccx.stats.n_closures.set(ccx.stats.n_closures.get() + 1); + ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); let _icx = push_ctxt("trans_closure"); set_uwtable(llfndecl); @@ -1820,7 +1820,7 @@ pub fn trans_closure(ccx: &CrateContext, ty_to_string(ccx.tcx(), *monomorphized_arg_type)); } debug!("trans_closure: function lltype: {}", - bcx.fcx.ccx.tn.val_to_string(bcx.fcx.llfn)); + bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn)); let arg_datums = if abi != RustCall { create_datums_for_fn_args(&fcx, @@ -1912,7 +1912,7 @@ pub fn trans_fn(ccx: &CrateContext, param_substs: ¶m_substs, id: ast::NodeId, attrs: &[ast::Attribute]) { - let _s = StatRecorder::new(ccx, ccx.tcx.map.path_to_string(id).to_string()); + let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string()); debug!("trans_fn(param_substs={})", param_substs.repr(ccx.tcx())); let _icx = push_ctxt("trans_fn"); let fn_ty = ty::node_id_to_type(ccx.tcx(), id); @@ -1958,7 +1958,7 @@ pub fn trans_named_tuple_constructor<'a>(mut bcx: &'a Block<'a>, dest: expr::Dest) -> Result<'a> { let ccx = bcx.fcx.ccx; - let tcx = &ccx.tcx; + let tcx = ccx.tcx(); let result_ty = match ty::get(ctor_ty).sty { ty::ty_bare_fn(ref bft) => bft.sig.output, @@ -2064,7 +2064,7 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext, fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) { let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully - let levels = ccx.tcx.node_lint_levels.borrow(); + let levels = ccx.tcx().node_lint_levels.borrow(); let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCE); let lvlsrc = match levels.find(&(id, lint_id)) { None | Some(&(lint::Allow, _)) => return, @@ -2181,7 +2181,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { static"); } - let v = ccx.const_values.borrow().get_copy(&item.id); + let v = ccx.const_values().borrow().get_copy(&item.id); unsafe { if !(llvm::LLVMConstIntGetZExtValue(v) != 0) { ccx.sess().span_fatal(expr.span, "static assertion failed"); @@ -2218,9 +2218,9 @@ pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) { fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId, llfn: ValueRef) { - ccx.item_symbols.borrow_mut().insert(node_id, sym); + ccx.item_symbols().borrow_mut().insert(node_id, sym); - if !ccx.reachable.contains(&node_id) { + if !ccx.reachable().contains(&node_id) { llvm::SetLinkage(llfn, llvm::InternalLinkage); } @@ -2228,11 +2228,11 @@ fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::N // otherwise it would continue to be exhausted (bad), and both it and the // eh_personality functions need to be externally linkable. let def = ast_util::local_def(node_id); - if ccx.tcx.lang_items.stack_exhausted() == Some(def) { + if ccx.tcx().lang_items.stack_exhausted() == Some(def) { unset_split_stack(llfn); llvm::SetLinkage(llfn, llvm::ExternalLinkage); } - if ccx.tcx.lang_items.eh_personality() == Some(def) { + if ccx.tcx().lang_items.eh_personality() == Some(def) { llvm::SetLinkage(llfn, llvm::ExternalLinkage); } @@ -2268,7 +2268,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true), ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false), ty::ty_unboxed_closure(closure_did, _) => { - let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); let ref function_type = unboxed_closures.get(&closure_did) .closure_type; @@ -2485,8 +2485,8 @@ pub fn create_entry_wrapper(ccx: &CrateContext, fn create_entry_fn(ccx: &CrateContext, rust_main: ValueRef, use_start_lang_item: bool) { - let llfty = Type::func([ccx.int_type, Type::i8p(ccx).ptr_to()], - &ccx.int_type); + let llfty = Type::func([ccx.int_type(), Type::i8p(ccx).ptr_to()], + &ccx.int_type()); let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil()); @@ -2498,15 +2498,15 @@ pub fn create_entry_wrapper(ccx: &CrateContext, let llbb = "top".with_c_str(|buf| { unsafe { - llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llfn, buf) + llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, buf) } }); - let bld = ccx.builder.b; + let bld = ccx.raw_builder(); unsafe { llvm::LLVMPositionBuilderAtEnd(bld, llbb); let (start_fn, args) = if use_start_lang_item { - let start_def_id = match ccx.tcx.lang_items.require(StartFnLangItem) { + let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) { Ok(id) => id, Err(s) => { ccx.sess().fatal(s.as_slice()); } }; @@ -2557,7 +2557,7 @@ fn exported_name(ccx: &CrateContext, id: ast::NodeId, // Use provided name Some(name) => name.get().to_string(), - _ => ccx.tcx.map.with_path(id, |mut path| { + _ => ccx.tcx().map.with_path(id, |mut path| { if attr::contains_name(attrs, "no_mangle") { // Don't mangle path.last().unwrap().to_string() @@ -2577,13 +2577,13 @@ fn exported_name(ccx: &CrateContext, id: ast::NodeId, pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { debug!("get_item_val(id=`{:?}`)", id); - match ccx.item_vals.borrow().find_copy(&id) { + match ccx.item_vals().borrow().find_copy(&id) { Some(v) => return v, None => {} } let mut foreign = false; - let item = ccx.tcx.map.get(id); + let item = ccx.tcx().map.get(id); let val = match item { ast_map::NodeItem(i) => { let ty = ty::node_id_to_type(ccx.tcx(), i.id); @@ -2597,7 +2597,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { // information in the hash of the symbol debug!("making {}", sym); let (sym, is_local) = { - match ccx.external_srcs.borrow().find(&i.id) { + match ccx.external_srcs().borrow().find(&i.id) { Some(&did) => { debug!("but found in other crate..."); (csearch::get_symbol(&ccx.sess().cstore, @@ -2610,16 +2610,16 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { // We need the translated value here, because for enums the // LLVM type is not fully determined by the Rust type. let (v, inlineable, _) = consts::const_expr(ccx, &**expr, is_local); - ccx.const_values.borrow_mut().insert(id, v); + ccx.const_values().borrow_mut().insert(id, v); let mut inlineable = inlineable; unsafe { let llty = llvm::LLVMTypeOf(v); let g = sym.as_slice().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, llty, buf) + llvm::LLVMAddGlobal(ccx.llmod(), llty, buf) }); - if !ccx.reachable.contains(&id) { + if !ccx.reachable().contains(&id) { llvm::SetLinkage(g, llvm::InternalLinkage); } @@ -2655,11 +2655,11 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { if !inlineable { debug!("{} not inlined", sym); - ccx.non_inlineable_statics.borrow_mut() + ccx.non_inlineable_statics().borrow_mut() .insert(id); } - ccx.item_symbols.borrow_mut().insert(i.id, sym); + ccx.item_symbols().borrow_mut().insert(i.id, sym); g } } @@ -2717,7 +2717,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { match ni.node { ast::ForeignItemFn(..) => { - let abi = ccx.tcx.map.get_foreign_abi(id); + let abi = ccx.tcx().map.get_foreign_abi(id); let ty = ty::node_id_to_type(ccx.tcx(), ni.id); let name = foreign::link_name(&*ni); foreign::register_foreign_item_fn(ccx, abi, ty, @@ -2740,8 +2740,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { }; assert!(args.len() != 0u); let ty = ty::node_id_to_type(ccx.tcx(), id); - let parent = ccx.tcx.map.get_parent(id); - let enm = ccx.tcx.map.expect_item(parent); + let parent = ccx.tcx().map.get_parent(id); + let enm = ccx.tcx().map.expect_item(parent); let sym = exported_name(ccx, id, ty, @@ -2766,8 +2766,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { } Some(ctor_id) => ctor_id, }; - let parent = ccx.tcx.map.get_parent(id); - let struct_item = ccx.tcx.map.expect_item(parent); + let parent = ccx.tcx().map.get_parent(id); + let struct_item = ccx.tcx().map.expect_item(parent); let ty = ty::node_id_to_type(ccx.tcx(), ctor_id); let sym = exported_name(ccx, id, @@ -2789,11 +2789,11 @@ 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) { + if !foreign && !ccx.reachable().contains(&id) { llvm::SetLinkage(val, llvm::InternalLinkage); } - ccx.item_vals.borrow_mut().insert(id, val); + ccx.item_vals().borrow_mut().insert(id, val); val } @@ -2810,7 +2810,7 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId, pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef { unsafe { - return llvm::LLVMConstPtrToInt(v, ccx.int_type.to_ref()); + return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref()); } } @@ -2819,13 +2819,13 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI encoder::EncodeParams { diag: cx.sess().diagnostic(), tcx: cx.tcx(), - reexports2: &cx.exp_map2, - item_symbols: &cx.item_symbols, - non_inlineable_statics: &cx.non_inlineable_statics, - link_meta: &cx.link_meta, + reexports2: cx.exp_map2(), + item_symbols: cx.item_symbols(), + non_inlineable_statics: cx.non_inlineable_statics(), + link_meta: cx.link_meta(), cstore: &cx.sess().cstore, encode_inlined_item: ie, - reachable: &cx.reachable, + reachable: cx.reachable(), } } @@ -2854,11 +2854,11 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec { let llmeta = C_bytes(cx, compressed.as_slice()); let llconst = C_struct(cx, [llmeta], false); let name = format!("rust_metadata_{}_{}", - cx.link_meta.crate_name, - cx.link_meta.crate_hash); + cx.link_meta().crate_name, + cx.link_meta().crate_hash); let llglobal = name.with_c_str(|buf| { unsafe { - llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf) + llvm::LLVMAddGlobal(cx.metadata_llmod(), val_ty(llconst).to_ref(), buf) } }); unsafe { @@ -2927,20 +2927,20 @@ pub fn trans_crate(krate: ast::Crate, let metadata = write_metadata(&ccx, &krate); if ccx.sess().trans_stats() { println!("--- trans stats ---"); - println!("n_static_tydescs: {}", ccx.stats.n_static_tydescs.get()); - println!("n_glues_created: {}", ccx.stats.n_glues_created.get()); - println!("n_null_glues: {}", ccx.stats.n_null_glues.get()); - println!("n_real_glues: {}", ccx.stats.n_real_glues.get()); - - println!("n_fns: {}", ccx.stats.n_fns.get()); - println!("n_monos: {}", ccx.stats.n_monos.get()); - println!("n_inlines: {}", ccx.stats.n_inlines.get()); - println!("n_closures: {}", ccx.stats.n_closures.get()); + println!("n_static_tydescs: {}", ccx.stats().n_static_tydescs.get()); + println!("n_glues_created: {}", ccx.stats().n_glues_created.get()); + println!("n_null_glues: {}", ccx.stats().n_null_glues.get()); + println!("n_real_glues: {}", ccx.stats().n_real_glues.get()); + + println!("n_fns: {}", ccx.stats().n_fns.get()); + println!("n_monos: {}", ccx.stats().n_monos.get()); + println!("n_inlines: {}", ccx.stats().n_inlines.get()); + println!("n_closures: {}", ccx.stats().n_closures.get()); println!("fn stats:"); - ccx.stats.fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| { + ccx.stats().fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| { insns_b.cmp(&insns_a) }); - for tuple in ccx.stats.fn_stats.borrow().iter() { + for tuple in ccx.stats().fn_stats.borrow().iter() { match *tuple { (ref name, ms, insns) => { println!("{} insns, {} ms, {}", insns, ms, *name); @@ -2949,17 +2949,17 @@ pub fn trans_crate(krate: ast::Crate, } } if ccx.sess().count_llvm_insns() { - for (k, v) in ccx.stats.llvm_insns.borrow().iter() { + for (k, v) in ccx.stats().llvm_insns.borrow().iter() { println!("{:7u} {}", *v, *k); } } - let llcx = ccx.llcx; - let link_meta = ccx.link_meta.clone(); - let llmod = ccx.llmod; + let llcx = ccx.llcx(); + let link_meta = ccx.link_meta().clone(); + let llmod = ccx.llmod(); - let mut reachable: Vec = ccx.reachable.iter().filter_map(|id| { - ccx.item_symbols.borrow().find(id).map(|s| s.to_string()) + let mut reachable: Vec = ccx.reachable().iter().filter_map(|id| { + ccx.item_symbols().borrow().find(id).map(|s| s.to_string()) }).collect(); // For the purposes of LTO, we add to the reachable set all of the upstream @@ -2986,11 +2986,11 @@ pub fn trans_crate(krate: ast::Crate, // referenced from rt/rust_try.ll reachable.push("rust_eh_personality_catch".to_string()); - let metadata_module = ccx.metadata_llmod; - let formats = ccx.tcx.dependency_formats.borrow().clone(); + let metadata_module = ccx.metadata_llmod(); + let formats = ccx.tcx().dependency_formats.borrow().clone(); let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins"); - (ccx.tcx, CrateTranslation { + (ccx.take_tcx(), CrateTranslation { context: llcx, module: llmod, link: link_meta, diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index 76059e8df4f8c..fd988eb7fc137 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -352,7 +352,7 @@ pub fn Load(cx: &Block, pointer_val: ValueRef) -> ValueRef { let eltty = if ty.kind() == llvm::Array { ty.element_type() } else { - ccx.int_type + ccx.int_type() }; return llvm::LLVMGetUndef(eltty.to_ref()); } @@ -373,7 +373,7 @@ pub fn AtomicLoad(cx: &Block, pointer_val: ValueRef, order: AtomicOrdering) -> V unsafe { let ccx = cx.fcx.ccx; if cx.unreachable.get() { - return llvm::LLVMGetUndef(ccx.int_type.to_ref()); + return llvm::LLVMGetUndef(ccx.int_type().to_ref()); } B(cx).atomic_load(pointer_val, order) } @@ -388,7 +388,7 @@ pub fn LoadRangeAssert(cx: &Block, pointer_val: ValueRef, lo: c_ulonglong, let eltty = if ty.kind() == llvm::Array { ty.element_type() } else { - ccx.int_type + ccx.int_type() }; unsafe { llvm::LLVMGetUndef(eltty.to_ref()) @@ -658,7 +658,7 @@ pub fn _UndefReturn(cx: &Block, fn_: ValueRef) -> ValueRef { let retty = if ty.kind() == llvm::Integer { ty.return_type() } else { - ccx.int_type + ccx.int_type() }; B(cx).count_insn("ret_undef"); llvm::LLVMGetUndef(retty.to_ref()) @@ -786,7 +786,7 @@ pub fn IsNotNull(cx: &Block, val: ValueRef) -> ValueRef { pub fn PtrDiff(cx: &Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { unsafe { let ccx = cx.fcx.ccx; - if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type.to_ref()); } + if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type().to_ref()); } B(cx).ptrdiff(lhs, rhs) } } diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index b3192a405be59..29c7b93a3691f 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -38,21 +38,21 @@ pub fn noname() -> *const c_char { impl<'a> Builder<'a> { pub fn new(ccx: &'a CrateContext) -> Builder<'a> { Builder { - llbuilder: ccx.builder.b, + llbuilder: ccx.raw_builder(), ccx: ccx, } } pub fn count_insn(&self, category: &str) { if self.ccx.sess().trans_stats() { - self.ccx.stats.n_llvm_insns.set(self.ccx - .stats + self.ccx.stats().n_llvm_insns.set(self.ccx + .stats() .n_llvm_insns .get() + 1); } if self.ccx.sess().count_llvm_insns() { base::with_insn_ctxt(|v| { - let mut h = self.ccx.stats.llvm_insns.borrow_mut(); + let mut h = self.ccx.stats().llvm_insns.borrow_mut(); // Build version of path with cycles removed. @@ -160,9 +160,9 @@ impl<'a> Builder<'a> { self.count_insn("invoke"); debug!("Invoke {} with args ({})", - self.ccx.tn.val_to_string(llfn), + self.ccx.tn().val_to_string(llfn), args.iter() - .map(|&v| self.ccx.tn.val_to_string(v)) + .map(|&v| self.ccx.tn().val_to_string(v)) .collect::>() .connect(", ")); @@ -488,7 +488,7 @@ impl<'a> Builder<'a> { let v = [min, max]; llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.ccx.llcx, + llvm::LLVMMDNodeInContext(self.ccx.llcx(), v.as_ptr(), v.len() as c_uint)); } @@ -497,8 +497,8 @@ impl<'a> Builder<'a> { pub fn store(&self, val: ValueRef, ptr: ValueRef) { debug!("Store {} -> {}", - self.ccx.tn.val_to_string(val), - self.ccx.tn.val_to_string(ptr)); + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); assert!(self.llbuilder.is_not_null()); self.count_insn("store"); unsafe { @@ -508,8 +508,8 @@ impl<'a> Builder<'a> { pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) { debug!("Store {} -> {}", - self.ccx.tn.val_to_string(val), - self.ccx.tn.val_to_string(ptr)); + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); assert!(self.llbuilder.is_not_null()); self.count_insn("store.volatile"); unsafe { @@ -520,8 +520,8 @@ impl<'a> Builder<'a> { pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { debug!("Store {} -> {}", - self.ccx.tn.val_to_string(val), - self.ccx.tn.val_to_string(ptr)); + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); self.count_insn("store.atomic"); unsafe { let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); @@ -794,11 +794,11 @@ impl<'a> Builder<'a> { else { llvm::False }; let argtys = inputs.iter().map(|v| { - debug!("Asm Input Type: {:?}", self.ccx.tn.val_to_string(*v)); + debug!("Asm Input Type: {:?}", self.ccx.tn().val_to_string(*v)); val_ty(*v) }).collect::>(); - debug!("Asm Output Type: {:?}", self.ccx.tn.type_to_string(output)); + debug!("Asm Output Type: {:?}", self.ccx.tn().type_to_string(output)); let fty = Type::func(argtys.as_slice(), &output); unsafe { let v = llvm::LLVMInlineAsm( @@ -812,9 +812,9 @@ impl<'a> Builder<'a> { self.count_insn("call"); debug!("Call {} with args ({})", - self.ccx.tn.val_to_string(llfn), + self.ccx.tn().val_to_string(llfn), args.iter() - .map(|&v| self.ccx.tn.val_to_string(v)) + .map(|&v| self.ccx.tn().val_to_string(v)) .collect::>() .connect(", ")); diff --git a/src/librustc/middle/trans/cabi_mips.rs b/src/librustc/middle/trans/cabi_mips.rs index 77815285428fd..90bd1521705f0 100644 --- a/src/librustc/middle/trans/cabi_mips.rs +++ b/src/librustc/middle/trans/cabi_mips.rs @@ -146,7 +146,7 @@ fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec { let r = size % 32; if r > 0 { unsafe { - args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx, r as c_uint))); + args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint))); } } diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index 515413e03f060..15d296ac72371 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -87,7 +87,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { */ debug!("push_ast_cleanup_scope({})", - self.ccx.tcx.map.node_to_string(id)); + self.ccx.tcx().map.node_to_string(id)); // FIXME(#2202) -- currently closure bodies have a parent // region, which messes up the assertion below, since there @@ -101,7 +101,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { // this new AST scope had better be its immediate child. let top_scope = self.top_ast_scope(); if top_scope.is_some() { - assert_eq!(self.ccx.tcx.region_maps.opt_encl_scope(id), top_scope); + assert_eq!(self.ccx.tcx().region_maps.opt_encl_scope(id), top_scope); } self.push_scope(CleanupScope::new(AstScopeKind(id))); @@ -111,7 +111,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { id: ast::NodeId, exits: [&'a Block<'a>, ..EXIT_MAX]) { debug!("push_loop_cleanup_scope({})", - self.ccx.tcx.map.node_to_string(id)); + self.ccx.tcx().map.node_to_string(id)); assert_eq!(Some(id), self.top_ast_scope()); self.push_scope(CleanupScope::new(LoopScopeKind(id, exits))); @@ -135,7 +135,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { */ debug!("pop_and_trans_ast_cleanup_scope({})", - self.ccx.tcx.map.node_to_string(cleanup_scope)); + self.ccx.tcx().map.node_to_string(cleanup_scope)); assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope))); @@ -154,7 +154,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { */ debug!("pop_loop_cleanup_scope({})", - self.ccx.tcx.map.node_to_string(cleanup_scope)); + self.ccx.tcx().map.node_to_string(cleanup_scope)); assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope))); @@ -237,7 +237,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { debug!("schedule_lifetime_end({:?}, val={})", cleanup_scope, - self.ccx.tn.val_to_string(val)); + self.ccx.tn().val_to_string(val)); self.schedule_clean(cleanup_scope, drop as CleanupObj); } @@ -262,7 +262,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { debug!("schedule_drop_mem({:?}, val={}, ty={})", cleanup_scope, - self.ccx.tn.val_to_string(val), + self.ccx.tn().val_to_string(val), ty.repr(self.ccx.tcx())); self.schedule_clean(cleanup_scope, drop as CleanupObj); @@ -288,7 +288,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { debug!("schedule_drop_and_zero_mem({:?}, val={}, ty={}, zero={})", cleanup_scope, - self.ccx.tn.val_to_string(val), + self.ccx.tn().val_to_string(val), ty.repr(self.ccx.tcx()), true); @@ -314,7 +314,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { debug!("schedule_drop_immediate({:?}, val={}, ty={})", cleanup_scope, - self.ccx.tn.val_to_string(val), + self.ccx.tn().val_to_string(val), ty.repr(self.ccx.tcx())); self.schedule_clean(cleanup_scope, drop as CleanupObj); @@ -334,7 +334,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { debug!("schedule_free_value({:?}, val={}, heap={:?})", cleanup_scope, - self.ccx.tn.val_to_string(val), + self.ccx.tn().val_to_string(val), heap); self.schedule_clean(cleanup_scope, drop as CleanupObj); @@ -374,7 +374,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { self.ccx.sess().bug( format!("no cleanup scope {} found", - self.ccx.tcx.map.node_to_string(cleanup_scope)).as_slice()); + self.ccx.tcx().map.node_to_string(cleanup_scope)).as_slice()); } fn schedule_clean_in_custom_scope(&self, @@ -720,7 +720,7 @@ impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> { let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { Some(def_id) => callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)), None => { - let mut personality = self.ccx.eh_personality.borrow_mut(); + let mut personality = self.ccx.eh_personality().borrow_mut(); match *personality { Some(llpersonality) => llpersonality, None => { diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index 17f1b6ca52669..4e9431f262911 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -427,12 +427,12 @@ pub fn trans_expr_fn<'a>( pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) -> Option { - if !ccx.tcx.unboxed_closures.borrow().contains_key(&closure_id) { + if !ccx.tcx().unboxed_closures.borrow().contains_key(&closure_id) { // Not an unboxed closure. return None } - match ccx.unboxed_closure_vals.borrow().find(&closure_id) { + match ccx.unboxed_closure_vals().borrow().find(&closure_id) { Some(llfn) => { debug!("get_or_create_declaration_if_unboxed_closure(): found \ closure"); @@ -441,10 +441,10 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext, None => {} } - let function_type = ty::mk_unboxed_closure(&ccx.tcx, + let function_type = ty::mk_unboxed_closure(ccx.tcx(), closure_id, ty::ReStatic); - let symbol = ccx.tcx.map.with_path(closure_id.node, |path| { + let symbol = ccx.tcx().map.with_path(closure_id.node, |path| { mangle_internal_name_by_path_and_seq(path, "unboxed_closure") }); @@ -456,8 +456,8 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext, debug!("get_or_create_declaration_if_unboxed_closure(): inserting new \ closure {} (type {})", closure_id, - ccx.tn.type_to_string(val_ty(llfn))); - ccx.unboxed_closure_vals.borrow_mut().insert(closure_id, llfn); + ccx.tn().type_to_string(val_ty(llfn))); + ccx.unboxed_closure_vals().borrow_mut().insert(closure_id, llfn); Some(llfn) } @@ -554,7 +554,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext, } }; - match ccx.closure_bare_wrapper_cache.borrow().find(&fn_ptr) { + match ccx.closure_bare_wrapper_cache().borrow().find(&fn_ptr) { Some(&llval) => return llval, None => {} } @@ -581,7 +581,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext, decl_rust_fn(ccx, closure_ty, name.as_slice()) }; - ccx.closure_bare_wrapper_cache.borrow_mut().insert(fn_ptr, llfn); + ccx.closure_bare_wrapper_cache().borrow_mut().insert(fn_ptr, llfn); // This is only used by statics inlined from a different crate. if !is_local { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index d92364b257010..f0014827cc780 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -82,7 +82,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | ty::ty_unboxed_closure(..) => { let llty = sizing_type_of(ccx, ty); - llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type) + llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type()) } _ => type_is_zero_size(ccx, ty) } @@ -342,7 +342,7 @@ impl<'a> FunctionContext<'a> { self.llreturn.set(Some(unsafe { "return".with_c_str(|buf| { - llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx, self.llfn, buf) + llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf) }) })) } @@ -365,7 +365,7 @@ impl<'a> FunctionContext<'a> { -> &'a Block<'a> { unsafe { let llbb = name.with_c_str(|buf| { - llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx, + llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf) }); @@ -451,7 +451,7 @@ impl<'a> Block<'a> { pub fn ccx(&self) -> &'a CrateContext { self.fcx.ccx } pub fn tcx(&self) -> &'a ty::ctxt { - &self.fcx.ccx.tcx + self.fcx.ccx.tcx() } pub fn sess(&self) -> &'a Session { self.fcx.ccx.sess() } @@ -478,11 +478,11 @@ impl<'a> Block<'a> { } pub fn val_to_string(&self, val: ValueRef) -> String { - self.ccx().tn.val_to_string(val) + self.ccx().tn().val_to_string(val) } pub fn llty_str(&self, ty: Type) -> String { - self.ccx().tn.type_to_string(ty) + self.ccx().tn().type_to_string(ty) } pub fn ty_to_string(&self, t: ty::t) -> String { @@ -601,11 +601,11 @@ pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef { } pub fn C_int(ccx: &CrateContext, i: int) -> ValueRef { - C_integral(ccx.int_type, i as u64, true) + C_integral(ccx.int_type(), i as u64, true) } pub fn C_uint(ccx: &CrateContext, i: uint) -> ValueRef { - C_integral(ccx.int_type, i as u64, false) + C_integral(ccx.int_type(), i as u64, false) } pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef { @@ -617,25 +617,25 @@ pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef { // our boxed-and-length-annotated strings. pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> ValueRef { unsafe { - match cx.const_cstr_cache.borrow().find(&s) { + match cx.const_cstr_cache().borrow().find(&s) { Some(&llval) => return llval, None => () } - let sc = llvm::LLVMConstStringInContext(cx.llcx, + let sc = llvm::LLVMConstStringInContext(cx.llcx(), s.get().as_ptr() as *const c_char, s.get().len() as c_uint, !null_terminated as Bool); let gsym = token::gensym("str"); let g = format!("str{}", gsym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(cx.llmod, val_ty(sc).to_ref(), buf) + llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf) }); llvm::LLVMSetInitializer(g, sc); llvm::LLVMSetGlobalConstant(g, True); llvm::SetLinkage(g, llvm::InternalLinkage); - cx.const_cstr_cache.borrow_mut().insert(s, g); + cx.const_cstr_cache().borrow_mut().insert(s, g); g } } @@ -647,7 +647,7 @@ pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef { let len = s.get().len(); let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s, false), Type::i8p(cx).to_ref()); - C_named_struct(cx.tn.find_type("str_slice").unwrap(), [cs, C_uint(cx, len)]) + C_named_struct(cx.tn().find_type("str_slice").unwrap(), [cs, C_uint(cx, len)]) } } @@ -658,7 +658,7 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { let gsym = token::gensym("binary"); let g = format!("binary{}", gsym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(cx.llmod, val_ty(lldata).to_ref(), buf) + llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), buf) }); llvm::LLVMSetInitializer(g, lldata); llvm::LLVMSetGlobalConstant(g, True); @@ -671,7 +671,7 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { pub fn C_struct(ccx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { unsafe { - llvm::LLVMConstStructInContext(ccx.llcx, + llvm::LLVMConstStructInContext(ccx.llcx(), elts.as_ptr(), elts.len() as c_uint, packed as Bool) } @@ -692,7 +692,7 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { pub fn C_bytes(ccx: &CrateContext, bytes: &[u8]) -> ValueRef { unsafe { let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(ccx.llcx, ptr, bytes.len() as c_uint, True); + return llvm::LLVMConstStringInContext(ccx.llcx(), ptr, bytes.len() as c_uint, True); } } @@ -702,7 +702,7 @@ pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint]) let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); debug!("const_get_elt(v={}, us={:?}, r={})", - cx.tn.val_to_string(v), us, cx.tn.val_to_string(r)); + cx.tn().val_to_string(v), us, cx.tn().val_to_string(r)); return r; } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 10b6adad1e33d..2571ae1fe72e1 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -91,7 +91,7 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: ast::Lit) pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef { unsafe { let b = llvm::LLVMConstPointerCast(a, t.ptr_to().to_ref()); - assert!(cx.const_globals.borrow_mut().insert(b as int, a)); + assert!(cx.const_globals().borrow_mut().insert(b as int, a)); b } } @@ -119,7 +119,7 @@ fn const_vec(cx: &CrateContext, e: &ast::Expr, pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef { unsafe { let gv = "const".with_c_str(|name| { - llvm::LLVMAddGlobal(cx.llmod, val_ty(cv).to_ref(), name) + llvm::LLVMAddGlobal(cx.llmod(), val_ty(cv).to_ref(), name) }); llvm::LLVMSetInitializer(gv, cv); llvm::LLVMSetGlobalConstant(gv, @@ -130,7 +130,7 @@ pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> } fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { - let v = match cx.const_globals.borrow().find(&(v as int)) { + let v = match cx.const_globals().borrow().find(&(v as int)) { Some(&v) => v, None => v }; @@ -178,13 +178,13 @@ fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool) pub fn get_const_val(cx: &CrateContext, mut def_id: ast::DefId) -> (ValueRef, bool) { - let contains_key = cx.const_values.borrow().contains_key(&def_id.node); + let contains_key = cx.const_values().borrow().contains_key(&def_id.node); if !ast_util::is_local(def_id) || !contains_key { if !ast_util::is_local(def_id) { def_id = inline::maybe_instantiate_inline(cx, def_id); } - match cx.tcx.map.expect_item(def_id.node).node { + match cx.tcx().map.expect_item(def_id.node).node { ast::ItemStatic(_, ast::MutImmutable, _) => { trans_const(cx, ast::MutImmutable, def_id.node); } @@ -192,8 +192,8 @@ pub fn get_const_val(cx: &CrateContext, } } - (cx.const_values.borrow().get_copy(&def_id.node), - !cx.non_inlineable_statics.borrow().contains(&def_id.node)) + (cx.const_values().borrow().get_copy(&def_id.node), + !cx.non_inlineable_statics().borrow().contains(&def_id.node)) } pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool, ty::t) { @@ -202,7 +202,7 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef let mut inlineable = inlineable; let ety = ty::expr_ty(cx.tcx(), e); let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); - let opt_adj = cx.tcx.adjustments.borrow().find_copy(&e.id); + let opt_adj = cx.tcx().adjustments.borrow().find_copy(&e.id); match opt_adj { None => { } Some(adj) => { @@ -523,7 +523,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, (expr::cast_enum, expr::cast_integral) => { let repr = adt::represent_type(cx, basety); let discr = adt::const_get_discrim(cx, &*repr, v); - let iv = C_integral(cx.int_type, discr, false); + let iv = C_integral(cx.int_type(), discr, false); let ety_cast = expr::cast_type_kind(cx.tcx(), ety); match ety_cast { expr::cast_integral => { @@ -690,7 +690,7 @@ pub fn trans_const(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) { let g = base::get_item_val(ccx, id); // At this point, get_item_val has already translated the // constant's initializer to determine its LLVM type. - let v = ccx.const_values.borrow().get_copy(&id); + let v = ccx.const_values().borrow().get_copy(&id); llvm::LLVMSetInitializer(g, v); 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 bb528790314da..d3538afa972f3 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -11,7 +11,7 @@ use driver::config::NoDebugInfo; use driver::session::Session; use llvm; -use llvm::{ContextRef, ModuleRef, ValueRef}; +use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; use llvm::{TargetData}; use llvm::mk_target_data; use metadata::common::LinkMeta; @@ -52,38 +52,38 @@ pub struct Stats { } pub struct CrateContext { - pub llmod: ModuleRef, - pub llcx: ContextRef, - pub metadata_llmod: ModuleRef, - pub td: TargetData, - pub tn: TypeNames, - pub externs: RefCell, - pub item_vals: RefCell>, - pub exp_map2: resolve::ExportMap2, - pub reachable: NodeSet, - pub item_symbols: RefCell>, - pub link_meta: LinkMeta, - pub drop_glues: RefCell>, - pub tydescs: RefCell>>, + llmod: ModuleRef, + llcx: ContextRef, + metadata_llmod: ModuleRef, + td: TargetData, + tn: TypeNames, + externs: RefCell, + item_vals: RefCell>, + exp_map2: resolve::ExportMap2, + reachable: NodeSet, + item_symbols: RefCell>, + link_meta: LinkMeta, + drop_glues: RefCell>, + tydescs: RefCell>>, /// Set when running emit_tydescs to enforce that no more tydescs are /// created. - pub finished_tydescs: Cell, + finished_tydescs: Cell, /// Track mapping of external ids to local items imported for inlining - pub external: RefCell>>, + external: RefCell>>, /// Backwards version of the `external` map (inlined items to where they /// came from) - pub external_srcs: RefCell>, + external_srcs: RefCell>, /// A set of static items which cannot be inlined into other crates. This /// will prevent in IIItem() structures from being encoded into the metadata /// that is generated - pub non_inlineable_statics: RefCell, + non_inlineable_statics: RefCell, /// Cache instances of monomorphized functions - pub monomorphized: RefCell>, - pub monomorphizing: RefCell>, + monomorphized: RefCell>, + monomorphizing: RefCell>, /// Cache generated vtables - pub vtables: RefCell>, + vtables: RefCell>, /// Cache of constant strings, - pub const_cstr_cache: RefCell>, + const_cstr_cache: RefCell>, /// Reverse-direction for const ptrs cast from globals. /// Key is an int, cast from a ValueRef holding a *T, @@ -93,37 +93,37 @@ pub struct CrateContext { /// when we ptrcast, and we have to ptrcast during translation /// of a [T] const because we form a slice, a [*T,int] pair, not /// a pointer to an LLVM array type. - pub const_globals: RefCell>, + const_globals: RefCell>, /// Cache of emitted const values - pub const_values: RefCell>, + const_values: RefCell>, /// Cache of external const values - pub extern_const_values: RefCell>, + extern_const_values: RefCell>, - pub impl_method_cache: RefCell>, + impl_method_cache: RefCell>, /// Cache of closure wrappers for bare fn's. - pub closure_bare_wrapper_cache: RefCell>, - - pub lltypes: RefCell>, - pub llsizingtypes: RefCell>, - pub adt_reprs: RefCell>>, - pub symbol_hasher: RefCell, - pub type_hashcodes: RefCell>, - pub all_llvm_symbols: RefCell>, - pub tcx: ty::ctxt, - pub stats: Stats, - pub int_type: Type, - pub opaque_vec_type: Type, - pub builder: BuilderRef_res, + closure_bare_wrapper_cache: RefCell>, + + lltypes: RefCell>, + llsizingtypes: RefCell>, + adt_reprs: RefCell>>, + symbol_hasher: RefCell, + type_hashcodes: RefCell>, + all_llvm_symbols: RefCell>, + tcx: ty::ctxt, + stats: Stats, + int_type: Type, + opaque_vec_type: Type, + builder: BuilderRef_res, /// Holds the LLVM values for closure IDs. - pub unboxed_closure_vals: RefCell>, + unboxed_closure_vals: RefCell>, - pub dbg_cx: Option, + dbg_cx: Option, - pub eh_personality: RefCell>, + eh_personality: RefCell>, intrinsics: RefCell>, } @@ -235,10 +235,10 @@ impl CrateContext { ccx.opaque_vec_type = Type::opaque_vec(&ccx); let mut str_slice_ty = Type::named_struct(&ccx, "str_slice"); - str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type], false); - ccx.tn.associate_type("str_slice", &str_slice_ty); + str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type()], false); + ccx.tn().associate_type("str_slice", &str_slice_ty); - ccx.tn.associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); + ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); if ccx.sess().count_llvm_insns() { base::init_insn_ctxt() @@ -252,6 +252,10 @@ impl CrateContext { &self.tcx } + pub fn take_tcx(self) -> ty::ctxt { + self.tcx + } + pub fn sess<'a>(&'a self) -> &'a Session { &self.tcx.sess } @@ -260,6 +264,10 @@ impl CrateContext { Builder::new(self) } + pub fn raw_builder<'a>(&'a self) -> BuilderRef { + self.builder.b + } + pub fn tydesc_type(&self) -> Type { self.tn.find_type("tydesc").unwrap() } @@ -286,6 +294,164 @@ impl CrateContext { let ref cfg = self.sess().targ_cfg; cfg.os != abi::OsiOS || cfg.arch != abi::Arm } + + + pub fn llmod(&self) -> ModuleRef { + self.llmod + } + + pub fn llcx(&self) -> ContextRef { + self.llcx + } + + pub fn metadata_llmod(&self) -> ModuleRef { + self.metadata_llmod + } + + pub fn td<'a>(&'a self) -> &'a TargetData { + &self.td + } + + pub fn tn<'a>(&'a self) -> &'a TypeNames { + &self.tn + } + + pub fn externs<'a>(&'a self) -> &'a RefCell { + &self.externs + } + + pub fn item_vals<'a>(&'a self) -> &'a RefCell> { + &self.item_vals + } + + pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { + &self.exp_map2 + } + + pub fn reachable<'a>(&'a self) -> &'a NodeSet { + &self.reachable + } + + pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { + &self.item_symbols + } + + pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { + &self.link_meta + } + + pub fn drop_glues<'a>(&'a self) -> &'a RefCell> { + &self.drop_glues + } + + pub fn tydescs<'a>(&'a self) -> &'a RefCell>> { + &self.tydescs + } + + pub fn finished_tydescs<'a>(&'a self) -> &'a Cell { + &self.finished_tydescs + } + + pub fn external<'a>(&'a self) -> &'a RefCell>> { + &self.external + } + + pub fn external_srcs<'a>(&'a self) -> &'a RefCell> { + &self.external_srcs + } + + pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell { + &self.non_inlineable_statics + } + + pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { + &self.monomorphized + } + + pub fn monomorphizing<'a>(&'a self) -> &'a RefCell> { + &self.monomorphizing + } + + pub fn vtables<'a>(&'a self) -> &'a RefCell> { + &self.vtables + } + + pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell> { + &self.const_cstr_cache + } + + pub fn const_globals<'a>(&'a self) -> &'a RefCell> { + &self.const_globals + } + + pub fn const_values<'a>(&'a self) -> &'a RefCell> { + &self.const_values + } + + pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { + &self.extern_const_values + } + + pub fn impl_method_cache<'a>(&'a self) + -> &'a RefCell> { + &self.impl_method_cache + } + + pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell> { + &self.closure_bare_wrapper_cache + } + + pub fn lltypes<'a>(&'a self) -> &'a RefCell> { + &self.lltypes + } + + pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell> { + &self.llsizingtypes + } + + pub fn adt_reprs<'a>(&'a self) -> &'a RefCell>> { + &self.adt_reprs + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &self.symbol_hasher + } + + pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell> { + &self.type_hashcodes + } + + pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell> { + &self.all_llvm_symbols + } + + pub fn stats<'a>(&'a self) -> &'a Stats { + &self.stats + } + + pub fn int_type(&self) -> Type { + self.int_type + } + + pub fn opaque_vec_type(&self) -> Type { + self.opaque_vec_type + } + + pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell> { + &self.unboxed_closure_vals + } + + pub fn dbg_cx<'a>(&'a self) -> &'a Option { + &self.dbg_cx + } + + pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { + &self.eh_personality + } + + fn intrinsics<'a>(&'a self) -> &'a RefCell> { + &self.intrinsics + } } fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { @@ -293,7 +459,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $ret:expr) => ( if *key == $name { let f = base::decl_cdecl_fn(ccx, $name, Type::func([], &$ret), ty::mk_nil()); - ccx.intrinsics.borrow_mut().insert($name, f.clone()); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); return Some(f); } ); @@ -301,7 +467,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option Option Datum { #[allow(dead_code)] // useful for debugging pub fn to_string(&self, ccx: &CrateContext) -> String { format!("Datum({}, {}, {:?})", - ccx.tn.val_to_string(self.val), + ccx.tn().val_to_string(self.val), ty_to_string(ccx.tcx(), self.ty), self.kind) } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 8403e84f7b655..01633bea9565c 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -538,7 +538,7 @@ impl TypeMap { // First, find out the 'real' def_id of the type. Items inlined from // other crates have to be mapped back to their source. let source_def_id = if def_id.krate == ast::LOCAL_CRATE { - match cx.external_srcs.borrow().find_copy(&def_id.node) { + match cx.external_srcs().borrow().find_copy(&def_id.node) { Some(source_def_id) => { // The given def_id identifies the inlined copy of a // type definition, let's take the source of the copy. @@ -552,7 +552,7 @@ impl TypeMap { // Get the crate hash as first part of the identifier. let crate_hash = if source_def_id.krate == ast::LOCAL_CRATE { - cx.link_meta.crate_hash.clone() + cx.link_meta().crate_hash.clone() } else { cx.sess().cstore.get_crate_hash(source_def_id.krate) }; @@ -721,7 +721,7 @@ enum VariableKind { /// Create any deferred debug metadata nodes pub fn finalize(cx: &CrateContext) { - if cx.dbg_cx.is_none() { + if cx.dbg_cx().is_none() { return; } @@ -738,18 +738,18 @@ pub fn finalize(cx: &CrateContext) { if cx.sess().targ_cfg.os == abi::OsMacos || cx.sess().targ_cfg.os == abi::OsiOS { "Dwarf Version".with_c_str( - |s| llvm::LLVMRustAddModuleFlag(cx.llmod, s, 2)); + |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 2)); } else { // FIXME(#13611) this is a kludge fix because the Linux bots have // gdb 7.4 which doesn't understand dwarf4, we should // do something more graceful here. "Dwarf Version".with_c_str( - |s| llvm::LLVMRustAddModuleFlag(cx.llmod, s, 3)); + |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 3)); } // Prevent bitcode readers from deleting the debug info. "Debug Info Version".with_c_str( - |s| llvm::LLVMRustAddModuleFlag(cx.llmod, s, + |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, llvm::LLVMRustDebugMetadataVersion)); }; } @@ -760,7 +760,7 @@ pub fn finalize(cx: &CrateContext) { pub fn create_global_var_metadata(cx: &CrateContext, node_id: ast::NodeId, global: ValueRef) { - if cx.dbg_cx.is_none() { + if cx.dbg_cx().is_none() { return; } @@ -768,11 +768,11 @@ pub fn create_global_var_metadata(cx: &CrateContext, // crate should already contain debuginfo for it. More importantly, the // global might not even exist in un-inlined form anywhere which would lead // to a linker errors. - if cx.external_srcs.borrow().contains_key(&node_id) { + if cx.external_srcs().borrow().contains_key(&node_id) { return; } - let var_item = cx.tcx.map.get(node_id); + let var_item = cx.tcx().map.get(node_id); let (ident, span) = match var_item { ast_map::NodeItem(item) => { @@ -838,7 +838,7 @@ pub fn create_local_var_metadata(bcx: &Block, local: &ast::Local) { } let cx = bcx.ccx(); - let def_map = &cx.tcx.def_map; + let def_map = &cx.tcx().def_map; pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, path1| { let var_ident = path1.node; @@ -880,7 +880,7 @@ pub fn create_captured_var_metadata(bcx: &Block, let cx = bcx.ccx(); - let ast_item = cx.tcx.map.find(node_id); + let ast_item = cx.tcx().map.find(node_id); let variable_ident = match ast_item { None => { @@ -963,7 +963,7 @@ pub fn create_match_binding_metadata(bcx: &Block, let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span); let aops = unsafe { - [llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type.to_ref())] + [llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type().to_ref())] }; // Regardless of the actual type (`T`) we're always passed the stack slot (alloca) // for the binding. For ByRef bindings that's a `T*` but for ByMove bindings we @@ -1002,7 +1002,7 @@ pub fn create_argument_metadata(bcx: &Block, arg: &ast::Arg) { let fcx = bcx.fcx; let cx = fcx.ccx; - let def_map = &cx.tcx.def_map; + let def_map = &cx.tcx().def_map; let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata; pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, path1| { @@ -1120,7 +1120,7 @@ pub fn create_function_debug_context(cx: &CrateContext, let empty_generics = ast_util::empty_generics(); - let fnitem = cx.tcx.map.get(fn_ast_id); + let fnitem = cx.tcx().map.get(fn_ast_id); let (ident, fn_decl, generics, top_level_block, span, has_path) = match fnitem { ast_map::NodeItem(ref item) => { @@ -1447,7 +1447,7 @@ fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool // externally visible or by being inlined into something externally visible). // It might better to use the `exported_items` set from `driver::CrateAnalysis` // in the future, but (atm) this set is not available in the translation pass. - !cx.reachable.contains(&node_id) + !cx.reachable().contains(&node_id) } #[allow(non_snake_case)] @@ -1514,7 +1514,7 @@ fn compile_unit_metadata(cx: &CrateContext) { }); fn fallback_path(cx: &CrateContext) -> CString { - cx.link_meta.crate_name.as_slice().to_c_str() + cx.link_meta().crate_name.as_slice().to_c_str() } } @@ -1643,7 +1643,7 @@ fn scope_metadata(fcx: &FunctionContext, match scope_map.borrow().find_copy(&node_id) { Some(scope_metadata) => scope_metadata, None => { - let node = fcx.ccx.tcx.map.get(node_id); + let node = fcx.ccx.tcx().map.get(node_id); fcx.ccx.sess().span_bug(span, format!("debuginfo: Could not find scope info for node {:?}", @@ -2440,9 +2440,9 @@ fn prepare_enum_metadata(cx: &CrateContext, def_id: ast::DefId) -> token::InternedString { let name = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx.map.get_path_elem(def_id.node).name() + cx.tcx().map.get_path_elem(def_id.node).name() } else { - csearch::get_item_path(&cx.tcx, def_id).last().unwrap().name() + csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name() }; token::get_name(name) @@ -2685,7 +2685,7 @@ fn at_box_metadata(cx: &CrateContext, content_llvm_type: Type) -> bool { member_llvm_types.len() == 5 && - member_llvm_types[0] == cx.int_type && + member_llvm_types[0] == cx.int_type() && member_llvm_types[1] == Type::generic_glue_fn(cx).ptr_to() && member_llvm_types[2] == Type::i8(cx).ptr_to() && member_llvm_types[3] == Type::i8(cx).ptr_to() && @@ -2787,7 +2787,7 @@ fn vec_slice_metadata(cx: &CrateContext, -> bool { member_llvm_types.len() == 2 && member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && - member_llvm_types[1] == cx.int_type + member_llvm_types[1] == cx.int_type() } } @@ -3090,7 +3090,7 @@ fn set_debug_location(cx: &CrateContext, debug_location: DebugLocation) { }; unsafe { - llvm::LLVMSetCurrentDebugLocation(cx.builder.b, metadata_node); + llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); } debug_context(cx).current_debug_location.set(debug_location); @@ -3125,14 +3125,14 @@ fn bytes_to_bits(bytes: u64) -> c_ulonglong { #[inline] fn debug_context<'a>(cx: &'a CrateContext) -> &'a CrateDebugContext { - let debug_context: &'a CrateDebugContext = cx.dbg_cx.get_ref(); + let debug_context: &'a CrateDebugContext = cx.dbg_cx().get_ref(); debug_context } #[inline] #[allow(non_snake_case)] fn DIB(cx: &CrateContext) -> DIBuilderRef { - cx.dbg_cx.get_ref().builder + cx.dbg_cx().get_ref().builder } fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { @@ -3143,7 +3143,7 @@ fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { } fn assert_type_for_node_id(cx: &CrateContext, node_id: ast::NodeId, error_span: Span) { - if !cx.tcx.node_types.borrow().contains_key(&(node_id as uint)) { + if !cx.tcx().node_types.borrow().contains_key(&(node_id as uint)) { cx.sess().span_bug(error_span, "debuginfo: Could not find type for node id!"); } } @@ -3152,7 +3152,7 @@ fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) -> (DIScope, Span) { let containing_scope = namespace_for_item(cx, def_id).scope; let definition_span = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx.map.span(def_id.node) + cx.tcx().map.span(def_id.node) } else { // For external items there is no span information codemap::DUMMY_SP @@ -3173,7 +3173,7 @@ fn populate_scope_map(cx: &CrateContext, fn_entry_block: &ast::Block, fn_metadata: DISubprogram, scope_map: &mut HashMap) { - let def_map = &cx.tcx.def_map; + let def_map = &cx.tcx().def_map; struct ScopeStackEntry { scope_metadata: DIScope, @@ -3290,7 +3290,7 @@ fn populate_scope_map(cx: &CrateContext, scope_stack: &mut Vec , scope_map: &mut HashMap) { - let def_map = &cx.tcx.def_map; + let def_map = &cx.tcx().def_map; // Unfortunately, we cannot just use pat_util::pat_bindings() or // ast_util::walk_pat() here because we have to visit *all* nodes in @@ -3942,7 +3942,7 @@ impl NamespaceTreeNode { } fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str { - cx.link_meta.crate_name.as_slice() + cx.link_meta().crate_name.as_slice() } fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 4fe687da4b194..fcb0ac9d9306f 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -94,7 +94,7 @@ pub enum Dest { impl Dest { pub fn to_string(&self, ccx: &CrateContext) -> String { match *self { - SaveIn(v) => format!("SaveIn({})", ccx.tn.val_to_string(v)), + SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)), Ignore => "Ignore".to_string() } } @@ -711,7 +711,7 @@ fn trans_index<'a>(bcx: &'a Block<'a>, let mut bcx = bcx; // Check for overloaded index. - let method_ty = ccx.tcx + let method_ty = ccx.tcx() .method_map .borrow() .find(&method_call) @@ -758,14 +758,14 @@ fn trans_index<'a>(bcx: &'a Block<'a>, let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val)); let int_size = machine::llbitsize_of_real(bcx.ccx(), - ccx.int_type); + ccx.int_type()); let ix_val = { if ix_size < int_size { if ty::type_is_signed(expr_ty(bcx, idx)) { - SExt(bcx, ix_val, ccx.int_type) - } else { ZExt(bcx, ix_val, ccx.int_type) } + SExt(bcx, ix_val, ccx.int_type()) + } else { ZExt(bcx, ix_val, ccx.int_type()) } } else if ix_size > int_size { - Trunc(bcx, ix_val, ccx.int_type) + Trunc(bcx, ix_val, ccx.int_type()) } else { ix_val } @@ -839,7 +839,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 { - match bcx.ccx().extern_const_values.borrow().find(&did) { + match bcx.ccx().extern_const_values().borrow().find(&did) { None => {} // Continue. Some(llval) => { return *llval; @@ -852,11 +852,11 @@ fn trans_def<'a>(bcx: &'a Block<'a>, &bcx.ccx().sess().cstore, did); let llval = symbol.as_slice().with_c_str(|buf| { - llvm::LLVMAddGlobal(bcx.ccx().llmod, + llvm::LLVMAddGlobal(bcx.ccx().llmod(), llty.to_ref(), buf) }); - bcx.ccx().extern_const_values.borrow_mut() + bcx.ccx().extern_const_values().borrow_mut() .insert(did, llval); llval } @@ -1439,7 +1439,7 @@ fn trans_unary<'a>(bcx: &'a Block<'a>, // Otherwise, we should be in the RvalueDpsExpr path. assert!( op == ast::UnDeref || - !ccx.tcx.method_map.borrow().contains_key(&method_call)); + !ccx.tcx().method_map.borrow().contains_key(&method_call)); let un_ty = expr_ty(bcx, expr); @@ -1706,7 +1706,7 @@ fn trans_binary<'a>(bcx: &'a Block<'a>, let ccx = bcx.ccx(); // if overloaded, would be RvalueDpsExpr - assert!(!ccx.tcx.method_map.borrow().contains_key(&MethodCall::expr(expr.id))); + assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); match op { ast::BiAnd => { @@ -2050,7 +2050,7 @@ fn deref_once<'a>(bcx: &'a Block<'a>, let mut bcx = bcx; // Check for overloaded deref. - let method_ty = ccx.tcx.method_map.borrow() + let method_ty = ccx.tcx().method_map.borrow() .find(&method_call).map(|method| method.ty); let datum = match method_ty { Some(method_ty) => { diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index fe9b593c11c7e..240c109e17bd3 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -160,14 +160,14 @@ pub fn register_static(ccx: &CrateContext, }; unsafe { let g1 = ident.get().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, llty2.to_ref(), buf) + llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(), buf) }); llvm::SetLinkage(g1, linkage); let mut real_name = "_rust_extern_with_linkage_".to_string(); real_name.push_str(ident.get()); let g2 = real_name.with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf) + llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) }); llvm::SetLinkage(g2, llvm::InternalLinkage); llvm::LLVMSetInitializer(g2, g1); @@ -176,7 +176,7 @@ pub fn register_static(ccx: &CrateContext, } None => unsafe { ident.get().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf) + llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) }) } } @@ -229,7 +229,7 @@ pub fn register_foreign_item_fn(ccx: &CrateContext, abi: Abi, fty: ty::t, let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); let llfn = base::get_extern_fn(ccx, - &mut *ccx.externs.borrow_mut(), + &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, @@ -271,8 +271,8 @@ pub fn trans_native_call<'a>( llfn={}, \ llretptr={})", callee_ty.repr(tcx), - ccx.tn.val_to_string(llfn), - ccx.tn.val_to_string(llretptr)); + ccx.tn().val_to_string(llfn), + ccx.tn().val_to_string(llretptr)); let (fn_abi, fn_sig) = match ty::get(callee_ty).sty { ty::ty_bare_fn(ref fn_ty) => (fn_ty.abi, fn_ty.sig.clone()), @@ -319,9 +319,9 @@ pub fn trans_native_call<'a>( debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}", i, - ccx.tn.val_to_string(llarg_rust), + ccx.tn().val_to_string(llarg_rust), rust_indirect, - ccx.tn.type_to_string(arg_tys[i].ty)); + ccx.tn().type_to_string(arg_tys[i].ty)); // Ensure that we always have the Rust value indirectly, // because it makes bitcasting easier. @@ -335,7 +335,7 @@ pub fn trans_native_call<'a>( } debug!("llarg_rust={} (after indirection)", - ccx.tn.val_to_string(llarg_rust)); + ccx.tn().val_to_string(llarg_rust)); // Check whether we need to do any casting match arg_tys[i].cast { @@ -344,7 +344,7 @@ pub fn trans_native_call<'a>( } debug!("llarg_rust={} (after casting)", - ccx.tn.val_to_string(llarg_rust)); + ccx.tn().val_to_string(llarg_rust)); // Finally, load the value if needed for the foreign ABI let foreign_indirect = arg_tys[i].is_indirect(); @@ -360,7 +360,7 @@ pub fn trans_native_call<'a>( }; debug!("argument {}, llarg_foreign={}", - i, ccx.tn.val_to_string(llarg_foreign)); + i, ccx.tn().val_to_string(llarg_foreign)); // fill padding with undef value match arg_tys[i].pad { @@ -438,10 +438,10 @@ pub fn trans_native_call<'a>( None => fn_type.ret_ty.ty }; - debug!("llretptr={}", ccx.tn.val_to_string(llretptr)); - debug!("llforeign_retval={}", ccx.tn.val_to_string(llforeign_retval)); - debug!("llrust_ret_ty={}", ccx.tn.type_to_string(llrust_ret_ty)); - debug!("llforeign_ret_ty={}", ccx.tn.type_to_string(llforeign_ret_ty)); + debug!("llretptr={}", ccx.tn().val_to_string(llretptr)); + debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval)); + debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty)); + debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty)); if llrust_ret_ty == llforeign_ret_ty { base::store_ty(bcx, llforeign_retval, llretptr, fn_sig.output) @@ -496,7 +496,7 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { _ => {} } - ccx.item_symbols.borrow_mut().insert(foreign_item.id, + ccx.item_symbols().borrow_mut().insert(foreign_item.id, lname.get().to_string()); } } @@ -542,7 +542,7 @@ pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext, let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::mk_nil()); add_argument_attributes(&tys, llfn); debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", - ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn)); + ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); llfn } @@ -566,7 +566,7 @@ pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext, let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty); add_argument_attributes(&tys, llfn); debug!("register_rust_fn_with_foreign_abi(node_id={:?}, llfn_ty={}, llfn={})", - node_id, ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn)); + node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); llfn } @@ -605,7 +605,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, let t = ty::node_id_to_type(tcx, id).subst( ccx.tcx(), ¶m_substs.substs); - let ps = ccx.tcx.map.with_path(id, |path| { + let ps = ccx.tcx().map.with_path(id, |path| { let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name)); link::mangle(path.chain(abi.move_iter()), hash) }); @@ -619,13 +619,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, _ => { ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \ expected a bare fn ty", - ccx.tcx.map.path_to_string(id), + ccx.tcx().map.path_to_string(id), t.repr(tcx)).as_slice()); } }; debug!("build_rust_fn: path={} id={} t={}", - ccx.tcx.map.path_to_string(id), + ccx.tcx().map.path_to_string(id), id, t.repr(tcx)); let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice()); @@ -644,8 +644,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, let tcx = ccx.tcx(); debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})", - ccx.tn.val_to_string(llrustfn), - ccx.tn.val_to_string(llwrapfn), + ccx.tn().val_to_string(llrustfn), + ccx.tn().val_to_string(llwrapfn), t.repr(ccx.tcx())); // Avoid all the Rust generation stuff and just generate raw @@ -661,7 +661,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, let the_block = "the block".with_c_str( - |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llwrapfn, s)); + |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, s)); let builder = ccx.builder(); builder.position_at_end(the_block); @@ -702,11 +702,11 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, match foreign_outptr { Some(llforeign_outptr) => { debug!("out pointer, foreign={}", - ccx.tn.val_to_string(llforeign_outptr)); + ccx.tn().val_to_string(llforeign_outptr)); let llrust_retptr = builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); debug!("out pointer, foreign={} (casted)", - ccx.tn.val_to_string(llrust_retptr)); + ccx.tn().val_to_string(llrust_retptr)); llrust_args.push(llrust_retptr); return_alloca = None; } @@ -717,8 +717,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, allocad={}, \ llrust_ret_ty={}, \ return_ty={}", - ccx.tn.val_to_string(slot), - ccx.tn.type_to_string(llrust_ret_ty), + ccx.tn().val_to_string(slot), + ccx.tn().type_to_string(llrust_ret_ty), tys.fn_sig.output.repr(tcx)); llrust_args.push(slot); return_alloca = Some(slot); @@ -752,7 +752,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, let mut llforeign_arg = get_param(llwrapfn, foreign_index); debug!("llforeign_arg {}{}: {}", "#", - i, ccx.tn.val_to_string(llforeign_arg)); + i, ccx.tn().val_to_string(llforeign_arg)); debug!("rust_indirect = {}, foreign_indirect = {}", rust_indirect, foreign_indirect); @@ -791,12 +791,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, }; debug!("llrust_arg {}{}: {}", "#", - i, ccx.tn.val_to_string(llrust_arg)); + i, ccx.tn().val_to_string(llrust_arg)); llrust_args.push(llrust_arg); } // Perform the call itself - debug!("calling llrustfn = {}, t = {}", ccx.tn.val_to_string(llrustfn), t.repr(ccx.tcx())); + debug!("calling llrustfn = {}, t = {}", + ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx())); let attributes = base::get_fn_llvm_attributes(ccx, t); let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes)); @@ -915,10 +916,10 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext, fn_ty={} -> {}, \ ret_def={}", ty.repr(ccx.tcx()), - ccx.tn.types_to_str(llsig.llarg_tys.as_slice()), - ccx.tn.type_to_string(llsig.llret_ty), - ccx.tn.types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::>().as_slice()), - ccx.tn.type_to_string(fn_ty.ret_ty.ty), + ccx.tn().types_to_str(llsig.llarg_tys.as_slice()), + ccx.tn().type_to_string(llsig.llret_ty), + ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::>().as_slice()), + ccx.tn().type_to_string(fn_ty.ret_ty.ty), ret_def); ForeignTypes { diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 51cd9a115cd7c..915c171b31816 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -159,7 +159,7 @@ pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { debug!("make drop glue for {}", ppaux::ty_to_string(ccx.tcx(), t)); let t = get_drop_glue_type(ccx, t); debug!("drop glue type {}", ppaux::ty_to_string(ccx.tcx(), t)); - match ccx.drop_glues.borrow().find(&t) { + match ccx.drop_glues().borrow().find(&t) { Some(&glue) => return glue, _ => { } } @@ -173,7 +173,7 @@ pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { let llfnty = Type::glue_fn(ccx, llty); let glue = declare_generic_glue(ccx, t, llfnty, "drop"); - ccx.drop_glues.borrow_mut().insert(t, glue); + ccx.drop_glues().borrow_mut().insert(t, glue); make_generic_glue(ccx, t, glue, make_drop_glue, "drop"); @@ -566,7 +566,7 @@ fn incr_refcnt_of_boxed<'a>(bcx: &'a Block<'a>, pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info { // If emit_tydescs already ran, then we shouldn't be creating any new // tydescs. - assert!(!ccx.finished_tydescs.get()); + assert!(!ccx.finished_tydescs().get()); let llty = type_of(ccx, t); @@ -581,7 +581,7 @@ pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info { debug!("+++ declare_tydesc {} {}", ppaux::ty_to_string(ccx.tcx(), t), name); let gvar = name.as_slice().with_c_str(|buf| { unsafe { - llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type().to_ref(), buf) + llvm::LLVMAddGlobal(ccx.llmod(), ccx.tydesc_type().to_ref(), buf) } }); note_unique_llvm_symbol(ccx, name); @@ -632,7 +632,7 @@ fn make_generic_glue(ccx: &CrateContext, let bcx = init_function(&fcx, false, ty::mk_nil()); llvm::SetLinkage(llfn, llvm::InternalLinkage); - ccx.stats.n_glues_created.set(ccx.stats.n_glues_created.get() + 1u); + ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u); // All glue functions take values passed *by alias*; this is a // requirement since in many contexts glue is invoked indirectly and // the caller has no idea if it's dealing with something that can be @@ -651,9 +651,9 @@ fn make_generic_glue(ccx: &CrateContext, pub fn emit_tydescs(ccx: &CrateContext) { let _icx = push_ctxt("emit_tydescs"); // As of this point, allow no more tydescs to be created. - ccx.finished_tydescs.set(true); + ccx.finished_tydescs().set(true); let glue_fn_ty = Type::generic_glue_fn(ccx).ptr_to(); - for (_, ti) in ccx.tydescs.borrow().iter() { + for (_, ti) in ccx.tydescs().borrow().iter() { // Each of the glue functions needs to be cast to a generic type // before being put into the tydesc because we only have a singleton // tydesc type. Then we'll recast each function to its real type when @@ -661,17 +661,17 @@ pub fn emit_tydescs(ccx: &CrateContext) { let drop_glue = unsafe { llvm::LLVMConstPointerCast(get_drop_glue(ccx, ti.ty), glue_fn_ty.to_ref()) }; - ccx.stats.n_real_glues.set(ccx.stats.n_real_glues.get() + 1); + ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() + 1); let visit_glue = match ti.visit_glue.get() { None => { - ccx.stats.n_null_glues.set(ccx.stats.n_null_glues.get() + + ccx.stats().n_null_glues.set(ccx.stats().n_null_glues.get() + 1u); C_null(glue_fn_ty) } Some(v) => { unsafe { - ccx.stats.n_real_glues.set(ccx.stats.n_real_glues.get() + + ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() + 1); llvm::LLVMConstPointerCast(v, glue_fn_ty.to_ref()) } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index 4b1f37fcdc22a..e8a56f4a9265d 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -22,7 +22,7 @@ use syntax::ast_util; pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) -> ast::DefId { let _icx = push_ctxt("maybe_instantiate_inline"); - match ccx.external.borrow().find(&fn_id) { + match ccx.external().borrow().find(&fn_id) { Some(&Some(node_id)) => { // Already inline debug!("maybe_instantiate_inline({}): already inline as node id {}", @@ -43,14 +43,14 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) |a,b,c,d| astencode::decode_inlined_item(a, b, c, d)); return match csearch_result { csearch::not_found => { - ccx.external.borrow_mut().insert(fn_id, None); + ccx.external().borrow_mut().insert(fn_id, None); fn_id } csearch::found(ast::IIItem(item)) => { - ccx.external.borrow_mut().insert(fn_id, Some(item.id)); - ccx.external_srcs.borrow_mut().insert(item.id, fn_id); + ccx.external().borrow_mut().insert(fn_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, fn_id); - ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1); + 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 @@ -78,13 +78,13 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) local_def(item.id) } csearch::found(ast::IIForeign(item)) => { - ccx.external.borrow_mut().insert(fn_id, Some(item.id)); - ccx.external_srcs.borrow_mut().insert(item.id, fn_id); + ccx.external().borrow_mut().insert(fn_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, fn_id); local_def(item.id) } csearch::found_parent(parent_id, ast::IIItem(item)) => { - ccx.external.borrow_mut().insert(parent_id, Some(item.id)); - ccx.external_srcs.borrow_mut().insert(item.id, parent_id); + ccx.external().borrow_mut().insert(parent_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, parent_id); let mut my_id = 0; match item.node { @@ -93,14 +93,14 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) let vs_there = ty::enum_variants(ccx.tcx(), parent_id); for (here, there) in vs_here.iter().zip(vs_there.iter()) { if there.id == fn_id { my_id = here.id.node; } - ccx.external.borrow_mut().insert(there.id, Some(here.id.node)); + ccx.external().borrow_mut().insert(there.id, Some(here.id.node)); } } ast::ItemStruct(ref struct_def, _) => { match struct_def.ctor_id { None => {} Some(ctor_id) => { - ccx.external.borrow_mut().insert(fn_id, Some(ctor_id)); + ccx.external().borrow_mut().insert(fn_id, Some(ctor_id)); my_id = ctor_id; } } @@ -119,10 +119,10 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) match impl_item { ast::ProvidedInlinedTraitItem(mth) | ast::RequiredInlinedTraitItem(mth) => { - ccx.external.borrow_mut().insert(fn_id, Some(mth.id)); - ccx.external_srcs.borrow_mut().insert(mth.id, fn_id); + ccx.external().borrow_mut().insert(fn_id, Some(mth.id)); + ccx.external_srcs().borrow_mut().insert(mth.id, fn_id); - ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1); + ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); } } diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 3c61708fb7b83..f10df00ca9184 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -89,7 +89,7 @@ pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Opti /// Performs late verification that intrinsics are used correctly. At present, /// the only intrinsic that needs such verification is `transmute`. pub fn check_intrinsics(ccx: &CrateContext) { - for transmute_restriction in ccx.tcx + for transmute_restriction in ccx.tcx() .transmute_restrictions .borrow() .iter() { @@ -276,7 +276,7 @@ pub fn trans_intrinsic_call<'a>(mut bcx: &'a Block<'a>, node: ast::NodeId, let hash = ty::hash_crate_independent( ccx.tcx(), *substs.types.get(FnSpace, 0), - &ccx.link_meta.crate_hash); + &ccx.link_meta().crate_hash); // NB: This needs to be kept in lockstep with the TypeId struct in // the intrinsic module C_named_struct(llret_ty, [C_u64(ccx, hash)]) @@ -554,7 +554,7 @@ fn copy_intrinsic(bcx: &Block, allow_overlap: bool, volatile: bool, let lltp_ty = type_of::type_of(ccx, tp_ty); let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); let size = machine::llsize_of(ccx, lltp_ty); - let int_size = machine::llbitsize_of_real(ccx, ccx.int_type); + let int_size = machine::llbitsize_of_real(ccx, ccx.int_type()); let name = if allow_overlap { if int_size == 32 { "llvm.memmove.p0i8.p0i8.i32" @@ -583,7 +583,7 @@ fn memset_intrinsic(bcx: &Block, volatile: bool, tp_ty: ty::t, let lltp_ty = type_of::type_of(ccx, tp_ty); let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); let size = machine::llsize_of(ccx, lltp_ty); - let name = if machine::llbitsize_of_real(ccx, ccx.int_type) == 32 { + let name = if machine::llbitsize_of_real(ccx, ccx.int_type()) == 32 { "llvm.memset.p0i8.i32" } else { "llvm.memset.p0i8.i64" diff --git a/src/librustc/middle/trans/llrepr.rs b/src/librustc/middle/trans/llrepr.rs index 2740e5695be10..5aec1cfbf268a 100644 --- a/src/librustc/middle/trans/llrepr.rs +++ b/src/librustc/middle/trans/llrepr.rs @@ -25,13 +25,13 @@ impl<'a, T:LlvmRepr> LlvmRepr for &'a [T] { impl LlvmRepr for Type { fn llrepr(&self, ccx: &CrateContext) -> String { - ccx.tn.type_to_string(*self) + ccx.tn().type_to_string(*self) } } impl LlvmRepr for ValueRef { fn llrepr(&self, ccx: &CrateContext) -> String { - ccx.tn.val_to_string(*self) + ccx.tn().val_to_string(*self) } } diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs index 15bbc5ae84589..938cbc2176468 100644 --- a/src/librustc/middle/trans/machine.rs +++ b/src/librustc/middle/trans/machine.rs @@ -23,7 +23,7 @@ use middle::trans::type_::Type; // Returns the number of bytes clobbered by a Store to this type. pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> u64 { unsafe { - return llvm::LLVMStoreSizeOfType(cx.td.lltd, ty.to_ref()) as u64; + return llvm::LLVMStoreSizeOfType(cx.td().lltd, ty.to_ref()) as u64; } } @@ -31,7 +31,7 @@ pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> u64 { // array of T. This is the "ABI" size. It includes any ABI-mandated padding. pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> u64 { unsafe { - return llvm::LLVMABISizeOfType(cx.td.lltd, ty.to_ref()) as u64; + return llvm::LLVMABISizeOfType(cx.td().lltd, ty.to_ref()) as u64; } } @@ -45,7 +45,7 @@ pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> u64 { // below. pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> u64 { unsafe { - let nbits = llvm::LLVMSizeOfTypeInBits(cx.td.lltd, ty.to_ref()) as u64; + let nbits = llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) as u64; if nbits & 7 != 0 { // Not an even number of bytes, spills into "next" byte. 1 + (nbits >> 3) @@ -58,7 +58,7 @@ pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> u64 { /// Returns the "real" size of the type in bits. pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> u64 { unsafe { - llvm::LLVMSizeOfTypeInBits(cx.td.lltd, ty.to_ref()) as u64 + llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) as u64 } } @@ -79,7 +79,7 @@ pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { // space to be consumed. pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { if llbitsize_of_real(cx, ty) == 0 { - unsafe { llvm::LLVMConstInt(cx.int_type.to_ref(), 1, False) } + unsafe { llvm::LLVMConstInt(cx.int_type().to_ref(), 1, False) } } else { llsize_of(cx, ty) } @@ -91,7 +91,7 @@ pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { // allocations inside a stack frame, which LLVM has a free hand in. pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> u64 { unsafe { - return llvm::LLVMPreferredAlignmentOfType(cx.td.lltd, ty.to_ref()) as u64; + return llvm::LLVMPreferredAlignmentOfType(cx.td().lltd, ty.to_ref()) as u64; } } @@ -100,7 +100,7 @@ pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> u64 { // and similar ABI-mandated things. pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> u64 { unsafe { - return llvm::LLVMABIAlignmentOfType(cx.td.lltd, ty.to_ref()) as u64; + return llvm::LLVMABIAlignmentOfType(cx.td().lltd, ty.to_ref()) as u64; } } @@ -110,12 +110,12 @@ pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> u64 { pub fn llalign_of(cx: &CrateContext, ty: Type) -> ValueRef { unsafe { return llvm::LLVMConstIntCast( - llvm::LLVMAlignOf(ty.to_ref()), cx.int_type.to_ref(), False); + llvm::LLVMAlignOf(ty.to_ref()), cx.int_type().to_ref(), False); } } pub fn llelement_offset(cx: &CrateContext, struct_ty: Type, element: uint) -> u64 { unsafe { - return llvm::LLVMOffsetOfElement(cx.td.lltd, struct_ty.to_ref(), element as u32) as u64; + return llvm::LLVMOffsetOfElement(cx.td().lltd, struct_ty.to_ref(), element as u32) as u64; } } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 83bdcc9dead64..9c587d08f01c1 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -196,7 +196,7 @@ pub fn trans_static_method_callee(bcx: &Block, let vtable_key = MethodCall::expr(expr_id); let vtbls = resolve_vtables_in_fn_ctxt( bcx.fcx, - ccx.tcx.vtable_map.borrow().get(&vtable_key)); + ccx.tcx().vtable_map.borrow().get(&vtable_key)); match *vtbls.get_self().unwrap().get(0) { typeck::vtable_static(impl_did, ref rcvr_substs, ref rcvr_origins) => { @@ -228,12 +228,12 @@ pub fn trans_static_method_callee(bcx: &Block, fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name) -> ast::DefId { - match ccx.impl_method_cache.borrow().find_copy(&(impl_id, name)) { + match ccx.impl_method_cache().borrow().find_copy(&(impl_id, name)) { Some(m) => return m, None => {} } - let impl_items = ccx.tcx.impl_items.borrow(); + let impl_items = ccx.tcx().impl_items.borrow(); let impl_items = impl_items.find(&impl_id) .expect("could not find impl while translating"); @@ -241,7 +241,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name) .find(|&did| { match *did { ty::MethodTraitItemId(did) => { - ty::impl_or_trait_item(&ccx.tcx, + ty::impl_or_trait_item(ccx.tcx(), did).ident() .name == name @@ -250,7 +250,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name) }).expect("could not find method while \ translating"); - ccx.impl_method_cache.borrow_mut().insert((impl_id, name), + ccx.impl_method_cache().borrow_mut().insert((impl_id, name), meth_did.def_id()); meth_did.def_id() } @@ -502,7 +502,7 @@ fn get_vtable(bcx: &Block, // Check the cache. let hash_id = (self_ty, monomorphize::make_vtable_id(ccx, origins.get(0))); - match ccx.vtables.borrow().find(&hash_id) { + match ccx.vtables().borrow().find(&hash_id) { Some(&val) => { return val } None => { } } @@ -594,7 +594,7 @@ fn get_vtable(bcx: &Block, let drop_glue = glue::get_drop_glue(ccx, self_ty); let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods); - ccx.vtables.borrow_mut().insert(hash_id, vtable); + ccx.vtables().borrow_mut().insert(hash_id, vtable); vtable } @@ -614,7 +614,7 @@ pub fn make_vtable>(ccx: &CrateContext, let tbl = C_struct(ccx, components.as_slice(), false); let sym = token::gensym("vtable"); let vt_gvar = format!("vtable{}", sym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl).to_ref(), buf) + llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), buf) }); llvm::LLVMSetInitializer(vt_gvar, tbl); llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True); @@ -684,9 +684,9 @@ pub fn vtable_ptr<'a>(bcx: &'a Block<'a>, self_ty: ty::t) -> ValueRef { let ccx = bcx.ccx(); let origins = { - let vtable_map = ccx.tcx.vtable_map.borrow(); + let vtable_map = ccx.tcx().vtable_map.borrow(); // This trait cast might be because of implicit coercion - let adjs = ccx.tcx.adjustments.borrow(); + let adjs = ccx.tcx().adjustments.borrow(); let adjust = adjs.find(&id); let method_call = if adjust.is_some() && ty::adjust_is_object(adjust.unwrap()) { MethodCall::autoobject(id) diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index f2a7f1dc4f8ee..16d1ad810b775 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -56,7 +56,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, params: real_substs.types.clone() }; - match ccx.monomorphized.borrow().find(&hash_id) { + match ccx.monomorphized().borrow().find(&hash_id) { Some(&val) => { debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id)); @@ -83,7 +83,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, let map_node = session::expect( ccx.sess(), - ccx.tcx.map.find(fn_id.node), + ccx.tcx().map.find(fn_id.node), || { format!("while monomorphizing {:?}, couldn't find it in \ the item map (may have attempted to monomorphize \ @@ -93,7 +93,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, match map_node { ast_map::NodeForeignItem(_) => { - if ccx.tcx.map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic { + if ccx.tcx().map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic { // Foreign externs don't have to be monomorphized. return (get_item_val(ccx, fn_id.node), true); } @@ -104,11 +104,11 @@ pub fn monomorphic_fn(ccx: &CrateContext, debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx())); let mono_ty = llitem_ty.subst(ccx.tcx(), real_substs); - ccx.stats.n_monos.set(ccx.stats.n_monos.get() + 1); + ccx.stats().n_monos.set(ccx.stats().n_monos.get() + 1); let depth; { - let mut monomorphizing = ccx.monomorphizing.borrow_mut(); + let mut monomorphizing = ccx.monomorphizing().borrow_mut(); depth = match monomorphizing.find(&fn_id) { Some(&d) => d, None => 0 }; @@ -117,7 +117,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, // recursively more than thirty times can probably safely be assumed // to be causing an infinite expansion. if depth > ccx.sess().recursion_limit.get() { - ccx.sess().span_fatal(ccx.tcx.map.span(fn_id.node), + ccx.sess().span_fatal(ccx.tcx().map.span(fn_id.node), "reached the recursion limit during monomorphization"); } @@ -131,7 +131,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, mono_ty.hash(&mut state); hash = format!("h{}", state.result()); - ccx.tcx.map.with_path(fn_id.node, |path| { + ccx.tcx().map.with_path(fn_id.node, |path| { exported_name(path, hash.as_slice()) }) }; @@ -147,7 +147,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, decl_internal_rust_fn(ccx, mono_ty, s.as_slice()) }; - ccx.monomorphized.borrow_mut().insert(hash_id.take().unwrap(), lldecl); + ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); lldecl }; @@ -177,7 +177,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, } } ast_map::NodeVariant(v) => { - let parent = ccx.tcx.map.get_parent(fn_id.node); + let parent = ccx.tcx().map.get_parent(fn_id.node); let tvs = ty::enum_variants(ccx.tcx(), local_def(parent)); let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap(); let d = mk_lldecl(abi::Rust); @@ -254,7 +254,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, } }; - ccx.monomorphizing.borrow_mut().insert(fn_id, depth); + ccx.monomorphizing().borrow_mut().insert(fn_id, depth); debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id)); (lldecl, true) diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index a49dc1b915028..1fcf4c189129b 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -335,7 +335,7 @@ impl<'a, 'b> Reflector<'a, 'b> { let sym = mangle_internal_name_by_path_and_seq( ast_map::Values([].iter()).chain(None), "get_disr"); - let fn_ty = ty::mk_ctor_fn(&ccx.tcx, ast::DUMMY_NODE_ID, + let fn_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID, [opaqueptrty], ty::mk_u64()); let llfdecl = decl_internal_rust_fn(ccx, fn_ty, diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index a6ba758d5f810..3701d83d6a122 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -94,8 +94,8 @@ impl VecTypes { format!("VecTypes {{unit_ty={}, llunit_ty={}, \ llunit_size={}, llunit_alloc_size={}}}", ty_to_string(ccx.tcx(), self.unit_ty), - ccx.tn.type_to_string(self.llunit_ty), - ccx.tn.val_to_string(self.llunit_size), + ccx.tn().type_to_string(self.llunit_ty), + ccx.tn().val_to_string(self.llunit_size), self.llunit_alloc_size) } } @@ -546,7 +546,7 @@ pub fn iter_vec_loop<'r, let loop_counter = { // i = 0 - let i = alloca(loop_bcx, bcx.ccx().int_type, "__i"); + let i = alloca(loop_bcx, bcx.ccx().int_type(), "__i"); Store(loop_bcx, C_uint(bcx.ccx(), 0), i); Br(loop_bcx, cond_bcx.llbb); diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index 7b98d65a3105b..3df1ce32fc7d7 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -53,7 +53,7 @@ impl Type { } pub fn void(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMVoidTypeInContext(ccx.llcx)) + ty!(llvm::LLVMVoidTypeInContext(ccx.llcx())) } pub fn nil(ccx: &CrateContext) -> Type { @@ -61,35 +61,35 @@ impl Type { } pub fn metadata(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx)) + ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx())) } pub fn i1(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt1TypeInContext(ccx.llcx)) + ty!(llvm::LLVMInt1TypeInContext(ccx.llcx())) } pub fn i8(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt8TypeInContext(ccx.llcx)) + ty!(llvm::LLVMInt8TypeInContext(ccx.llcx())) } pub fn i16(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt16TypeInContext(ccx.llcx)) + ty!(llvm::LLVMInt16TypeInContext(ccx.llcx())) } pub fn i32(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt32TypeInContext(ccx.llcx)) + ty!(llvm::LLVMInt32TypeInContext(ccx.llcx())) } pub fn i64(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt64TypeInContext(ccx.llcx)) + ty!(llvm::LLVMInt64TypeInContext(ccx.llcx())) } pub fn f32(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMFloatTypeInContext(ccx.llcx)) + ty!(llvm::LLVMFloatTypeInContext(ccx.llcx())) } pub fn f64(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx)) + ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx())) } pub fn bool(ccx: &CrateContext) -> Type { @@ -105,7 +105,7 @@ impl Type { } pub fn int(ccx: &CrateContext) -> Type { - match ccx.tcx.sess.targ_cfg.arch { + match ccx.tcx().sess.targ_cfg.arch { X86 | Arm | Mips | Mipsel => Type::i32(ccx), X86_64 => Type::i64(ccx) } @@ -113,7 +113,7 @@ impl Type { pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type { match t { - ast::TyI => ccx.int_type, + ast::TyI => ccx.int_type(), ast::TyI8 => Type::i8(ccx), ast::TyI16 => Type::i16(ccx), ast::TyI32 => Type::i32(ccx), @@ -123,7 +123,7 @@ impl Type { pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type { match t { - ast::TyU => ccx.int_type, + ast::TyU => ccx.int_type(), ast::TyU8 => Type::i8(ccx), ast::TyU16 => Type::i16(ccx), ast::TyU32 => Type::i32(ccx), @@ -152,13 +152,13 @@ impl Type { pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type { let els : &[TypeRef] = unsafe { mem::transmute(els) }; - ty!(llvm::LLVMStructTypeInContext(ccx.llcx, els.as_ptr(), + ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(), els.len() as c_uint, packed as Bool)) } pub fn named_struct(ccx: &CrateContext, name: &str) -> Type { - ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx, s))) + ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx(), s))) } pub fn empty_struct(ccx: &CrateContext) -> Type { @@ -170,13 +170,13 @@ impl Type { } pub fn generic_glue_fn(cx: &CrateContext) -> Type { - match cx.tn.find_type("glue_fn") { + match cx.tn().find_type("glue_fn") { Some(ty) => return ty, None => () } let ty = Type::glue_fn(cx, Type::i8p(cx)); - cx.tn.associate_type("glue_fn", &ty); + cx.tn().associate_type("glue_fn", &ty); ty } @@ -226,7 +226,7 @@ impl Type { // The box pointed to by @T. pub fn at_box(ccx: &CrateContext, ty: Type) -> Type { Type::struct_(ccx, [ - ccx.int_type, Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(), + ccx.int_type(), Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(), Type::i8p(ccx), Type::i8p(ccx), ty ], false) } diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index ab7e71c41d368..54f24516867f0 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -156,7 +156,7 @@ pub fn type_of_fn_from_ty(cx: &CrateContext, fty: ty::t) -> Type { // recursive types. For example, enum types rely on this behavior. pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { - match cx.llsizingtypes.borrow().find_copy(&t) { + match cx.llsizingtypes().borrow().find_copy(&t) { Some(t) => return t, None => () } @@ -217,7 +217,7 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { ty::ty_vec(_, None) | ty::ty_trait(..) | ty::ty_str => fail!("unreachable") }; - cx.llsizingtypes.borrow_mut().insert(t, llsizingty); + cx.llsizingtypes().borrow_mut().insert(t, llsizingty); llsizingty } @@ -249,7 +249,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { } // Check the cache. - match cx.lltypes.borrow().find(&t) { + match cx.lltypes().borrow().find(&t) { Some(&llty) => return llty, None => () } @@ -270,8 +270,8 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { t, t_norm.repr(cx.tcx()), t_norm, - cx.tn.type_to_string(llty)); - cx.lltypes.borrow_mut().insert(t, llty); + cx.tn().type_to_string(llty)); + cx.lltypes().borrow_mut().insert(t, llty); return llty; } @@ -308,7 +308,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { ty::ty_str => { // This means we get a nicer name in the output (str is always // unsized). - cx.tn.find_type("str_slice").unwrap() + cx.tn().find_type("str_slice").unwrap() } ty::ty_trait(..) => Type::opaque_trait(cx), _ if !ty::type_is_sized(cx.tcx(), ty) => { @@ -385,9 +385,9 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { debug!("--> mapped t={} {:?} to llty={}", t.repr(cx.tcx()), t, - cx.tn.type_to_string(llty)); + cx.tn().type_to_string(llty)); - cx.lltypes.borrow_mut().insert(t, llty); + cx.lltypes().borrow_mut().insert(t, llty); // If this was an enum or struct, fill in the type now. match ty::get(t).sty { From 0ab27b1d5b68d3bc3c727895e5219ce254c685ad Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Wed, 16 Jul 2014 11:27:57 -0700 Subject: [PATCH 02/14] split CrateContext into shared and local pieces Break up `CrateContext` into `SharedCrateContext` and `LocalCrateContext`. The local piece corresponds to a single compilation unit, and contains all LLVM-related components. (LLVM data structures are tied to a specific `LLVMContext`, and we will need separate `LLVMContext`s to safely run multithreaded optimization.) The shared piece contains data structures that need to be shared across all compilation units, such as the `ty::ctxt` and some tables related to crate metadata. --- src/librustc/back/link.rs | 1 + src/librustc/driver/driver.rs | 1 + src/librustc/middle/trans/base.rs | 108 ++++---- src/librustc/middle/trans/builder.rs | 2 +- src/librustc/middle/trans/common.rs | 4 +- src/librustc/middle/trans/context.rs | 393 ++++++++++++++++++--------- 6 files changed, 327 insertions(+), 182 deletions(-) diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index d9c05f9a76da9..cb71e5983235d 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -405,6 +405,7 @@ pub mod write { llvm::LLVMRustDisposeTargetMachine(tm); llvm::LLVMDisposeModule(trans.metadata_module); + llvm::LLVMContextDispose(trans.metadata_context); llvm::LLVMDisposeModule(llmod); llvm::LLVMContextDispose(llcx); if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 3d0678aa0e7ac..5e9d8da5b4edb 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -444,6 +444,7 @@ pub fn phase_save_analysis(sess: &Session, pub struct CrateTranslation { pub context: ContextRef, pub module: ModuleRef, + pub metadata_context: ContextRef, pub metadata_module: ModuleRef, pub link: LinkMeta, pub metadata: Vec, diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 78488654c510c..dc8dabda83438 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -56,6 +56,7 @@ use middle::trans::common::{tydesc_info, type_is_immediate}; use middle::trans::common::{type_is_zero_size, val_ty}; use middle::trans::common; use middle::trans::consts; +use middle::trans::context::SharedCrateContext; use middle::trans::controlflow; use middle::trans::datum; use middle::trans::debuginfo; @@ -136,7 +137,7 @@ pub fn push_ctxt(s: &'static str) -> _InsnCtxt { } pub struct StatRecorder<'a> { - ccx: &'a CrateContext, + ccx: &'a CrateContext<'a>, name: Option, start: u64, istart: uint, @@ -2114,7 +2115,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, } pub struct TransItemVisitor<'a> { - pub ccx: &'a CrateContext, + pub ccx: &'a CrateContext<'a>, } impl<'a> Visitor<()> for TransItemVisitor<'a> { @@ -2895,52 +2896,54 @@ pub fn trans_crate(krate: ast::Crate, let link_meta = link::build_link_meta(&tcx.sess, &krate, name); - // Append ".rs" to crate name as LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let mut llmod_id = link_meta.crate_name.clone(); - llmod_id.push_str(".rs"); + // Multiple compilation units won't be supported until a later commit. + let codegen_units = 1; + let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(), + codegen_units, + tcx, + exp_map2, + Sha256::new(), + link_meta.clone(), + reachable); - let ccx = CrateContext::new(llmod_id.as_slice(), tcx, exp_map2, - Sha256::new(), link_meta, reachable); + let metadata = { + let ccx = shared_ccx.get_ccx(0); - // First, verify intrinsics. - intrinsic::check_intrinsics(&ccx); + // First, verify intrinsics. + intrinsic::check_intrinsics(&ccx); - // Next, translate the module. - { - let _icx = push_ctxt("text"); - trans_mod(&ccx, &krate.module); - } + // Next, translate the module. + { + let _icx = push_ctxt("text"); + trans_mod(&ccx, &krate.module); + } - glue::emit_tydescs(&ccx); - if ccx.sess().opts.debuginfo != NoDebugInfo { - debuginfo::finalize(&ccx); - } + 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(&ccx, &krate); - if ccx.sess().trans_stats() { + if shared_ccx.sess().trans_stats() { + let stats = shared_ccx.stats(); println!("--- trans stats ---"); - println!("n_static_tydescs: {}", ccx.stats().n_static_tydescs.get()); - println!("n_glues_created: {}", ccx.stats().n_glues_created.get()); - println!("n_null_glues: {}", ccx.stats().n_null_glues.get()); - println!("n_real_glues: {}", ccx.stats().n_real_glues.get()); - - println!("n_fns: {}", ccx.stats().n_fns.get()); - println!("n_monos: {}", ccx.stats().n_monos.get()); - println!("n_inlines: {}", ccx.stats().n_inlines.get()); - println!("n_closures: {}", ccx.stats().n_closures.get()); + println!("n_static_tydescs: {}", stats.n_static_tydescs.get()); + println!("n_glues_created: {}", stats.n_glues_created.get()); + println!("n_null_glues: {}", stats.n_null_glues.get()); + println!("n_real_glues: {}", stats.n_real_glues.get()); + + println!("n_fns: {}", stats.n_fns.get()); + println!("n_monos: {}", stats.n_monos.get()); + println!("n_inlines: {}", stats.n_inlines.get()); + println!("n_closures: {}", stats.n_closures.get()); println!("fn stats:"); - ccx.stats().fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| { + stats.fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| { insns_b.cmp(&insns_a) }); - for tuple in ccx.stats().fn_stats.borrow().iter() { + for tuple in stats.fn_stats.borrow().iter() { match *tuple { (ref name, ms, insns) => { println!("{} insns, {} ms, {}", insns, ms, *name); @@ -2948,27 +2951,26 @@ pub fn trans_crate(krate: ast::Crate, } } } - if ccx.sess().count_llvm_insns() { - for (k, v) in ccx.stats().llvm_insns.borrow().iter() { + if shared_ccx.sess().count_llvm_insns() { + for (k, v) in shared_ccx.stats().llvm_insns.borrow().iter() { println!("{:7u} {}", *v, *k); } } - let llcx = ccx.llcx(); - let link_meta = ccx.link_meta().clone(); - let llmod = ccx.llmod(); + let llcx = shared_ccx.get_ccx(0).llcx(); + let llmod = shared_ccx.get_ccx(0).llmod(); - let mut reachable: Vec = ccx.reachable().iter().filter_map(|id| { - ccx.item_symbols().borrow().find(id).map(|s| s.to_string()) + let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { + shared_ccx.item_symbols().borrow().find(id).map(|s| s.to_string()) }).collect(); // For the purposes of LTO, we add to the reachable set all of the upstream // reachable extern fns. These functions are all part of the public ABI of // the final product, so LTO needs to preserve them. - ccx.sess().cstore.iter_crate_data(|cnum, _| { - let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum); + shared_ccx.sess().cstore.iter_crate_data(|cnum, _| { + let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum); reachable.extend(syms.move_iter().map(|did| { - csearch::get_symbol(&ccx.sess().cstore, did) + csearch::get_symbol(&shared_ccx.sess().cstore, did) })); }); @@ -2986,15 +2988,17 @@ pub fn trans_crate(krate: ast::Crate, // referenced from rt/rust_try.ll reachable.push("rust_eh_personality_catch".to_string()); - let metadata_module = ccx.metadata_llmod(); - let formats = ccx.tcx().dependency_formats.borrow().clone(); + let metadata_module = shared_ccx.metadata_llmod(); + let metadata_context = shared_ccx.metadata_llcx(); + let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins"); - (ccx.take_tcx(), CrateTranslation { + (shared_ccx.take_tcx(), CrateTranslation { context: llcx, module: llmod, link: link_meta, metadata_module: metadata_module, + metadata_context: metadata_context, metadata: metadata, reachable: reachable, crate_formats: formats, diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index 29c7b93a3691f..61a79f4a8eeba 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -25,7 +25,7 @@ use syntax::codemap::Span; pub struct Builder<'a> { pub llbuilder: BuilderRef, - pub ccx: &'a CrateContext, + pub ccx: &'a CrateContext<'a>, } // This is a really awful way to get a zero-length c-string, but better (and a diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index f0014827cc780..dbd3de2a9986a 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -297,7 +297,7 @@ pub struct FunctionContext<'a> { pub block_arena: &'a TypedArena>, // This function's enclosing crate context. - pub ccx: &'a CrateContext, + pub ccx: &'a CrateContext<'a>, // Used and maintained by the debuginfo module. pub debug_context: debuginfo::FunctionDebugContext, @@ -449,7 +449,7 @@ impl<'a> Block<'a> { }) } - pub fn ccx(&self) -> &'a CrateContext { self.fcx.ccx } + pub fn ccx(&self) -> &'a CrateContext<'a> { self.fcx.ccx } pub fn tcx(&self) -> &'a ty::ctxt { self.fcx.ccx.tcx() } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index d3538afa972f3..130e382e4e897 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -51,18 +51,40 @@ pub struct Stats { pub fn_stats: RefCell >, } -pub struct CrateContext { +/// The shared portion of a `CrateContext`. There is one `SharedCrateContext` +/// per crate. The data here is shared between all compilation units of the +/// crate, so it must not contain references to any LLVM data structures +/// (aside from metadata-related ones). +pub struct SharedCrateContext { + local_ccxs: Vec, + + metadata_llmod: ModuleRef, + metadata_llcx: ContextRef, + + exp_map2: resolve::ExportMap2, + reachable: NodeSet, + item_symbols: RefCell>, + link_meta: LinkMeta, + /// A set of static items which cannot be inlined into other crates. This + /// will prevent in IIItem() structures from being encoded into the metadata + /// that is generated + non_inlineable_statics: RefCell, + symbol_hasher: RefCell, + tcx: ty::ctxt, + stats: Stats, +} + +/// The local portion of a `CrateContext`. There is one `LocalCrateContext` +/// per compilation unit. Each one has its own LLVM `ContextRef` so that +/// several compilation units may be optimized in parallel. All other LLVM +/// data structures in the `LocalCrateContext` are tied to that `ContextRef`. +pub struct LocalCrateContext { llmod: ModuleRef, llcx: ContextRef, - metadata_llmod: ModuleRef, td: TargetData, tn: TypeNames, externs: RefCell, item_vals: RefCell>, - exp_map2: resolve::ExportMap2, - reachable: NodeSet, - item_symbols: RefCell>, - link_meta: LinkMeta, drop_glues: RefCell>, tydescs: RefCell>>, /// Set when running emit_tydescs to enforce that no more tydescs are @@ -73,10 +95,6 @@ pub struct CrateContext { /// Backwards version of the `external` map (inlined items to where they /// came from) external_srcs: RefCell>, - /// A set of static items which cannot be inlined into other crates. This - /// will prevent in IIItem() structures from being encoded into the metadata - /// that is generated - non_inlineable_statics: RefCell, /// Cache instances of monomorphized functions monomorphized: RefCell>, monomorphizing: RefCell>, @@ -109,11 +127,8 @@ pub struct CrateContext { lltypes: RefCell>, llsizingtypes: RefCell>, adt_reprs: RefCell>>, - symbol_hasher: RefCell, type_hashcodes: RefCell>, all_llvm_symbols: RefCell>, - tcx: ty::ctxt, - stats: Stats, int_type: Type, opaque_vec_type: Type, builder: BuilderRef_res, @@ -128,71 +143,178 @@ pub struct CrateContext { intrinsics: RefCell>, } -impl CrateContext { - pub fn new(name: &str, +pub struct CrateContext<'a> { + shared: &'a SharedCrateContext, + local: &'a LocalCrateContext, +} + +unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { + let llcx = llvm::LLVMContextCreate(); + let llmod = mod_name.with_c_str(|buf| { + llvm::LLVMModuleCreateWithNameInContext(buf, llcx) + }); + sess.targ_cfg + .target_strs + .data_layout + .as_slice() + .with_c_str(|buf| { + llvm::LLVMSetDataLayout(llmod, buf); + }); + sess.targ_cfg + .target_strs + .target_triple + .as_slice() + .with_c_str(|buf| { + llvm::LLVMRustSetNormalizedTarget(llmod, buf); + }); + (llcx, llmod) +} + +impl SharedCrateContext { + pub fn new(crate_name: &str, + local_count: uint, tcx: ty::ctxt, emap2: resolve::ExportMap2, symbol_hasher: Sha256, link_meta: LinkMeta, reachable: NodeSet) - -> CrateContext { + -> SharedCrateContext { + let (metadata_llcx, metadata_llmod) = unsafe { + create_context_and_module(&tcx.sess, "metadata") + }; + + let mut shared_ccx = SharedCrateContext { + local_ccxs: Vec::with_capacity(local_count), + metadata_llmod: metadata_llmod, + metadata_llcx: metadata_llcx, + exp_map2: emap2, + reachable: reachable, + item_symbols: RefCell::new(NodeMap::new()), + link_meta: link_meta, + non_inlineable_statics: RefCell::new(NodeSet::new()), + symbol_hasher: RefCell::new(symbol_hasher), + tcx: tcx, + stats: Stats { + n_static_tydescs: Cell::new(0u), + n_glues_created: Cell::new(0u), + n_null_glues: Cell::new(0u), + n_real_glues: Cell::new(0u), + n_fns: Cell::new(0u), + n_monos: Cell::new(0u), + n_inlines: Cell::new(0u), + n_closures: Cell::new(0u), + n_llvm_insns: Cell::new(0u), + llvm_insns: RefCell::new(HashMap::new()), + fn_stats: RefCell::new(Vec::new()), + }, + }; + + for i in range(0, local_count) { + // Append ".rs" to crate name as LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}.{}.rs", crate_name, i); + let local_ccx = LocalCrateContext::new(&shared_ccx, llmod_id.as_slice()); + shared_ccx.local_ccxs.push(local_ccx); + } + + shared_ccx + } + + pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a> { + CrateContext { + shared: self, + local: &self.local_ccxs[index], + } + } + + + pub fn metadata_llmod(&self) -> ModuleRef { + self.metadata_llmod + } + + pub fn metadata_llcx(&self) -> ContextRef { + self.metadata_llcx + } + + pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { + &self.exp_map2 + } + + pub fn reachable<'a>(&'a self) -> &'a NodeSet { + &self.reachable + } + + pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { + &self.item_symbols + } + + pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { + &self.link_meta + } + + pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell { + &self.non_inlineable_statics + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &self.symbol_hasher + } + + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt { + &self.tcx + } + + pub fn take_tcx(self) -> ty::ctxt { + self.tcx + } + + pub fn sess<'a>(&'a self) -> &'a Session { + &self.tcx.sess + } + + pub fn stats<'a>(&'a self) -> &'a Stats { + &self.stats + } +} + +impl LocalCrateContext { + fn new(shared: &SharedCrateContext, + name: &str) + -> LocalCrateContext { unsafe { - let llcx = llvm::LLVMContextCreate(); - let llmod = name.with_c_str(|buf| { - llvm::LLVMModuleCreateWithNameInContext(buf, llcx) - }); - let metadata_llmod = format!("{}_metadata", name).with_c_str(|buf| { - llvm::LLVMModuleCreateWithNameInContext(buf, llcx) - }); - tcx.sess - .targ_cfg - .target_strs - .data_layout - .as_slice() - .with_c_str(|buf| { - llvm::LLVMSetDataLayout(llmod, buf); - llvm::LLVMSetDataLayout(metadata_llmod, buf); - }); - tcx.sess - .targ_cfg - .target_strs - .target_triple - .as_slice() - .with_c_str(|buf| { - llvm::LLVMRustSetNormalizedTarget(llmod, buf); - llvm::LLVMRustSetNormalizedTarget(metadata_llmod, buf); - }); - - let td = mk_target_data(tcx.sess - .targ_cfg - .target_strs - .data_layout - .as_slice()); - - let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { + let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, name); + + let td = mk_target_data(shared.tcx + .sess + .targ_cfg + .target_strs + .data_layout + .as_slice()); + + let dbg_cx = if shared.tcx.sess.opts.debuginfo != NoDebugInfo { Some(debuginfo::CrateDebugContext::new(llmod)) } else { None }; - let mut ccx = CrateContext { + let mut local_ccx = LocalCrateContext { llmod: llmod, llcx: llcx, - metadata_llmod: metadata_llmod, td: td, tn: TypeNames::new(), externs: RefCell::new(HashMap::new()), item_vals: RefCell::new(NodeMap::new()), - exp_map2: emap2, - reachable: reachable, - item_symbols: RefCell::new(NodeMap::new()), - link_meta: link_meta, drop_glues: RefCell::new(HashMap::new()), tydescs: RefCell::new(HashMap::new()), finished_tydescs: Cell::new(false), external: RefCell::new(DefIdMap::new()), external_srcs: RefCell::new(NodeMap::new()), - non_inlineable_statics: RefCell::new(NodeSet::new()), monomorphized: RefCell::new(HashMap::new()), monomorphizing: RefCell::new(DefIdMap::new()), vtables: RefCell::new(HashMap::new()), @@ -205,23 +327,8 @@ impl CrateContext { lltypes: RefCell::new(HashMap::new()), llsizingtypes: RefCell::new(HashMap::new()), adt_reprs: RefCell::new(HashMap::new()), - symbol_hasher: RefCell::new(symbol_hasher), type_hashcodes: RefCell::new(HashMap::new()), all_llvm_symbols: RefCell::new(HashSet::new()), - tcx: tcx, - stats: Stats { - n_static_tydescs: Cell::new(0u), - n_glues_created: Cell::new(0u), - n_null_glues: Cell::new(0u), - n_real_glues: Cell::new(0u), - n_fns: Cell::new(0u), - n_monos: Cell::new(0u), - n_inlines: Cell::new(0u), - n_closures: Cell::new(0u), - n_llvm_insns: Cell::new(0u), - llvm_insns: RefCell::new(HashMap::new()), - fn_stats: RefCell::new(Vec::new()), - }, int_type: Type::from_ref(ptr::mut_null()), opaque_vec_type: Type::from_ref(ptr::mut_null()), builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)), @@ -231,33 +338,61 @@ impl CrateContext { intrinsics: RefCell::new(HashMap::new()), }; - ccx.int_type = Type::int(&ccx); - ccx.opaque_vec_type = Type::opaque_vec(&ccx); + local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared)); + local_ccx.opaque_vec_type = Type::opaque_vec(&local_ccx.dummy_ccx(shared)); + + // Done mutating local_ccx directly. (The rest of the + // initialization goes through RefCell.) + { + let ccx = local_ccx.dummy_ccx(shared); - let mut str_slice_ty = Type::named_struct(&ccx, "str_slice"); - str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type()], false); - ccx.tn().associate_type("str_slice", &str_slice_ty); + let mut str_slice_ty = Type::named_struct(&ccx, "str_slice"); + str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type()], false); + ccx.tn().associate_type("str_slice", &str_slice_ty); - ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); + ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); - if ccx.sess().count_llvm_insns() { - base::init_insn_ctxt() + if ccx.sess().count_llvm_insns() { + base::init_insn_ctxt() + } } - ccx + local_ccx } } - pub fn tcx<'a>(&'a self) -> &'a ty::ctxt { - &self.tcx + /// Create a dummy `CrateContext` from `self` and the provided + /// `SharedCrateContext`. This is somewhat dangerous because `self` may + /// not actually be an element of `shared.local_ccxs`, which can cause some + /// operations to `fail` unexpectedly. + /// + /// This is used in the `LocalCrateContext` constructor to allow calling + /// functions that expect a complete `CrateContext`, even before the local + /// portion is fully initialized and attached to the `SharedCrateContext`. + fn dummy_ccx<'a>(&'a self, shared: &'a SharedCrateContext) -> CrateContext<'a> { + CrateContext { + shared: shared, + local: self, + } } +} - pub fn take_tcx(self) -> ty::ctxt { - self.tcx +impl<'b> CrateContext<'b> { + pub fn shared(&self) -> &'b SharedCrateContext { + self.shared + } + + pub fn local(&self) -> &'b LocalCrateContext { + self.local + } + + + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt { + &self.shared.tcx } pub fn sess<'a>(&'a self) -> &'a Session { - &self.tcx.sess + &self.shared.tcx.sess } pub fn builder<'a>(&'a self) -> Builder<'a> { @@ -265,15 +400,15 @@ impl CrateContext { } pub fn raw_builder<'a>(&'a self) -> BuilderRef { - self.builder.b + self.local.builder.b } pub fn tydesc_type(&self) -> Type { - self.tn.find_type("tydesc").unwrap() + self.local.tn.find_type("tydesc").unwrap() } pub fn get_intrinsic(&self, key: & &'static str) -> ValueRef { - match self.intrinsics.borrow().find_copy(key) { + match self.intrinsics().borrow().find_copy(key) { Some(v) => return v, _ => {} } @@ -297,160 +432,164 @@ impl CrateContext { pub fn llmod(&self) -> ModuleRef { - self.llmod + self.local.llmod } pub fn llcx(&self) -> ContextRef { - self.llcx + self.local.llcx } pub fn metadata_llmod(&self) -> ModuleRef { - self.metadata_llmod + self.shared.metadata_llmod + } + + pub fn metadata_llcx(&self) -> ContextRef { + self.shared.metadata_llcx } pub fn td<'a>(&'a self) -> &'a TargetData { - &self.td + &self.local.td } pub fn tn<'a>(&'a self) -> &'a TypeNames { - &self.tn + &self.local.tn } pub fn externs<'a>(&'a self) -> &'a RefCell { - &self.externs + &self.local.externs } pub fn item_vals<'a>(&'a self) -> &'a RefCell> { - &self.item_vals + &self.local.item_vals } pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { - &self.exp_map2 + &self.shared.exp_map2 } pub fn reachable<'a>(&'a self) -> &'a NodeSet { - &self.reachable + &self.shared.reachable } pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { - &self.item_symbols + &self.shared.item_symbols } pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { - &self.link_meta + &self.shared.link_meta } pub fn drop_glues<'a>(&'a self) -> &'a RefCell> { - &self.drop_glues + &self.local.drop_glues } pub fn tydescs<'a>(&'a self) -> &'a RefCell>> { - &self.tydescs + &self.local.tydescs } pub fn finished_tydescs<'a>(&'a self) -> &'a Cell { - &self.finished_tydescs + &self.local.finished_tydescs } pub fn external<'a>(&'a self) -> &'a RefCell>> { - &self.external + &self.local.external } pub fn external_srcs<'a>(&'a self) -> &'a RefCell> { - &self.external_srcs + &self.local.external_srcs } pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell { - &self.non_inlineable_statics + &self.shared.non_inlineable_statics } pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { - &self.monomorphized + &self.local.monomorphized } pub fn monomorphizing<'a>(&'a self) -> &'a RefCell> { - &self.monomorphizing + &self.local.monomorphizing } pub fn vtables<'a>(&'a self) -> &'a RefCell> { - &self.vtables + &self.local.vtables } pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell> { - &self.const_cstr_cache + &self.local.const_cstr_cache } pub fn const_globals<'a>(&'a self) -> &'a RefCell> { - &self.const_globals + &self.local.const_globals } pub fn const_values<'a>(&'a self) -> &'a RefCell> { - &self.const_values + &self.local.const_values } pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { - &self.extern_const_values + &self.local.extern_const_values } pub fn impl_method_cache<'a>(&'a self) -> &'a RefCell> { - &self.impl_method_cache + &self.local.impl_method_cache } pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell> { - &self.closure_bare_wrapper_cache + &self.local.closure_bare_wrapper_cache } pub fn lltypes<'a>(&'a self) -> &'a RefCell> { - &self.lltypes + &self.local.lltypes } pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell> { - &self.llsizingtypes + &self.local.llsizingtypes } pub fn adt_reprs<'a>(&'a self) -> &'a RefCell>> { - &self.adt_reprs + &self.local.adt_reprs } pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { - &self.symbol_hasher + &self.shared.symbol_hasher } pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell> { - &self.type_hashcodes + &self.local.type_hashcodes } pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell> { - &self.all_llvm_symbols + &self.local.all_llvm_symbols } pub fn stats<'a>(&'a self) -> &'a Stats { - &self.stats + &self.shared.stats } pub fn int_type(&self) -> Type { - self.int_type + self.local.int_type } pub fn opaque_vec_type(&self) -> Type { - self.opaque_vec_type + self.local.opaque_vec_type } pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell> { - &self.unboxed_closure_vals + &self.local.unboxed_closure_vals } pub fn dbg_cx<'a>(&'a self) -> &'a Option { - &self.dbg_cx + &self.local.dbg_cx } pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { - &self.eh_personality + &self.local.eh_personality } fn intrinsics<'a>(&'a self) -> &'a RefCell> { - &self.intrinsics + &self.local.intrinsics } } From e29aa1430bb45d18a5d3fcc5f3b7d20e99a57758 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Mon, 11 Aug 2014 10:33:58 -0700 Subject: [PATCH 03/14] move back::link::write into a separate file --- src/librustc/back/link.rs | 478 +--------------------------------- src/librustc/back/lto.rs | 7 +- src/librustc/back/write.rs | 476 +++++++++++++++++++++++++++++++++ src/librustc/driver/config.rs | 16 +- src/librustc/driver/driver.rs | 35 +-- src/librustc/lib.rs | 1 + src/librustdoc/test.rs | 4 +- 7 files changed, 511 insertions(+), 506 deletions(-) create mode 100644 src/librustc/back/write.rs diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index cb71e5983235d..07fa63336e33e 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -13,12 +13,11 @@ use super::archive; use super::rpath; use super::rpath::RPathConfig; use super::svh::Svh; +use super::write::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput}; use driver::config::NoDebugInfo; use driver::session::Session; use driver::config; -use llvm; -use llvm::ModuleRef; use metadata::common::LinkMeta; use metadata::{encoder, cstore, filesearch, csearch, loader, creader}; use middle::trans::context::CrateContext; @@ -28,13 +27,11 @@ use util::common::time; use util::ppaux; use util::sha2::{Digest, Sha256}; -use std::c_str::{ToCStr, CString}; use std::char; use std::collections::HashSet; use std::io::{fs, TempDir, Command}; use std::io; use std::mem; -use std::ptr; use std::str; use std::string::String; use flate; @@ -77,477 +74,6 @@ pub static RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint = RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; -#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] -pub enum OutputType { - OutputTypeBitcode, - OutputTypeAssembly, - OutputTypeLlvmAssembly, - OutputTypeObject, - OutputTypeExe, -} - -pub fn llvm_err(sess: &Session, msg: String) -> ! { - unsafe { - let cstr = llvm::LLVMRustGetLastError(); - if cstr == ptr::null() { - sess.fatal(msg.as_slice()); - } else { - let err = CString::new(cstr, true); - let err = String::from_utf8_lossy(err.as_bytes()); - sess.fatal(format!("{}: {}", - msg.as_slice(), - err.as_slice()).as_slice()); - } - } -} - -pub fn write_output_file( - sess: &Session, - target: llvm::TargetMachineRef, - pm: llvm::PassManagerRef, - m: ModuleRef, - output: &Path, - file_type: llvm::FileType) { - unsafe { - output.with_c_str(|output| { - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output, file_type); - if !result { - llvm_err(sess, "could not write output".to_string()); - } - }) - } -} - -pub mod write { - - use super::super::lto; - use super::{write_output_file, OutputType}; - use super::{OutputTypeAssembly, OutputTypeBitcode}; - use super::{OutputTypeExe, OutputTypeLlvmAssembly}; - use super::{OutputTypeObject}; - use driver::driver::{CrateTranslation, OutputFilenames}; - use driver::config::NoDebugInfo; - use driver::session::Session; - use driver::config; - use llvm; - use llvm::{ModuleRef, TargetMachineRef, PassManagerRef}; - use util::common::time; - use syntax::abi; - - use std::c_str::ToCStr; - use std::io::{Command}; - use libc::{c_uint, c_int}; - use std::str; - - // On android, we by default compile for armv7 processors. This enables - // things like double word CAS instructions (rather than emulating them) - // which are *far* more efficient. This is obviously undesirable in some - // cases, so if any sort of target feature is specified we don't append v7 - // to the feature list. - // - // On iOS only armv7 and newer are supported. So it is useful to - // get all hardware potential via VFP3 (hardware floating point) - // and NEON (SIMD) instructions supported by LLVM. - // Note that without those flags various linking errors might - // arise as some of intrinsics are converted into function calls - // and nobody provides implementations those functions - fn target_feature<'a>(sess: &'a Session) -> &'a str { - match sess.targ_cfg.os { - abi::OsAndroid => { - if "" == sess.opts.cg.target_feature.as_slice() { - "+v7" - } else { - sess.opts.cg.target_feature.as_slice() - } - }, - abi::OsiOS if sess.targ_cfg.arch == abi::Arm => { - "+v7,+thumb2,+vfp3,+neon" - }, - _ => sess.opts.cg.target_feature.as_slice() - } - } - - pub fn run_passes(sess: &Session, - trans: &CrateTranslation, - output_types: &[OutputType], - output: &OutputFilenames) { - let llmod = trans.module; - let llcx = trans.context; - unsafe { - configure_llvm(sess); - - if sess.opts.cg.save_temps { - output.with_extension("no-opt.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - let opt_level = match sess.opts.optimize { - config::No => llvm::CodeGenLevelNone, - config::Less => llvm::CodeGenLevelLess, - config::Default => llvm::CodeGenLevelDefault, - config::Aggressive => llvm::CodeGenLevelAggressive, - }; - let use_softfp = sess.opts.cg.soft_float; - - // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. - // FIXME: #11954: mac64 unwinding may not work with fp elim - let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || - (sess.targ_cfg.os == abi::OsMacos && - sess.targ_cfg.arch == abi::X86_64); - - // OSX has -dead_strip, which doesn't rely on ffunction_sections - // FIXME(#13846) this should be enabled for windows - let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && - sess.targ_cfg.os != abi::OsWindows; - let fdata_sections = ffunction_sections; - - let reloc_model = match sess.opts.cg.relocation_model.as_slice() { - "pic" => llvm::RelocPIC, - "static" => llvm::RelocStatic, - "default" => llvm::RelocDefault, - "dynamic-no-pic" => llvm::RelocDynamicNoPic, - _ => { - sess.err(format!("{} is not a valid relocation mode", - sess.opts - .cg - .relocation_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; - - let code_model = match sess.opts.cg.code_model.as_slice() { - "default" => llvm::CodeModelDefault, - "small" => llvm::CodeModelSmall, - "kernel" => llvm::CodeModelKernel, - "medium" => llvm::CodeModelMedium, - "large" => llvm::CodeModelLarge, - _ => { - sess.err(format!("{} is not a valid code model", - sess.opts - .cg - .code_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; - - let tm = sess.targ_cfg - .target_strs - .target_triple - .as_slice() - .with_c_str(|t| { - sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| { - target_feature(sess).with_c_str(|features| { - llvm::LLVMRustCreateTargetMachine( - t, cpu, features, - code_model, - reloc_model, - opt_level, - true /* EnableSegstk */, - use_softfp, - no_fp_elim, - ffunction_sections, - fdata_sections, - ) - }) - }) - }); - - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass: &str| { - pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) - }; - if !sess.no_verify() { assert!(addpass("verify")); } - - if !sess.opts.cg.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - populate_llvm_passes(fpm, mpm, llmod, opt_level, - trans.no_builtins); - } - - for pass in sess.opts.cg.passes.iter() { - pass.as_slice().with_c_str(|s| { - if !llvm::LLVMRustAddPass(mpm, s) { - sess.warn(format!("unknown pass {}, ignoring", - *pass).as_slice()); - } - }) - } - - // Finally, run the actual optimization passes - time(sess.time_passes(), "llvm function passes", (), |()| - llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(sess.time_passes(), "llvm module passes", (), |()| - llvm::LLVMRunPassManager(mpm, llmod)); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - - // Emit the bytecode if we're either saving our temporaries or - // emitting an rlib. Whenever an rlib is created, the bytecode is - // inserted into the archive in order to allow LTO against it. - if sess.opts.cg.save_temps || - (sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains(&OutputTypeExe)) { - output.temp_path(OutputTypeBitcode).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - if sess.lto() { - time(sess.time_passes(), "all lto passes", (), |()| - lto::run(sess, llmod, tm, trans.reachable.as_slice())); - - if sess.opts.cg.save_temps { - output.with_extension("lto.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - } - - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, - no_builtins: bool, f: |PassManagerRef|) { - unsafe { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm); - llvm::LLVMDisposePassManager(cpm); - } - } - - let mut object_file = None; - let mut needs_metadata = false; - for output_type in output_types.iter() { - let path = output.path(*output_type); - match *output_type { - OutputTypeBitcode => { - path.with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - OutputTypeLlvmAssembly => { - path.with_c_str(|output| { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, output); - }) - }) - } - OutputTypeAssembly => { - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, - // so in this case we still want the metadata object - // file. - let ty = OutputTypeAssembly; - let path = if sess.opts.output_types.contains(&ty) { - path - } else { - needs_metadata = true; - output.temp_path(OutputTypeAssembly) - }; - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, &path, - llvm::AssemblyFile); - }); - } - OutputTypeObject => { - object_file = Some(path); - } - OutputTypeExe => { - object_file = Some(output.temp_path(OutputTypeObject)); - needs_metadata = true; - } - } - } - - time(sess.time_passes(), "codegen passes", (), |()| { - match object_file { - Some(ref path) => { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, path, - llvm::ObjectFile); - }); - } - None => {} - } - if needs_metadata { - with_codegen(tm, trans.metadata_module, - trans.no_builtins, |cpm| { - let out = output.temp_path(OutputTypeObject) - .with_extension("metadata.o"); - write_output_file(sess, tm, cpm, - trans.metadata_module, &out, - llvm::ObjectFile); - }) - } - }); - - llvm::LLVMRustDisposeTargetMachine(tm); - llvm::LLVMDisposeModule(trans.metadata_module); - llvm::LLVMContextDispose(trans.metadata_context); - llvm::LLVMDisposeModule(llmod); - llvm::LLVMContextDispose(llcx); - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } - } - } - - pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { - let pname = super::get_cc_prog(sess); - let mut cmd = Command::new(pname.as_slice()); - - cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject)) - .arg(outputs.temp_path(OutputTypeAssembly)); - debug!("{}", &cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", - pname, - prog.status).as_slice()); - sess.note(format!("{}", &cmd).as_slice()); - let mut note = prog.error.clone(); - note.push_all(prog.output.as_slice()); - sess.note(str::from_utf8(note.as_slice()).unwrap()); - sess.abort_if_errors(); - } - }, - Err(e) => { - sess.err(format!("could not exec the linker `{}`: {}", - pname, - e).as_slice()); - sess.abort_if_errors(); - } - } - } - - unsafe fn configure_llvm(sess: &Session) { - use std::sync::{Once, ONCE_INIT}; - static mut INIT: Once = ONCE_INIT; - - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3 - let vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive); - let vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::Aggressive; - - let mut llvm_c_strs = Vec::new(); - let mut llvm_args = Vec::new(); - { - let add = |arg: &str| { - let s = arg.to_c_str(); - llvm_args.push(s.as_ptr()); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - if vectorize_loop { add("-vectorize-loops"); } - if vectorize_slp { add("-vectorize-slp"); } - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - - for arg in sess.opts.cg.llvm_args.iter() { - add((*arg).as_slice()); - } - } - - INIT.doit(|| { - llvm::LLVMInitializePasses(); - - // Only initialize the platforms supported by Rust here, because - // using --llvm-root will have multiple platforms that rustllvm - // doesn't actually link to and it's pointless to put target info - // into the registry that Rust cannot generate machine code for. - llvm::LLVMInitializeX86TargetInfo(); - llvm::LLVMInitializeX86Target(); - llvm::LLVMInitializeX86TargetMC(); - llvm::LLVMInitializeX86AsmPrinter(); - llvm::LLVMInitializeX86AsmParser(); - - llvm::LLVMInitializeARMTargetInfo(); - llvm::LLVMInitializeARMTarget(); - llvm::LLVMInitializeARMTargetMC(); - llvm::LLVMInitializeARMAsmPrinter(); - llvm::LLVMInitializeARMAsmParser(); - - llvm::LLVMInitializeMipsTargetInfo(); - llvm::LLVMInitializeMipsTarget(); - llvm::LLVMInitializeMipsTargetMC(); - llvm::LLVMInitializeMipsAsmPrinter(); - llvm::LLVMInitializeMipsAsmParser(); - - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, - llvm_args.as_ptr()); - }); - } - - unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, - mpm: llvm::PassManagerRef, - llmod: ModuleRef, - opt: llvm::CodeGenOptLevel, - no_builtins: bool) { - // Create the PassManagerBuilder for LLVM. We configure it with - // reasonable defaults and prepare it to actually populate the pass - // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); - match opt { - llvm::CodeGenLevelNone => { - // Don't add lifetime intrinsics at O0 - llvm::LLVMRustAddAlwaysInlinePass(builder, false); - } - llvm::CodeGenLevelLess => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); - } - // numeric values copied from clang - llvm::CodeGenLevelDefault => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 225); - } - llvm::CodeGenLevelAggressive => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 275); - } - } - llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); - - // Use the builder to populate the function/module pass managers. - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); - llvm::LLVMPassManagerBuilderDispose(builder); - - match opt { - llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { - "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); - } - _ => {} - }; - } -} - - /* * Name mangling and its relationship to metadata. This is complex. Read * carefully. @@ -878,7 +404,7 @@ pub fn get_ar_prog(sess: &Session) -> String { } } -fn remove(sess: &Session, path: &Path) { +pub fn remove(sess: &Session, path: &Path) { match fs::unlink(path) { Ok(..) => {} Err(e) => { diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs index 6c6a07f35029f..eeba34064905a 100644 --- a/src/librustc/back/lto.rs +++ b/src/librustc/back/lto.rs @@ -9,6 +9,7 @@ // except according to those terms. use super::link; +use super::write; use driver::session; use driver::config; use llvm; @@ -119,9 +120,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, if !llvm::LLVMRustLinkInExternalBitcode(llmod, ptr as *const libc::c_char, bc_decoded.len() as libc::size_t) { - link::llvm_err(sess, - format!("failed to load bc of `{}`", - name.as_slice())); + write::llvm_err(sess, + format!("failed to load bc of `{}`", + name.as_slice())); } }); } diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs new file mode 100644 index 0000000000000..c234539036603 --- /dev/null +++ b/src/librustc/back/write.rs @@ -0,0 +1,476 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::lto; +use back::link::get_cc_prog; +use driver::driver::{CrateTranslation, OutputFilenames}; +use driver::config::NoDebugInfo; +use driver::session::Session; +use driver::config; +use llvm; +use llvm::{ModuleRef, TargetMachineRef, PassManagerRef}; +use util::common::time; +use syntax::abi; + +use std::c_str::{ToCStr, CString}; +use std::io::Command; +use std::ptr; +use std::str; +use libc::{c_uint, c_int}; + + +#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] +pub enum OutputType { + OutputTypeBitcode, + OutputTypeAssembly, + OutputTypeLlvmAssembly, + OutputTypeObject, + OutputTypeExe, +} + + +pub fn llvm_err(sess: &Session, msg: String) -> ! { + unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.fatal(msg.as_slice()); + } else { + let err = CString::new(cstr, true); + let err = String::from_utf8_lossy(err.as_bytes()); + sess.fatal(format!("{}: {}", + msg.as_slice(), + err.as_slice()).as_slice()); + } + } +} + +pub fn write_output_file( + sess: &Session, + target: llvm::TargetMachineRef, + pm: llvm::PassManagerRef, + m: ModuleRef, + output: &Path, + file_type: llvm::FileType) { + unsafe { + output.with_c_str(|output| { + let result = llvm::LLVMRustWriteOutputFile( + target, pm, m, output, file_type); + if !result { + llvm_err(sess, "could not write output".to_string()); + } + }) + } +} + + +// On android, we by default compile for armv7 processors. This enables +// things like double word CAS instructions (rather than emulating them) +// which are *far* more efficient. This is obviously undesirable in some +// cases, so if any sort of target feature is specified we don't append v7 +// to the feature list. +// +// On iOS only armv7 and newer are supported. So it is useful to +// get all hardware potential via VFP3 (hardware floating point) +// and NEON (SIMD) instructions supported by LLVM. +// Note that without those flags various linking errors might +// arise as some of intrinsics are converted into function calls +// and nobody provides implementations those functions +fn target_feature<'a>(sess: &'a Session) -> &'a str { + match sess.targ_cfg.os { + abi::OsAndroid => { + if "" == sess.opts.cg.target_feature.as_slice() { + "+v7" + } else { + sess.opts.cg.target_feature.as_slice() + } + }, + abi::OsiOS if sess.targ_cfg.arch == abi::Arm => { + "+v7,+thumb2,+vfp3,+neon" + }, + _ => sess.opts.cg.target_feature.as_slice() + } +} + +pub fn run_passes(sess: &Session, + trans: &CrateTranslation, + output_types: &[OutputType], + output: &OutputFilenames) { + let llmod = trans.module; + let llcx = trans.context; + unsafe { + configure_llvm(sess); + + if sess.opts.cg.save_temps { + output.with_extension("no-opt.bc").with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + let opt_level = match sess.opts.optimize { + config::No => llvm::CodeGenLevelNone, + config::Less => llvm::CodeGenLevelLess, + config::Default => llvm::CodeGenLevelDefault, + config::Aggressive => llvm::CodeGenLevelAggressive, + }; + let use_softfp = sess.opts.cg.soft_float; + + // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. + // FIXME: #11954: mac64 unwinding may not work with fp elim + let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || + (sess.targ_cfg.os == abi::OsMacos && + sess.targ_cfg.arch == abi::X86_64); + + // OSX has -dead_strip, which doesn't rely on ffunction_sections + // FIXME(#13846) this should be enabled for windows + let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && + sess.targ_cfg.os != abi::OsWindows; + let fdata_sections = ffunction_sections; + + let reloc_model = match sess.opts.cg.relocation_model.as_slice() { + "pic" => llvm::RelocPIC, + "static" => llvm::RelocStatic, + "default" => llvm::RelocDefault, + "dynamic-no-pic" => llvm::RelocDynamicNoPic, + _ => { + sess.err(format!("{} is not a valid relocation mode", + sess.opts + .cg + .relocation_model).as_slice()); + sess.abort_if_errors(); + return; + } + }; + + let code_model = match sess.opts.cg.code_model.as_slice() { + "default" => llvm::CodeModelDefault, + "small" => llvm::CodeModelSmall, + "kernel" => llvm::CodeModelKernel, + "medium" => llvm::CodeModelMedium, + "large" => llvm::CodeModelLarge, + _ => { + sess.err(format!("{} is not a valid code model", + sess.opts + .cg + .code_model).as_slice()); + sess.abort_if_errors(); + return; + } + }; + + let tm = sess.targ_cfg + .target_strs + .target_triple + .as_slice() + .with_c_str(|t| { + sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| { + target_feature(sess).with_c_str(|features| { + llvm::LLVMRustCreateTargetMachine( + t, cpu, features, + code_model, + reloc_model, + opt_level, + true /* EnableSegstk */, + use_softfp, + no_fp_elim, + ffunction_sections, + fdata_sections, + ) + }) + }) + }); + + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass: &str| { + pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) + }; + if !sess.no_verify() { assert!(addpass("verify")); } + + if !sess.opts.cg.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + populate_llvm_passes(fpm, mpm, llmod, opt_level, + trans.no_builtins); + } + + for pass in sess.opts.cg.passes.iter() { + pass.as_slice().with_c_str(|s| { + if !llvm::LLVMRustAddPass(mpm, s) { + sess.warn(format!("unknown pass {}, ignoring", + *pass).as_slice()); + } + }) + } + + // Finally, run the actual optimization passes + time(sess.time_passes(), "llvm function passes", (), |()| + llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + time(sess.time_passes(), "llvm module passes", (), |()| + llvm::LLVMRunPassManager(mpm, llmod)); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + + // Emit the bytecode if we're either saving our temporaries or + // emitting an rlib. Whenever an rlib is created, the bytecode is + // inserted into the archive in order to allow LTO against it. + if sess.opts.cg.save_temps || + (sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains(&OutputTypeExe)) { + output.temp_path(OutputTypeBitcode).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + if sess.lto() { + time(sess.time_passes(), "all lto passes", (), |()| + lto::run(sess, llmod, tm, trans.reachable.as_slice())); + + if sess.opts.cg.save_temps { + output.with_extension("lto.bc").with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, + no_builtins: bool, f: |PassManagerRef|) { + unsafe { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm); + llvm::LLVMDisposePassManager(cpm); + } + } + + let mut object_file = None; + let mut needs_metadata = false; + for output_type in output_types.iter() { + let path = output.path(*output_type); + match *output_type { + OutputTypeBitcode => { + path.with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + OutputTypeLlvmAssembly => { + path.with_c_str(|output| { + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, output); + }) + }) + } + OutputTypeAssembly => { + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, + // so in this case we still want the metadata object + // file. + let ty = OutputTypeAssembly; + let path = if sess.opts.output_types.contains(&ty) { + path + } else { + needs_metadata = true; + output.temp_path(OutputTypeAssembly) + }; + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + write_output_file(sess, tm, cpm, llmod, &path, + llvm::AssemblyFile); + }); + } + OutputTypeObject => { + object_file = Some(path); + } + OutputTypeExe => { + object_file = Some(output.temp_path(OutputTypeObject)); + needs_metadata = true; + } + } + } + + time(sess.time_passes(), "codegen passes", (), |()| { + match object_file { + Some(ref path) => { + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + write_output_file(sess, tm, cpm, llmod, path, + llvm::ObjectFile); + }); + } + None => {} + } + if needs_metadata { + with_codegen(tm, trans.metadata_module, + trans.no_builtins, |cpm| { + let out = output.temp_path(OutputTypeObject) + .with_extension("metadata.o"); + write_output_file(sess, tm, cpm, + trans.metadata_module, &out, + llvm::ObjectFile); + }) + } + }); + + llvm::LLVMRustDisposeTargetMachine(tm); + llvm::LLVMDisposeModule(trans.metadata_module); + llvm::LLVMDisposeModule(llmod); + llvm::LLVMContextDispose(llcx); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + } +} + +pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject)) + .arg(outputs.temp_path(OutputTypeAssembly)); + debug!("{}", &cmd); + + match cmd.output() { + Ok(prog) => { + if !prog.status.success() { + sess.err(format!("linking with `{}` failed: {}", + pname, + prog.status).as_slice()); + sess.note(format!("{}", &cmd).as_slice()); + let mut note = prog.error.clone(); + note.push_all(prog.output.as_slice()); + sess.note(str::from_utf8(note.as_slice()).unwrap()); + sess.abort_if_errors(); + } + }, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + } + } +} + +unsafe fn configure_llvm(sess: &Session) { + use std::sync::{Once, ONCE_INIT}; + static mut INIT: Once = ONCE_INIT; + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3 + let vectorize_loop = !sess.opts.cg.no_vectorize_loops && + (sess.opts.optimize == config::Default || + sess.opts.optimize == config::Aggressive); + let vectorize_slp = !sess.opts.cg.no_vectorize_slp && + sess.opts.optimize == config::Aggressive; + + let mut llvm_c_strs = Vec::new(); + let mut llvm_args = Vec::new(); + { + let add = |arg: &str| { + let s = arg.to_c_str(); + llvm_args.push(s.as_ptr()); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + if vectorize_loop { add("-vectorize-loops"); } + if vectorize_slp { add("-vectorize-slp"); } + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + + for arg in sess.opts.cg.llvm_args.iter() { + add((*arg).as_slice()); + } + } + + INIT.doit(|| { + llvm::LLVMInitializePasses(); + + // Only initialize the platforms supported by Rust here, because + // using --llvm-root will have multiple platforms that rustllvm + // doesn't actually link to and it's pointless to put target info + // into the registry that Rust cannot generate machine code for. + llvm::LLVMInitializeX86TargetInfo(); + llvm::LLVMInitializeX86Target(); + llvm::LLVMInitializeX86TargetMC(); + llvm::LLVMInitializeX86AsmPrinter(); + llvm::LLVMInitializeX86AsmParser(); + + llvm::LLVMInitializeARMTargetInfo(); + llvm::LLVMInitializeARMTarget(); + llvm::LLVMInitializeARMTargetMC(); + llvm::LLVMInitializeARMAsmPrinter(); + llvm::LLVMInitializeARMAsmParser(); + + llvm::LLVMInitializeMipsTargetInfo(); + llvm::LLVMInitializeMipsTarget(); + llvm::LLVMInitializeMipsTargetMC(); + llvm::LLVMInitializeMipsAsmPrinter(); + llvm::LLVMInitializeMipsAsmParser(); + + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); + }); +} + +unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, + mpm: llvm::PassManagerRef, + llmod: ModuleRef, + opt: llvm::CodeGenOptLevel, + no_builtins: bool) { + // Create the PassManagerBuilder for LLVM. We configure it with + // reasonable defaults and prepare it to actually populate the pass + // manager. + let builder = llvm::LLVMPassManagerBuilderCreate(); + match opt { + llvm::CodeGenLevelNone => { + // Don't add lifetime intrinsics at O0 + llvm::LLVMRustAddAlwaysInlinePass(builder, false); + } + llvm::CodeGenLevelLess => { + llvm::LLVMRustAddAlwaysInlinePass(builder, true); + } + // numeric values copied from clang + llvm::CodeGenLevelDefault => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 225); + } + llvm::CodeGenLevelAggressive => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 275); + } + } + llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); + llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); + + // Use the builder to populate the function/module pass managers. + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); + llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); + llvm::LLVMPassManagerBuilderDispose(builder); + + match opt { + llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { + "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); + } + _ => {} + }; +} diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index c402c256a3719..04315149a784e 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -16,7 +16,7 @@ use driver::driver; use driver::session::Session; use back; -use back::link; +use back::write; use back::target_strs; use back::{arm, x86, x86_64, mips, mipsel}; use lint; @@ -72,7 +72,7 @@ pub struct Options { pub debuginfo: DebugInfoLevel, pub lint_opts: Vec<(String, lint::Level)>, pub describe_lints: bool, - pub output_types: Vec , + pub output_types: Vec , // This was mutable for rustpkg, which updates search paths based on the // parsed code. It remains mutable in case its replacements wants to use // this. @@ -646,11 +646,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { for unparsed_output_type in unparsed_output_types.iter() { for part in unparsed_output_type.as_slice().split(',') { let output_type = match part.as_slice() { - "asm" => link::OutputTypeAssembly, - "ir" => link::OutputTypeLlvmAssembly, - "bc" => link::OutputTypeBitcode, - "obj" => link::OutputTypeObject, - "link" => link::OutputTypeExe, + "asm" => write::OutputTypeAssembly, + "ir" => write::OutputTypeLlvmAssembly, + "bc" => write::OutputTypeBitcode, + "obj" => write::OutputTypeObject, + "link" => write::OutputTypeExe, _ => { early_error(format!("unknown emission type: `{}`", part).as_slice()) @@ -663,7 +663,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { output_types.as_mut_slice().sort(); output_types.dedup(); if output_types.len() == 0 { - output_types.push(link::OutputTypeExe); + output_types.push(write::OutputTypeExe); } let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m)); diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5e9d8da5b4edb..fa1a1b38a2d0d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -10,6 +10,7 @@ use back::link; +use back::write; use driver::session::Session; use driver::config; use front; @@ -473,23 +474,23 @@ pub fn phase_5_run_llvm_passes(sess: &Session, trans: &CrateTranslation, outputs: &OutputFilenames) { if sess.opts.cg.no_integrated_as { - let output_type = link::OutputTypeAssembly; + let output_type = write::OutputTypeAssembly; time(sess.time_passes(), "LLVM passes", (), |_| - link::write::run_passes(sess, trans, [output_type], outputs)); + write::run_passes(sess, trans, [output_type], outputs)); - link::write::run_assembler(sess, outputs); + write::run_assembler(sess, outputs); // Remove assembly source, unless --save-temps was specified if !sess.opts.cg.save_temps { - fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap(); + fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap(); } } else { time(sess.time_passes(), "LLVM passes", (), |_| - link::write::run_passes(sess, - trans, - sess.opts.output_types.as_slice(), - outputs)); + write::run_passes(sess, + trans, + sess.opts.output_types.as_slice(), + outputs)); } } @@ -533,7 +534,7 @@ pub fn stop_after_phase_2(sess: &Session) -> bool { } pub fn stop_after_phase_5(sess: &Session) -> bool { - if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) { + if !sess.opts.output_types.iter().any(|&i| i == write::OutputTypeExe) { debug!("not building executable, returning early from compile_input"); return true; } @@ -549,7 +550,7 @@ fn write_out_deps(sess: &Session, for output_type in sess.opts.output_types.iter() { let file = outputs.path(*output_type); match *output_type { - link::OutputTypeExe => { + write::OutputTypeExe => { for output in sess.crate_types.borrow().iter() { let p = link::filename_for_input(sess, *output, id, &file); @@ -688,7 +689,7 @@ pub struct OutputFilenames { } impl OutputFilenames { - pub fn path(&self, flavor: link::OutputType) -> Path { + pub fn path(&self, flavor: write::OutputType) -> Path { match self.single_output_file { Some(ref path) => return path.clone(), None => {} @@ -696,14 +697,14 @@ impl OutputFilenames { self.temp_path(flavor) } - pub fn temp_path(&self, flavor: link::OutputType) -> Path { + pub fn temp_path(&self, flavor: write::OutputType) -> Path { let base = self.out_directory.join(self.filestem()); match flavor { - link::OutputTypeBitcode => base.with_extension("bc"), - link::OutputTypeAssembly => base.with_extension("s"), - link::OutputTypeLlvmAssembly => base.with_extension("ll"), - link::OutputTypeObject => base.with_extension("o"), - link::OutputTypeExe => base, + write::OutputTypeBitcode => base.with_extension("bc"), + write::OutputTypeAssembly => base.with_extension("s"), + write::OutputTypeLlvmAssembly => base.with_extension("ll"), + write::OutputTypeObject => base.with_extension("o"), + write::OutputTypeExe => base, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 03dfcec18dbdb..e4eac80c4afd9 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -69,6 +69,7 @@ pub mod back { pub mod link; pub mod lto; + pub mod write; } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 9df748e74e8ba..d2345614b25c2 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -20,7 +20,7 @@ use std::string::String; use std::collections::{HashSet, HashMap}; use testing; -use rustc::back::link; +use rustc::back::write; use rustc::driver::config; use rustc::driver::driver; use rustc::driver::session; @@ -120,7 +120,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, externs: core::Exte maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), addl_lib_search_paths: RefCell::new(libs), crate_types: vec!(config::CrateTypeExecutable), - output_types: vec!(link::OutputTypeExe), + output_types: vec!(write::OutputTypeExe), no_trans: no_run, externs: externs, cg: config::CodegenOptions { From cf672850df05a05e8bb5785228c408a24e102d32 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Thu, 17 Jul 2014 10:52:52 -0700 Subject: [PATCH 04/14] run optimization and codegen on worker threads Refactor the code in `llvm::back` that invokes LLVM optimization and codegen passes so that it can be called from worker threads. (Previously, it used `&Session` extensively, and `Session` is not `Share`.) The new code can handle multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`, etc., and linking together all the `crate.N.o` files into a single `crate.o` using `ld -r`. The later linking steps can then be run unchanged. The new code preserves the behavior of `--emit`/`-o` when building a single compilation unit. With multiple compilation units, the `--emit=asm/ir/bc` options produce multiple files, so combinations like `--emit=ir -o foo.ll` will not actually produce `foo.ll` (they instead produce several `foo.N.ll` files). The new code supports `-Z lto` only when using a single compilation unit. Compiling with multiple compilation units and `-Z lto` will produce an error. (I can't think of any good reason to do such a thing.) Linking with `-Z lto` against a library that was built as multiple compilation units will also fail, because the rlib does not contain a `crate.bytecode.deflate` file. This could be supported in the future by linking together the `crate.N.bc` files produced when compiling the library into a single `crate.bc`, or by making the LTO code support multiple `crate.N.bytecode.deflate` files. --- src/compiletest/runtest.rs | 13 +- src/librustc/back/link.rs | 87 +-- src/librustc/back/lto.rs | 11 +- src/librustc/back/write.rs | 842 ++++++++++++++++++++------- src/librustc/driver/config.rs | 9 + src/librustc/driver/driver.rs | 12 +- src/librustc/middle/trans/base.rs | 36 +- src/librustc/middle/trans/common.rs | 18 +- src/librustc/middle/trans/context.rs | 28 + src/librustc_llvm/lib.rs | 2 +- 10 files changed, 773 insertions(+), 285 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index ee787a5f0a9bf..32671722ba728 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -1577,10 +1577,6 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) { // codegen tests (vs. clang) -fn make_o_name(config: &Config, testfile: &Path) -> Path { - output_base_name(config, testfile).with_extension("o") -} - fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path { if suffix.len() == 0 { (*p).clone() @@ -1596,14 +1592,13 @@ fn compile_test_and_save_bitcode(config: &Config, props: &TestProps, // FIXME (#9639): This needs to handle non-utf8 paths let link_args = vec!("-L".to_string(), aux_dir.as_str().unwrap().to_string()); - let llvm_args = vec!("--emit=obj".to_string(), - "--crate-type=lib".to_string(), - "-C".to_string(), - "save-temps".to_string()); + let llvm_args = vec!("--emit=bc,obj".to_string(), + "--crate-type=lib".to_string()); let args = make_compile_args(config, props, link_args.append(llvm_args.as_slice()), - |a, b| ThisFile(make_o_name(a, b)), testfile); + |a, b| ThisDirectory(output_base_name(a, b).dir_path()), + testfile); compose_and_run_compiler(config, props, testfile, args, None) } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 07fa63336e33e..5adf8b653813d 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -662,51 +662,56 @@ fn link_rlib<'a>(sess: &'a Session, ab.add_file(&metadata).unwrap(); remove(sess, &metadata); - // For LTO purposes, the bytecode of this library is also inserted - // into the archive. - // - // Note that we make sure that the bytecode filename in the archive - // is never exactly 16 bytes long by adding a 16 byte extension to - // it. This is to work around a bug in LLDB that would cause it to - // crash if the name of a file in an archive was exactly 16 bytes. - let bc_filename = obj_filename.with_extension("bc"); - let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate"); - - let bc_data = match fs::File::open(&bc_filename).read_to_end() { - Ok(buffer) => buffer, - Err(e) => sess.fatal(format!("failed to read bytecode: {}", - e).as_slice()) - }; + if sess.opts.cg.codegen_units == 1 { + // For LTO purposes, the bytecode of this library is also + // inserted into the archive. We currently do this only when + // codegen_units == 1, so we don't have to deal with multiple + // bitcode files per crate. + // + // Note that we make sure that the bytecode filename in the + // archive is never exactly 16 bytes long by adding a 16 byte + // extension to it. This is to work around a bug in LLDB that + // would cause it to crash if the name of a file in an archive + // was exactly 16 bytes. + let bc_filename = obj_filename.with_extension("bc"); + let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate"); + + let bc_data = match fs::File::open(&bc_filename).read_to_end() { + Ok(buffer) => buffer, + Err(e) => sess.fatal(format!("failed to read bytecode: {}", + e).as_slice()) + }; - let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) { - Some(compressed) => compressed, - None => sess.fatal(format!("failed to compress bytecode from {}", - bc_filename.display()).as_slice()) - }; + let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) { + Some(compressed) => compressed, + None => sess.fatal(format!("failed to compress bytecode from {}", + bc_filename.display()).as_slice()) + }; - let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { - Ok(file) => file, - Err(e) => { - sess.fatal(format!("failed to create compressed bytecode \ - file: {}", e).as_slice()) - } - }; + let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { + Ok(file) => file, + Err(e) => { + sess.fatal(format!("failed to create compressed bytecode \ + file: {}", e).as_slice()) + } + }; - match write_rlib_bytecode_object_v1(&mut bc_file_deflated, - bc_data_deflated.as_slice()) { - Ok(()) => {} - Err(e) => { - sess.err(format!("failed to write compressed bytecode: \ - {}", e).as_slice()); - sess.abort_if_errors() - } - }; + match write_rlib_bytecode_object_v1(&mut bc_file_deflated, + bc_data_deflated.as_slice()) { + Ok(()) => {} + Err(e) => { + sess.err(format!("failed to write compressed bytecode: \ + {}", e).as_slice()); + sess.abort_if_errors() + } + }; - ab.add_file(&bc_deflated_filename).unwrap(); - remove(sess, &bc_deflated_filename); - if !sess.opts.cg.save_temps && - !sess.opts.output_types.contains(&OutputTypeBitcode) { - remove(sess, &bc_filename); + ab.add_file(&bc_deflated_filename).unwrap(); + remove(sess, &bc_deflated_filename); + if !sess.opts.cg.save_temps && + !sess.opts.output_types.contains(&OutputTypeBitcode) { + remove(sess, &bc_filename); + } } } diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs index eeba34064905a..d7f183faa0192 100644 --- a/src/librustc/back/lto.rs +++ b/src/librustc/back/lto.rs @@ -67,7 +67,14 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, archive.read(format!("{}.bytecode.deflate", file).as_slice()) }); - let bc_encoded = bc_encoded.expect("missing compressed bytecode in archive!"); + let bc_encoded = match bc_encoded { + Some(data) => data, + None => { + sess.fatal(format!("missing compressed bytecode in {} \ + (perhaps it was compiled with -C codegen-units > 1)", + path.display()).as_slice()); + }, + }; let bc_extractor = if is_versioned_bytecode_format(bc_encoded) { |_| { // Read the version @@ -120,7 +127,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, if !llvm::LLVMRustLinkInExternalBitcode(llmod, ptr as *const libc::c_char, bc_decoded.len() as libc::size_t) { - write::llvm_err(sess, + write::llvm_err(sess.diagnostic().handler(), format!("failed to load bc of `{}`", name.as_slice())); } diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index c234539036603..076338ccef84f 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -9,8 +9,8 @@ // except according to those terms. use back::lto; -use back::link::get_cc_prog; -use driver::driver::{CrateTranslation, OutputFilenames}; +use back::link::{get_cc_prog, remove}; +use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames}; use driver::config::NoDebugInfo; use driver::session::Session; use driver::config; @@ -18,11 +18,18 @@ use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef}; use util::common::time; use syntax::abi; +use syntax::codemap; +use syntax::diagnostic; +use syntax::diagnostic::{Emitter, Handler, Level, mk_handler}; use std::c_str::{ToCStr, CString}; use std::io::Command; +use std::io::fs; +use std::iter::Unfold; use std::ptr; use std::str; +use std::sync::{Arc, Mutex}; +use std::task::TaskBuilder; use libc::{c_uint, c_int}; @@ -36,23 +43,23 @@ pub enum OutputType { } -pub fn llvm_err(sess: &Session, msg: String) -> ! { +pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! { unsafe { let cstr = llvm::LLVMRustGetLastError(); if cstr == ptr::null() { - sess.fatal(msg.as_slice()); + handler.fatal(msg.as_slice()); } else { let err = CString::new(cstr, true); let err = String::from_utf8_lossy(err.as_bytes()); - sess.fatal(format!("{}: {}", - msg.as_slice(), - err.as_slice()).as_slice()); + handler.fatal(format!("{}: {}", + msg.as_slice(), + err.as_slice()).as_slice()); } } } pub fn write_output_file( - sess: &Session, + handler: &diagnostic::Handler, target: llvm::TargetMachineRef, pm: llvm::PassManagerRef, m: ModuleRef, @@ -63,13 +70,74 @@ pub fn write_output_file( let result = llvm::LLVMRustWriteOutputFile( target, pm, m, output, file_type); if !result { - llvm_err(sess, "could not write output".to_string()); + llvm_err(handler, "could not write output".to_string()); } }) } } +struct Diagnostic { + msg: String, + code: Option, + lvl: Level, +} + +// We use an Arc instead of just returning a list of diagnostics from the +// child task because we need to make sure that the messages are seen even +// if the child task fails (for example, when `fatal` is called). +#[deriving(Clone)] +struct SharedEmitter { + buffer: Arc>>, +} + +impl SharedEmitter { + fn new() -> SharedEmitter { + SharedEmitter { + buffer: Arc::new(Mutex::new(Vec::new())), + } + } + + fn dump(&mut self, handler: &Handler) { + let mut buffer = self.buffer.lock(); + for diag in buffer.iter() { + match diag.code { + Some(ref code) => { + handler.emit_with_code(None, + diag.msg.as_slice(), + code.as_slice(), + diag.lvl); + }, + None => { + handler.emit(None, + diag.msg.as_slice(), + diag.lvl); + }, + } + } + buffer.clear(); + } +} + +impl Emitter for SharedEmitter { + fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>, + msg: &str, code: Option<&str>, lvl: Level) { + assert!(cmsp.is_none(), "SharedEmitter doesn't support spans"); + + self.buffer.lock().push(Diagnostic { + msg: msg.to_string(), + code: code.map(|s| s.to_string()), + lvl: lvl, + }); + } + + fn custom_emit(&mut self, _cm: &codemap::CodeMap, + _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) { + fail!("SharedEmitter doesn't support custom_emit"); + } +} + + // On android, we by default compile for armv7 processors. This enables // things like double word CAS instructions (rather than emulating them) // which are *far* more efficient. This is obviously undesirable in some @@ -98,77 +166,68 @@ fn target_feature<'a>(sess: &'a Session) -> &'a str { } } -pub fn run_passes(sess: &Session, - trans: &CrateTranslation, - output_types: &[OutputType], - output: &OutputFilenames) { - let llmod = trans.module; - let llcx = trans.context; - unsafe { - configure_llvm(sess); +fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { + match optimize { + config::No => llvm::CodeGenLevelNone, + config::Less => llvm::CodeGenLevelLess, + config::Default => llvm::CodeGenLevelDefault, + config::Aggressive => llvm::CodeGenLevelAggressive, + } +} - if sess.opts.cg.save_temps { - output.with_extension("no-opt.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) +fn create_target_machine(sess: &Session) -> TargetMachineRef { + let reloc_model = match sess.opts.cg.relocation_model.as_slice() { + "pic" => llvm::RelocPIC, + "static" => llvm::RelocStatic, + "default" => llvm::RelocDefault, + "dynamic-no-pic" => llvm::RelocDynamicNoPic, + _ => { + sess.err(format!("{} is not a valid relocation mode", + sess.opts + .cg + .relocation_model).as_slice()); + sess.abort_if_errors(); + unreachable!(); } + }; - let opt_level = match sess.opts.optimize { - config::No => llvm::CodeGenLevelNone, - config::Less => llvm::CodeGenLevelLess, - config::Default => llvm::CodeGenLevelDefault, - config::Aggressive => llvm::CodeGenLevelAggressive, - }; - let use_softfp = sess.opts.cg.soft_float; - - // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. - // FIXME: #11954: mac64 unwinding may not work with fp elim - let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || - (sess.targ_cfg.os == abi::OsMacos && - sess.targ_cfg.arch == abi::X86_64); - - // OSX has -dead_strip, which doesn't rely on ffunction_sections - // FIXME(#13846) this should be enabled for windows - let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && - sess.targ_cfg.os != abi::OsWindows; - let fdata_sections = ffunction_sections; - - let reloc_model = match sess.opts.cg.relocation_model.as_slice() { - "pic" => llvm::RelocPIC, - "static" => llvm::RelocStatic, - "default" => llvm::RelocDefault, - "dynamic-no-pic" => llvm::RelocDynamicNoPic, - _ => { - sess.err(format!("{} is not a valid relocation mode", - sess.opts - .cg - .relocation_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; - - let code_model = match sess.opts.cg.code_model.as_slice() { - "default" => llvm::CodeModelDefault, - "small" => llvm::CodeModelSmall, - "kernel" => llvm::CodeModelKernel, - "medium" => llvm::CodeModelMedium, - "large" => llvm::CodeModelLarge, - _ => { - sess.err(format!("{} is not a valid code model", - sess.opts - .cg - .code_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; + let opt_level = get_llvm_opt_level(sess.opts.optimize); + let use_softfp = sess.opts.cg.soft_float; + + // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. + // FIXME: #11954: mac64 unwinding may not work with fp elim + let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || + (sess.targ_cfg.os == abi::OsMacos && + sess.targ_cfg.arch == abi::X86_64); + + // OSX has -dead_strip, which doesn't rely on ffunction_sections + // FIXME(#13846) this should be enabled for windows + let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && + sess.targ_cfg.os != abi::OsWindows; + let fdata_sections = ffunction_sections; + + let code_model = match sess.opts.cg.code_model.as_slice() { + "default" => llvm::CodeModelDefault, + "small" => llvm::CodeModelSmall, + "kernel" => llvm::CodeModelKernel, + "medium" => llvm::CodeModelMedium, + "large" => llvm::CodeModelLarge, + _ => { + sess.err(format!("{} is not a valid code model", + sess.opts + .cg + .code_model).as_slice()); + sess.abort_if_errors(); + unreachable!(); + } + }; - let tm = sess.targ_cfg - .target_strs - .target_triple - .as_slice() - .with_c_str(|t| { + unsafe { + sess.targ_cfg + .target_strs + .target_triple + .as_slice() + .with_c_str(|t| { sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| { target_feature(sess).with_c_str(|features| { llvm::LLVMRustCreateTargetMachine( @@ -184,160 +243,531 @@ pub fn run_passes(sess: &Session, ) }) }) - }); + }) + } +} - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass: &str| { - pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) - }; - if !sess.no_verify() { assert!(addpass("verify")); } - if !sess.opts.cg.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - populate_llvm_passes(fpm, mpm, llmod, opt_level, - trans.no_builtins); +/// Module-specific configuration for `optimize_and_codegen`. +#[deriving(Clone)] +struct ModuleConfig { + /// LLVM TargetMachine to use for codegen. + tm: TargetMachineRef, + /// Names of additional optimization passes to run. + passes: Vec, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + opt_level: Option, + + // Flags indicating which outputs to produce. + emit_no_opt_bc: bool, + emit_bc: bool, + emit_lto_bc: bool, + emit_ir: bool, + emit_asm: bool, + emit_obj: bool, + + // Miscellaneous flags. These are mostly copied from command-line + // options. + no_verify: bool, + no_prepopulate_passes: bool, + no_builtins: bool, + time_passes: bool, +} + +impl ModuleConfig { + fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { + ModuleConfig { + tm: tm, + passes: passes, + opt_level: None, + + emit_no_opt_bc: false, + emit_bc: false, + emit_lto_bc: false, + emit_ir: false, + emit_asm: false, + emit_obj: false, + + no_verify: false, + no_prepopulate_passes: false, + no_builtins: false, + time_passes: false, } + } - for pass in sess.opts.cg.passes.iter() { - pass.as_slice().with_c_str(|s| { - if !llvm::LLVMRustAddPass(mpm, s) { - sess.warn(format!("unknown pass {}, ignoring", - *pass).as_slice()); - } - }) + fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { + self.no_verify = sess.no_verify(); + self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; + self.no_builtins = trans.no_builtins; + self.time_passes = sess.time_passes(); + } +} + +/// Additional resources used by optimize_and_codegen (not module specific) +struct CodegenContext<'a> { + // Extra resources used for LTO: (sess, reachable). This will be `None` + // when running in a worker thread. + lto_ctxt: Option<(&'a Session, &'a [String])>, + // Handler to use for diagnostics produced during codegen. + handler: &'a Handler, +} + +impl<'a> CodegenContext<'a> { + fn new(handler: &'a Handler) -> CodegenContext<'a> { + CodegenContext { + lto_ctxt: None, + handler: handler, } + } - // Finally, run the actual optimization passes - time(sess.time_passes(), "llvm function passes", (), |()| - llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(sess.time_passes(), "llvm module passes", (), |()| - llvm::LLVMRunPassManager(mpm, llmod)); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - - // Emit the bytecode if we're either saving our temporaries or - // emitting an rlib. Whenever an rlib is created, the bytecode is - // inserted into the archive in order to allow LTO against it. - if sess.opts.cg.save_temps || - (sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains(&OutputTypeExe)) { - output.temp_path(OutputTypeBitcode).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) + fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> { + CodegenContext { + lto_ctxt: Some((sess, reachable)), + handler: sess.diagnostic().handler(), } + } +} - if sess.lto() { - time(sess.time_passes(), "all lto passes", (), |()| - lto::run(sess, llmod, tm, trans.reachable.as_slice())); +// Unsafe due to LLVM calls. +unsafe fn optimize_and_codegen(cgcx: &CodegenContext, + mtrans: ModuleTranslation, + config: ModuleConfig, + name_extra: String, + output_names: OutputFilenames) { + let ModuleTranslation { llmod, llcx } = mtrans; + let tm = config.tm; + + if config.emit_no_opt_bc { + let ext = format!("{}.no-opt.bc", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } - if sess.opts.cg.save_temps { - output.with_extension("lto.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); + match config.opt_level { + Some(opt_level) => { + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass: &str| { + pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) + }; + if !config.no_verify { assert!(addpass("verify")); } + + if !config.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + populate_llvm_passes(fpm, mpm, llmod, opt_level, + config.no_builtins); + } + + for pass in config.passes.iter() { + pass.as_slice().with_c_str(|s| { + if !llvm::LLVMRustAddPass(mpm, s) { + cgcx.handler.warn(format!("unknown pass {}, ignoring", + *pass).as_slice()); + } }) } - } - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, - no_builtins: bool, f: |PassManagerRef|) { - unsafe { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm); - llvm::LLVMDisposePassManager(cpm); + // Finally, run the actual optimization passes + time(config.time_passes, "llvm function passes", (), |()| + llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + time(config.time_passes, "llvm module passes", (), |()| + llvm::LLVMRunPassManager(mpm, llmod)); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + + match cgcx.lto_ctxt { + Some((sess, reachable)) if sess.lto() => { + time(sess.time_passes(), "all lto passes", (), |()| + lto::run(sess, llmod, tm, reachable)); + + if config.emit_lto_bc { + let name = format!("{}.lto.bc", name_extra); + output_names.with_extension(name.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + }, + _ => {}, } + }, + None => {}, + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, + no_builtins: bool, f: |PassManagerRef|) { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm); + llvm::LLVMDisposePassManager(cpm); + } + + if config.emit_bc { + let ext = format!("{}.bc", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + time(config.time_passes, "codegen passes", (), |()| { + if config.emit_ir { + let ext = format!("{}.ll", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|output| { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, output); + }) + }) } - let mut object_file = None; - let mut needs_metadata = false; - for output_type in output_types.iter() { - let path = output.path(*output_type); - match *output_type { - OutputTypeBitcode => { - path.with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - OutputTypeLlvmAssembly => { - path.with_c_str(|output| { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, output); - }) - }) - } - OutputTypeAssembly => { - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, - // so in this case we still want the metadata object - // file. - let ty = OutputTypeAssembly; - let path = if sess.opts.output_types.contains(&ty) { - path - } else { - needs_metadata = true; - output.temp_path(OutputTypeAssembly) - }; - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, &path, - llvm::AssemblyFile); - }); - } - OutputTypeObject => { - object_file = Some(path); - } - OutputTypeExe => { - object_file = Some(output.temp_path(OutputTypeObject)); - needs_metadata = true; + if config.emit_asm { + let path = output_names.with_extension(format!("{}.s", name_extra).as_slice()); + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFile); + }); + } + + if config.emit_obj { + let path = output_names.with_extension(format!("{}.o", name_extra).as_slice()); + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFile); + }); + } + }); + + llvm::LLVMDisposeModule(llmod); + llvm::LLVMContextDispose(llcx); + llvm::LLVMRustDisposeTargetMachine(tm); +} + +pub fn run_passes(sess: &Session, + trans: &CrateTranslation, + output_types: &[OutputType], + crate_output: &OutputFilenames) { + // It's possible that we have `codegen_units > 1` but only one item in + // `trans.modules`. We could theoretically proceed and do LTO in that + // case, but it would be confusing to have the validity of + // `-Z lto -C codegen-units=2` depend on details of the crate being + // compiled, so we complain regardless. + if sess.lto() && sess.opts.cg.codegen_units > 1 { + // This case is impossible to handle because LTO expects to be able + // to combine the entire crate and all its dependencies into a + // single compilation unit, but each codegen unit is in a separate + // LLVM context, so they can't easily be combined. + sess.fatal("can't perform LTO when using multiple codegen units"); + } + + unsafe { + configure_llvm(sess); + } + + let tm = create_target_machine(sess); + + // Figure out what we actually need to build. + + let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(tm, vec!()); + + modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); + + // Save all versions of the bytecode if we're saving our temporaries. + if sess.opts.cg.save_temps { + modules_config.emit_no_opt_bc = true; + modules_config.emit_bc = true; + modules_config.emit_lto_bc = true; + metadata_config.emit_bc = true; + } + + // Emit a bitcode file for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the + // archive in order to allow LTO against it. + let needs_crate_bitcode = + sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains(&OutputTypeExe) && + sess.opts.cg.codegen_units == 1; + if needs_crate_bitcode { + modules_config.emit_bc = true; + } + + for output_type in output_types.iter() { + match *output_type { + OutputTypeBitcode => { modules_config.emit_bc = true; }, + OutputTypeLlvmAssembly => { modules_config.emit_ir = true; }, + OutputTypeAssembly => { + modules_config.emit_asm = true; + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, so + // in this case we still want the metadata object file. + if !sess.opts.output_types.contains(&OutputTypeAssembly) { + metadata_config.emit_obj = true; } + }, + OutputTypeObject => { modules_config.emit_obj = true; }, + OutputTypeExe => { + modules_config.emit_obj = true; + metadata_config.emit_obj = true; + }, + } + } + + modules_config.set_flags(sess, trans); + metadata_config.set_flags(sess, trans); + + + // Populate a buffer with a list of codegen tasks. Items are processed in + // LIFO order, just because it's a tiny bit simpler that way. (The order + // doesn't actually matter.) + let mut work_items = Vec::with_capacity(1 + trans.modules.len()); + + { + let work = build_work_item(sess, + trans.metadata_module, + metadata_config.clone(), + crate_output.clone(), + "metadata".to_string()); + work_items.push(work); + } + + for (index, mtrans) in trans.modules.iter().enumerate() { + let work = build_work_item(sess, + *mtrans, + modules_config.clone(), + crate_output.clone(), + format!("{}", index)); + work_items.push(work); + } + + // Process the work items, optionally using worker threads. + if sess.opts.cg.codegen_units == 1 { + run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items); + + if needs_crate_bitcode { + // The only bitcode file produced (aside from metadata) was + // "crate.0.bc". Rename to "crate.bc" since that's what + // `link_rlib` expects to find. + fs::copy(&crate_output.with_extension("0.bc"), + &crate_output.temp_path(OutputTypeBitcode)).unwrap(); + } + } else { + run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units); + + assert!(!needs_crate_bitcode, + "can't produce a crate bitcode file from multiple compilation units"); + } + + // All codegen is finished. + unsafe { + llvm::LLVMRustDisposeTargetMachine(tm); + } + + // Produce final compile outputs. + + let copy_if_one_unit = |ext: &str, output_type: OutputType| { + // Three cases: + if sess.opts.cg.codegen_units == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + fs::copy(&crate_output.with_extension(ext), + &crate_output.path(output_type)).unwrap(); + } else { + if crate_output.single_output_file.is_some() { + // 2) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(format!("ignoring specified output filename \ + because multiple .{} files were produced", + ext).as_slice()); + } else { + // 3) Multiple codegen units, but no `-o some_name`. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) } } + }; + + let link_obj = |output_path: &Path| { + let mut cmd = Command::new("ld"); + + for index in range(0, trans.modules.len()) { + cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice())); + } + + cmd.arg("-r").arg("-o").arg(output_path); + cmd.stdin(::std::io::process::Ignored) + .stdout(::std::io::process::InheritFd(1)) + .stderr(::std::io::process::InheritFd(2)); + cmd.status().unwrap(); + }; - time(sess.time_passes(), "codegen passes", (), |()| { - match object_file { - Some(ref path) => { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, path, - llvm::ObjectFile); - }); + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + // FIXME: Since we don't support LTO anyway, maybe we can avoid + // producing the temporary .0.bc's in the first place? + let mut save_bitcode = false; + for output_type in output_types.iter() { + match *output_type { + OutputTypeBitcode => { + save_bitcode = true; + copy_if_one_unit("0.bc", OutputTypeBitcode); + }, + OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly); }, + OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly); }, + OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); }, + OutputTypeExe => { + // If OutputTypeObject is already in the list, then + // `crate.o` will be handled by the OutputTypeObject case. + // Otherwise, we need to create the temporary object so we + // can run the linker. + if !sess.opts.output_types.contains(&OutputTypeObject) { + link_obj(&crate_output.temp_path(OutputTypeObject)); } - None => {} + }, + } + } + let save_bitcode = save_bitcode; + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - crate.0.bc + // - crate.0.o + // - crate.metadata.bc + // - crate.metadata.o + // - crate.o (linked from crate.##.o) + // - crate.bc (copied from crate.0.bc, or an empty bitcode file) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .0.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), we must remove + // .0.bc as well. (We don't touch the crate.bc that may have been + // produced earlier.) + for i in range(0, trans.modules.len()) { + if modules_config.emit_obj { + let ext = format!("{}.o", i); + remove(sess, &crate_output.with_extension(ext.as_slice())); } - if needs_metadata { - with_codegen(tm, trans.metadata_module, - trans.no_builtins, |cpm| { - let out = output.temp_path(OutputTypeObject) - .with_extension("metadata.o"); - write_output_file(sess, tm, cpm, - trans.metadata_module, &out, - llvm::ObjectFile); - }) + + if modules_config.emit_bc && !save_bitcode { + let ext = format!("{}.bc", i); + remove(sess, &crate_output.with_extension(ext.as_slice())); + } + } + + if metadata_config.emit_bc && !save_bitcode { + remove(sess, &crate_output.with_extension("metadata.bc")); + } + } + + // We leave the following files around by default: + // - crate.o + // - crate.metadata.o + // - crate.bc + // These are used in linking steps and will be cleaned up afterward. + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + //if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } +} + +type WorkItem = proc(&CodegenContext):Send; + +fn build_work_item(sess: &Session, + mtrans: ModuleTranslation, + config: ModuleConfig, + output_names: OutputFilenames, + name_extra: String) -> WorkItem { + let mut config = config; + config.tm = create_target_machine(sess); + + proc(cgcx) unsafe { + optimize_and_codegen(cgcx, mtrans, config, name_extra, output_names); + } +} + +fn run_work_singlethreaded(sess: &Session, + reachable: &[String], + work_items: Vec) { + let cgcx = CodegenContext::new_with_session(sess, reachable); + let mut work_items = work_items; + + // Since we're running single-threaded, we can pass the session to + // the proc, allowing `optimize_and_codegen` to perform LTO. + for work in Unfold::new((), |_| work_items.pop()) { + work(&cgcx); + } +} + +fn run_work_multithreaded(sess: &Session, + work_items: Vec, + num_workers: uint) { + // Run some workers to process the work items. + let work_items_arc = Arc::new(Mutex::new(work_items)); + let mut diag_emitter = SharedEmitter::new(); + let mut futures = Vec::with_capacity(num_workers); + + for i in range(0, num_workers) { + let work_items_arc = work_items_arc.clone(); + let diag_emitter = diag_emitter.clone(); + + let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() { + let diag_handler = mk_handler(box diag_emitter); + + // Must construct cgcx inside the proc because it has non-Send + // fields. + let cgcx = CodegenContext::new(&diag_handler); + + loop { + // Avoid holding the lock for the entire duration of the match. + let maybe_work = work_items_arc.lock().pop(); + match maybe_work { + Some(work) => { + work(&cgcx); + + // Make sure to fail the worker so the main thread can + // tell that there were errors. + cgcx.handler.abort_if_errors(); + } + None => break, + } } }); + futures.push(future); + } - llvm::LLVMRustDisposeTargetMachine(tm); - llvm::LLVMDisposeModule(trans.metadata_module); - llvm::LLVMDisposeModule(llmod); - llvm::LLVMContextDispose(llcx); - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + let mut failed = false; + for future in futures.move_iter() { + match future.unwrap() { + Ok(()) => {}, + Err(_) => { + failed = true; + }, + } + // Display any new diagnostics. + diag_emitter.dump(sess.diagnostic().handler()); + } + if failed { + sess.fatal("aborting due to worker thread failure"); } } diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 04315149a784e..3e3a88ceffd67 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -303,6 +303,13 @@ macro_rules! cgoptions( } } + fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool { + use std::from_str::FromStr; + match v.and_then(FromStr::from_str) { + Some(i) => { *slot = i; true }, + None => false + } + } } ) ) @@ -347,6 +354,8 @@ cgoptions!( "metadata to mangle symbol names with"), extra_filename: String = ("".to_string(), parse_string, "extra data to put in each output filename"), + codegen_units: uint = (1, parse_uint, + "divide crate into N units to optimize in parallel"), ) pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index fa1a1b38a2d0d..09bf69bff4cdd 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -442,11 +442,14 @@ pub fn phase_save_analysis(sess: &Session, middle::save::process_crate(sess, krate, analysis, odir)); } +pub struct ModuleTranslation { + pub llcx: ContextRef, + pub llmod: ModuleRef, +} + pub struct CrateTranslation { - pub context: ContextRef, - pub module: ModuleRef, - pub metadata_context: ContextRef, - pub metadata_module: ModuleRef, + pub modules: Vec, + pub metadata_module: ModuleTranslation, pub link: LinkMeta, pub metadata: Vec, pub reachable: Vec, @@ -681,6 +684,7 @@ pub fn collect_crate_metadata(session: &Session, session.opts.cg.metadata.clone() } +#[deriving(Clone)] pub struct OutputFilenames { pub out_directory: Path, pub out_filestem: String, diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index dc8dabda83438..d56fb31fb20dc 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -29,7 +29,7 @@ use back::link::{mangle_exported_name}; use back::{link, abi}; use driver::config; use driver::config::{NoDebugInfo, FullDebugInfo}; -use driver::driver::{CrateAnalysis, CrateTranslation}; +use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation}; use driver::session::Session; use lint; use llvm::{BasicBlockRef, ModuleRef, ValueRef, Vector, get_param}; @@ -47,8 +47,8 @@ use middle::trans::builder::{Builder, noname}; use middle::trans::callee; use middle::trans::cleanup::{CleanupMethods, ScopeId}; use middle::trans::cleanup; -use middle::trans::common::{Block, C_bool, C_bytes, C_i32, C_integral, C_nil}; -use middle::trans::common::{C_null, C_struct, C_u64, C_u8, C_uint, C_undef}; +use middle::trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral, C_nil}; +use middle::trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_uint, C_undef}; use middle::trans::common::{CrateContext, ExternMap, FunctionContext}; use middle::trans::common::{NodeInfo, Result, SubstP, monomorphize_type}; use middle::trans::common::{node_id_type, param_substs, return_type_is_void}; @@ -2852,8 +2852,8 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec { cx.sess().fatal("failed to compress metadata") } }.as_slice()); - let llmeta = C_bytes(cx, compressed.as_slice()); - let llconst = C_struct(cx, [llmeta], false); + let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice()); + let llconst = C_struct_in_context(cx.metadata_llcx(), [llmeta], false); let name = format!("rust_metadata_{}_{}", cx.link_meta().crate_name, cx.link_meta().crate_hash); @@ -2896,8 +2896,7 @@ pub fn trans_crate(krate: ast::Crate, let link_meta = link::build_link_meta(&tcx.sess, &krate, name); - // Multiple compilation units won't be supported until a later commit. - let codegen_units = 1; + let codegen_units = tcx.sess.opts.cg.codegen_units; let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(), codegen_units, tcx, @@ -2957,8 +2956,9 @@ pub fn trans_crate(krate: ast::Crate, } } - let llcx = shared_ccx.get_ccx(0).llcx(); - let llmod = shared_ccx.get_ccx(0).llmod(); + let modules = shared_ccx.iter() + .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) + .collect(); let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { shared_ccx.item_symbols().borrow().find(id).map(|s| s.to_string()) @@ -2988,20 +2988,22 @@ pub fn trans_crate(krate: ast::Crate, // referenced from rt/rust_try.ll reachable.push("rust_eh_personality_catch".to_string()); - let metadata_module = shared_ccx.metadata_llmod(); - let metadata_context = shared_ccx.metadata_llcx(); + let metadata_module = ModuleTranslation { + llcx: shared_ccx.metadata_llcx(), + llmod: shared_ccx.metadata_llmod(), + }; let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins"); - (shared_ccx.take_tcx(), CrateTranslation { - context: llcx, - module: llmod, - link: link_meta, + let translation = CrateTranslation { + modules: modules, metadata_module: metadata_module, - metadata_context: metadata_context, + link: link_meta, metadata: metadata, reachable: reachable, crate_formats: formats, no_builtins: no_builtins, - }) + }; + + (shared_ccx.take_tcx(), translation) } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index dbd3de2a9986a..4ea60a4e128ad 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -14,7 +14,7 @@ use driver::session::Session; use llvm; -use llvm::{ValueRef, BasicBlockRef, BuilderRef}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; use llvm::{True, False, Bool}; use middle::def; use middle::freevars; @@ -669,9 +669,13 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { } } -pub fn C_struct(ccx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { +pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { + C_struct_in_context(cx.llcx(), elts, packed) +} + +pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { unsafe { - llvm::LLVMConstStructInContext(ccx.llcx(), + llvm::LLVMConstStructInContext(llcx, elts.as_ptr(), elts.len() as c_uint, packed as Bool) } @@ -689,10 +693,14 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { } } -pub fn C_bytes(ccx: &CrateContext, bytes: &[u8]) -> ValueRef { +pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef { + C_bytes_in_context(cx.llcx(), bytes) +} + +pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { unsafe { let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(ccx.llcx(), ptr, bytes.len() as c_uint, True); + return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); } } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 130e382e4e897..a25070e89ee05 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -148,6 +148,27 @@ pub struct CrateContext<'a> { local: &'a LocalCrateContext, } +pub struct CrateContextIterator<'a> { + shared: &'a SharedCrateContext, + index: uint, +} + +impl<'a> Iterator> for CrateContextIterator<'a> { + fn next(&mut self) -> Option> { + if self.index >= self.shared.local_ccxs.len() { + return None; + } + + let index = self.index; + self.index += 1; + + Some(CrateContext { + shared: self.shared, + local: &self.shared.local_ccxs[index], + }) + } +} + unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { let llcx = llvm::LLVMContextCreate(); let llmod = mod_name.with_c_str(|buf| { @@ -226,6 +247,13 @@ impl SharedCrateContext { shared_ccx } + pub fn iter<'a>(&'a self) -> CrateContextIterator<'a> { + CrateContextIterator { + shared: self, + index: 0, + } + } + pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a> { CrateContext { shared: self, diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 12d10ec66302a..ceb29ddcf8fe5 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -331,7 +331,7 @@ pub enum AsmDialect { AD_Intel = 1 } -#[deriving(PartialEq)] +#[deriving(PartialEq, Clone)] #[repr(C)] pub enum CodeGenOptLevel { CodeGenLevelNone = 0, From 2e7bc0f808d9ad874010ac610ad796d07127c458 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Mon, 28 Jul 2014 14:45:27 -0700 Subject: [PATCH 05/14] reuse original symbols for inlined items When inlining an item from another crate, use the original symbol from that crate's metadata instead of generating a new symbol using the `ast::NodeId` of the inlined copy. This requires exporting symbols in the crate metadata in a few additional cases. Having predictable symbols for inlined items will be useful later to avoid generating duplicate object code for inlined items. --- src/librustc/metadata/encoder.rs | 9 +++++---- src/librustc/middle/trans/base.rs | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 1386e23b77dfc..da73f25b6d164 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -892,7 +892,8 @@ fn encode_info_for_method(ecx: &EncodeContext, IITraitItemRef(local_def(parent_id), RequiredInlinedTraitItemRef( &*ast_method))); - } else { + } + if !any_types { encode_symbol(ecx, rbml_w, m.def_id.node); } encode_method_argument_names(rbml_w, &*ast_method.pe_fn_decl()); @@ -1047,7 +1048,8 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_attributes(rbml_w, item.attrs.as_slice()); if tps_len > 0u || should_inline(item.attrs.as_slice()) { encode_inlined_item(ecx, rbml_w, IIItemRef(item)); - } else { + } + if tps_len == 0 { encode_symbol(ecx, rbml_w, item.id); } encode_visibility(rbml_w, vis); @@ -1411,9 +1413,8 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext, encode_name(rbml_w, nitem.ident.name); if abi == abi::RustIntrinsic { encode_inlined_item(ecx, rbml_w, IIForeignRef(nitem)); - } else { - encode_symbol(ecx, rbml_w, nitem.id); } + encode_symbol(ecx, rbml_w, nitem.id); } ForeignItemStatic(_, mutbl) => { if mutbl { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index d56fb31fb20dc..c1b92f71878f1 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2554,6 +2554,15 @@ pub fn create_entry_wrapper(ccx: &CrateContext, fn exported_name(ccx: &CrateContext, id: ast::NodeId, ty: ty::t, attrs: &[ast::Attribute]) -> String { + match ccx.external_srcs().borrow().find(&id) { + Some(&did) => { + let sym = csearch::get_symbol(&ccx.sess().cstore, did); + debug!("found item {} in other crate...", sym); + return sym; + } + None => {} + } + match attr::first_attr_value_str_by_name(attrs, "export_name") { // Use provided name Some(name) => name.get().to_string(), @@ -2597,16 +2606,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { // using the current crate's name/version // information in the hash of the symbol debug!("making {}", sym); - let (sym, is_local) = { - match ccx.external_srcs().borrow().find(&i.id) { - Some(&did) => { - debug!("but found in other crate..."); - (csearch::get_symbol(&ccx.sess().cstore, - did), false) - } - None => (sym, true) - } - }; + let is_local = !ccx.external_srcs().borrow().contains_key(&id); // We need the translated value here, because for enums the // LLVM type is not fully determined by the Rust type. From da9606247d5ddd0edebafaffd0367c541fbaee7e Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Mon, 21 Jul 2014 16:42:34 -0700 Subject: [PATCH 06/14] 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. --- src/librustc/middle/trans/base.rs | 56 ++++++++++++++---------- src/librustc/middle/trans/builder.rs | 1 + src/librustc/middle/trans/consts.rs | 9 ++++ src/librustc/middle/trans/context.rs | 36 +++++++++++---- src/librustc/middle/trans/expr.rs | 19 ++++++++ src/librustc/middle/trans/foreign.rs | 12 +++++ src/librustc/middle/trans/inline.rs | 65 ++++++++++++++++++++-------- src/librustc/middle/trans/meth.rs | 1 + 8 files changed, 149 insertions(+), 50 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index c1b92f71878f1..81f76a82a546c 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 { +pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { 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 61a79f4a8eeba..322a6a3cc909e 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 2571ae1fe72e1..bd5132ea42736 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 a25070e89ee05..4184c49b90508 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>, intrinsics: RefCell>, + + /// 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, } 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> { &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 { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index fcb0ac9d9306f..61c27292a3767 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 240c109e17bd3..8ed45f89c29e4 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 e8a56f4a9265d..0713b2b535c07 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) ¶m_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 9c587d08f01c1..92d8db0e4eafa 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, ¶m_substs::empty(), method.id, []); + update_linkage(ccx, llfn, method.id); } let mut v = TransItemVisitor { ccx: ccx, From e09bef810a95c82fa5de08872fccffdd5e0fe1e7 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Thu, 31 Jul 2014 16:45:29 -0700 Subject: [PATCH 07/14] avoid duplicate translation of monomorphizations, drop glue, and visit glue Use a shared lookup table of previously-translated monomorphizations/glue functions to avoid translating those functions in every compilation unit where they're used. Instead, the function will be translated in whichever compilation unit uses it first, and the remaining compilation units will link against that original definition. --- src/librustc/middle/trans/base.rs | 27 ++++++++--- src/librustc/middle/trans/context.rs | 19 ++++++++ src/librustc/middle/trans/glue.rs | 55 +++++++++++++++++++---- src/librustc/middle/trans/meth.rs | 2 +- src/librustc/middle/trans/monomorphize.rs | 42 ++++++++++------- 5 files changed, 114 insertions(+), 31 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 81f76a82a546c..e85e61d829187 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2124,11 +2124,24 @@ 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); +/// Set the appropriate linkage for an LLVM `ValueRef` (function or global). +/// If the `llval` is the direct translation of a specific Rust item, `id` +/// should be set to the `NodeId` of that item. (This mapping should be +/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to +/// `None`.) +pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option) { + match id { + Some(id) if ccx.reachable().contains(&id) => { + llvm::SetLinkage(llval, llvm::ExternalLinkage); + }, + _ => { + // `id` does not refer to an item in `ccx.reachable`. + if ccx.sess().opts.cg.codegen_units > 1 { + llvm::SetLinkage(llval, llvm::ExternalLinkage); + } else { + llvm::SetLinkage(llval, llvm::InternalLinkage); + } + }, } } @@ -2157,7 +2170,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { item.id, item.attrs.as_slice()); } - update_linkage(ccx, llfn, item.id); + update_linkage(ccx, llfn, Some(item.id)); } // Be sure to travel more than just one layer deep to catch nested @@ -2185,7 +2198,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { consts::trans_const(ccx, m, item.id); let g = get_item_val(ccx, item.id); - update_linkage(ccx, g, item.id); + update_linkage(ccx, g, Some(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 diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 4184c49b90508..64722208aa5f4 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -72,6 +72,10 @@ pub struct SharedCrateContext { symbol_hasher: RefCell, tcx: ty::ctxt, stats: Stats, + + available_monomorphizations: RefCell>, + available_drop_glues: RefCell>, + available_visit_glues: RefCell>, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -233,6 +237,9 @@ impl SharedCrateContext { llvm_insns: RefCell::new(HashMap::new()), fn_stats: RefCell::new(Vec::new()), }, + available_monomorphizations: RefCell::new(HashSet::new()), + available_drop_glues: RefCell::new(HashMap::new()), + available_visit_glues: RefCell::new(HashMap::new()), }; for i in range(0, local_count) { @@ -612,6 +619,18 @@ impl<'b> CrateContext<'b> { &self.shared.stats } + pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_monomorphizations + } + + pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_drop_glues + } + + pub fn available_visit_glues<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_visit_glues + } + pub fn int_type(&self) -> Type { self.local.int_type } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 915c171b31816..e0ef867c23eb3 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -171,11 +171,30 @@ pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { }; let llfnty = Type::glue_fn(ccx, llty); - let glue = declare_generic_glue(ccx, t, llfnty, "drop"); + + let (glue, new_sym) = match ccx.available_drop_glues().borrow().find(&t) { + Some(old_sym) => { + let glue = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil()); + (glue, None) + }, + None => { + let (sym, glue) = declare_generic_glue(ccx, t, llfnty, "drop"); + (glue, Some(sym)) + }, + }; ccx.drop_glues().borrow_mut().insert(t, glue); - make_generic_glue(ccx, t, glue, make_drop_glue, "drop"); + // To avoid infinite recursion, don't `make_drop_glue` until after we've + // added the entry to the `drop_glues` cache. + match new_sym { + Some(sym) => { + ccx.available_drop_glues().borrow_mut().insert(t, sym); + // We're creating a new drop glue, so also generate a body. + make_generic_glue(ccx, t, glue, make_drop_glue, "drop"); + }, + None => {}, + } glue } @@ -189,9 +208,28 @@ pub fn lazily_emit_visit_glue(ccx: &CrateContext, ti: &tydesc_info) -> ValueRef Some(visit_glue) => visit_glue, None => { debug!("+++ lazily_emit_tydesc_glue VISIT {}", ppaux::ty_to_string(ccx.tcx(), ti.ty)); - let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, "visit"); + + let (glue_fn, new_sym) = match ccx.available_visit_glues().borrow().find(&ti.ty) { + Some(old_sym) => { + let glue_fn = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil()); + (glue_fn, None) + }, + None => { + let (sym, glue_fn) = declare_generic_glue(ccx, ti.ty, llfnty, "visit"); + (glue_fn, Some(sym)) + }, + }; + ti.visit_glue.set(Some(glue_fn)); - make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, "visit"); + + match new_sym { + Some(sym) => { + ccx.available_visit_glues().borrow_mut().insert(ti.ty, sym); + make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, "visit"); + }, + None => {}, + } + debug!("--- lazily_emit_tydesc_glue VISIT {}", ppaux::ty_to_string(ccx.tcx(), ti.ty)); glue_fn } @@ -602,15 +640,15 @@ pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info { } fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type, - name: &str) -> ValueRef { + name: &str) -> (String, ValueRef) { let _icx = push_ctxt("declare_generic_glue"); let fn_nm = mangle_internal_name_by_type_and_seq( ccx, t, format!("glue_{}", name).as_slice()); let llfn = decl_cdecl_fn(ccx, fn_nm.as_slice(), llfnty, ty::mk_nil()); - note_unique_llvm_symbol(ccx, fn_nm); - return llfn; + note_unique_llvm_symbol(ccx, fn_nm.clone()); + return (fn_nm, llfn); } fn make_generic_glue(ccx: &CrateContext, @@ -631,7 +669,8 @@ fn make_generic_glue(ccx: &CrateContext, let bcx = init_function(&fcx, false, ty::mk_nil()); - llvm::SetLinkage(llfn, llvm::InternalLinkage); + update_linkage(ccx, llfn, None); + ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u); // All glue functions take values passed *by alias*; this is a // requirement since in many contexts glue is invoked indirectly and diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 92d8db0e4eafa..f1101060d979d 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -85,7 +85,7 @@ pub fn trans_impl(ccx: &CrateContext, ¶m_substs::empty(), method.id, []); - update_linkage(ccx, llfn, method.id); + update_linkage(ccx, llfn, Some(method.id)); } let mut v = TransItemVisitor { ccx: ccx, diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 16d1ad810b775..1dd0de3904d82 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -159,14 +159,18 @@ pub fn monomorphic_fn(ccx: &CrateContext, .. } => { let d = mk_lldecl(abi); + base::update_linkage(ccx, d, None); set_llvm_fn_attrs(i.attrs.as_slice(), d); - if abi != abi::Rust { - foreign::trans_rust_fn_with_foreign_abi( - ccx, &**decl, &**body, [], d, &psubsts, fn_id.node, - Some(hash.as_slice())); - } else { - trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, []); + if !ccx.available_monomorphizations().borrow().contains(&s) { + ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + if abi != abi::Rust { + foreign::trans_rust_fn_with_foreign_abi( + ccx, &**decl, &**body, [], d, &psubsts, fn_id.node, + Some(hash.as_slice())); + } else { + trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, []); + } } d @@ -201,14 +205,18 @@ pub fn monomorphic_fn(ccx: &CrateContext, match *ii { ast::MethodImplItem(mth) => { let d = mk_lldecl(abi::Rust); + base::update_linkage(ccx, d, None); set_llvm_fn_attrs(mth.attrs.as_slice(), d); - trans_fn(ccx, - &*mth.pe_fn_decl(), - &*mth.pe_body(), - d, - &psubsts, - mth.id, - []); + if !ccx.available_monomorphizations().borrow().contains(&s) { + ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + trans_fn(ccx, + &*mth.pe_fn_decl(), + &*mth.pe_body(), + d, + &psubsts, + mth.id, + []); + } d } } @@ -217,9 +225,13 @@ pub fn monomorphic_fn(ccx: &CrateContext, match *method { ast::ProvidedMethod(mth) => { let d = mk_lldecl(abi::Rust); + base::update_linkage(ccx, d, None); set_llvm_fn_attrs(mth.attrs.as_slice(), d); - trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, - &psubsts, mth.id, []); + if !ccx.available_monomorphizations().borrow().contains(&s) { + ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, + &psubsts, mth.id, []); + } d } _ => { From edc5cdcba2c217d3a4d75801190cc34096ee80c2 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 1 Aug 2014 10:29:44 -0700 Subject: [PATCH 08/14] make symbols internal when possible Add a post-processing pass to `trans` that converts symbols from external to internal when possible. Translation with multiple compilation units initially makes most symbols external, since it is not clear when translating a definition whether that symbol will need to be accessed from another compilation unit. This final pass internalizes symbols that are not reachable from other crates and not referenced from other compilation units, so that LLVM can perform more aggressive optimizations on those symbols. --- src/librustc/middle/trans/base.rs | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index e85e61d829187..bed4ded095622 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -85,6 +85,7 @@ use arena::TypedArena; use libc::{c_uint, uint64_t}; use std::c_str::ToCStr; use std::cell::{Cell, RefCell}; +use std::collections::HashSet; use std::rc::Rc; use std::{i8, i16, i32, i64}; use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel, Rust, RustCall}; @@ -2891,6 +2892,84 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { return metadata; } +/// Find any symbols that are defined in one compilation unit, but not declared +/// in any other compilation unit. Give these symbols internal linkage. +fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { + use std::c_str::CString; + + unsafe { + let mut declared = HashSet::new(); + + let iter_globals = |llmod| { + ValueIter { + cur: llvm::LLVMGetFirstGlobal(llmod), + step: llvm::LLVMGetNextGlobal, + } + }; + + let iter_functions = |llmod| { + ValueIter { + cur: llvm::LLVMGetFirstFunction(llmod), + step: llvm::LLVMGetNextFunction, + } + }; + + // Collect all external declarations in all compilation units. + for ccx in cx.iter() { + for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { + let linkage = llvm::LLVMGetLinkage(val); + // We only care about external declarations (not definitions) + // and available_externally definitions. + if !(linkage == llvm::ExternalLinkage as c_uint && + llvm::LLVMIsDeclaration(val) != 0) && + !(linkage == llvm::AvailableExternallyLinkage as c_uint) { + continue + } + + let name = CString::new(llvm::LLVMGetValueName(val), false); + declared.insert(name); + } + } + + // Examine each external definition. If the definition is not used in + // any other compilation unit, and is not reachable from other crates, + // then give it internal linkage. + for ccx in cx.iter() { + for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { + // We only care about external definitions. + if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint && + llvm::LLVMIsDeclaration(val) == 0) { + continue + } + + let name = CString::new(llvm::LLVMGetValueName(val), false); + if !declared.contains(&name) && + !reachable.contains_equiv(&name.as_str().unwrap()) { + llvm::SetLinkage(val, llvm::InternalLinkage); + } + } + } + } + + + struct ValueIter { + cur: ValueRef, + step: unsafe extern "C" fn(ValueRef) -> ValueRef, + } + + impl Iterator for ValueIter { + fn next(&mut self) -> Option { + let old = self.cur; + if !old.is_null() { + self.cur = unsafe { (self.step)(old) }; + Some(old) + } else { + None + } + } + } +} + pub fn trans_crate(krate: ast::Crate, analysis: CrateAnalysis) -> (ty::ctxt, CrateTranslation) { let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis; @@ -3009,6 +3088,10 @@ pub fn trans_crate(krate: ast::Crate, // referenced from rt/rust_try.ll reachable.push("rust_eh_personality_catch".to_string()); + if codegen_units > 1 { + internalize_symbols(&shared_ccx, &reachable.iter().map(|x| x.clone()).collect()); + } + let metadata_module = ModuleTranslation { llcx: shared_ccx.metadata_llcx(), llmod: shared_ccx.metadata_llmod(), From 73f8adcbc830b3099026832eadb1ee5f876e041b Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 1 Aug 2014 12:27:12 -0700 Subject: [PATCH 09/14] make separate compilation respect #[inline] attributes Adjust the handling of `#[inline]` items so that they get translated into every compilation unit that uses them. This is necessary to preserve the semantics of `#[inline(always)]`. Crate-local `#[inline]` functions and statics are blindly translated into every compilation unit. Cross-crate inlined items and monomorphizations of `#[inline]` functions are translated the first time a reference is seen in each compilation unit. When using multiple compilation units, inlined items are given `available_externally` linkage whenever possible to avoid duplicating object code. --- src/librustc/middle/trans/base.rs | 98 +++++++++++++++++------ src/librustc/middle/trans/context.rs | 58 +++++++++++++- src/librustc/middle/trans/glue.rs | 2 +- src/librustc/middle/trans/meth.rs | 26 +++--- src/librustc/middle/trans/monomorphize.rs | 54 ++++++++----- src/libsyntax/attr.rs | 10 ++- 6 files changed, 190 insertions(+), 58 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index bed4ded095622..49e058333e523 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2125,12 +2125,43 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> { } } +/// Enum describing the origin of an LLVM `Value`, for linkage purposes. +pub enum ValueOrigin { + /// The LLVM `Value` is in this context because the corresponding item was + /// assigned to the current compilation unit. + OriginalTranslation, + /// The `Value`'s corresponding item was assigned to some other compilation + /// unit, but the `Value` was translated in this context anyway because the + /// item is marked `#[inline]`. + InlinedCopy, +} + /// Set the appropriate linkage for an LLVM `ValueRef` (function or global). /// If the `llval` is the direct translation of a specific Rust item, `id` /// should be set to the `NodeId` of that item. (This mapping should be /// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to -/// `None`.) -pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option) { +/// `None`.) `llval_origin` indicates whether `llval` is the translation of an +/// item assigned to `ccx`'s compilation unit or an inlined copy of an item +/// assigned to a different compilation unit. +pub fn update_linkage(ccx: &CrateContext, + llval: ValueRef, + id: Option, + llval_origin: ValueOrigin) { + match llval_origin { + InlinedCopy => { + // `llval` is a translation of an item defined in a separate + // compilation unit. This only makes sense if there are at least + // two compilation units. + assert!(ccx.sess().opts.cg.codegen_units > 1); + // `llval` is a copy of something defined elsewhere, so use + // `AvailableExternallyLinkage` to avoid duplicating code in the + // output. + llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage); + return; + }, + OriginalTranslation => {}, + } + match id { Some(id) if ccx.reachable().contains(&id) => { llvm::SetLinkage(llval, llvm::ExternalLinkage); @@ -2149,29 +2180,41 @@ pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option { if !generics.is_type_parameterized() { - let llfn = get_item_val(ccx, item.id); - if abi != Rust { - foreign::trans_rust_fn_with_foreign_abi(ccx, - &**decl, - &**body, - item.attrs.as_slice(), - llfn, - ¶m_substs::empty(), - item.id, - None); - } else { - trans_fn(ccx, - &**decl, - &**body, - llfn, - ¶m_substs::empty(), - item.id, - item.attrs.as_slice()); + let trans_everywhere = attr::requests_inline(item.attrs.as_slice()); + // Ignore `trans_everywhere` for cross-crate inlined items + // (`from_external`). `trans_item` will be called once for each + // compilation unit that references the item, so it will still get + // translated everywhere it's needed. + for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) { + let llfn = get_item_val(ccx, item.id); + if abi != Rust { + foreign::trans_rust_fn_with_foreign_abi(ccx, + &**decl, + &**body, + item.attrs.as_slice(), + llfn, + ¶m_substs::empty(), + item.id, + None); + } else { + trans_fn(ccx, + &**decl, + &**body, + llfn, + ¶m_substs::empty(), + item.id, + item.attrs.as_slice()); + } + update_linkage(ccx, + llfn, + Some(item.id), + if is_origin { OriginalTranslation } else { InlinedCopy }); } - update_linkage(ccx, llfn, Some(item.id)); } // Be sure to travel more than just one layer deep to catch nested @@ -2196,10 +2239,17 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { // Recurse on the expression to catch items in blocks 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, Some(item.id)); + let trans_everywhere = attr::requests_inline(item.attrs.as_slice()); + for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) { + consts::trans_const(ccx, m, item.id); + + let g = get_item_val(ccx, item.id); + update_linkage(ccx, + g, + Some(item.id), + if is_origin { OriginalTranslation } else { InlinedCopy }); + } // 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 diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 64722208aa5f4..5bdd5f6739d61 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -155,6 +155,9 @@ pub struct LocalCrateContext { pub struct CrateContext<'a> { shared: &'a SharedCrateContext, local: &'a LocalCrateContext, + /// The index of `local` in `shared.local_ccxs`. This is used in + /// `maybe_iter(true)` to identify the original `LocalCrateContext`. + index: uint, } pub struct CrateContextIterator<'a> { @@ -174,10 +177,41 @@ impl<'a> Iterator> for CrateContextIterator<'a> { Some(CrateContext { shared: self.shared, local: &self.shared.local_ccxs[index], + index: index, }) } } +/// The iterator produced by `CrateContext::maybe_iter`. +pub struct CrateContextMaybeIterator<'a> { + shared: &'a SharedCrateContext, + index: uint, + single: bool, + origin: uint, +} + +impl<'a> Iterator<(CrateContext<'a>, bool)> for CrateContextMaybeIterator<'a> { + fn next(&mut self) -> Option<(CrateContext<'a>, bool)> { + if self.index >= self.shared.local_ccxs.len() { + return None; + } + + let index = self.index; + self.index += 1; + if self.single { + self.index = self.shared.local_ccxs.len(); + } + + let ccx = CrateContext { + shared: self.shared, + local: &self.shared.local_ccxs[index], + index: index, + }; + Some((ccx, index == self.origin)) + } +} + + unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { let llcx = llvm::LLVMContextCreate(); let llmod = mod_name.with_c_str(|buf| { @@ -270,18 +304,21 @@ impl SharedCrateContext { CrateContext { shared: self, local: &self.local_ccxs[index], + index: index, } } fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a> { - let local_ccx = + let (local_ccx, index) = self.local_ccxs .iter() - .min_by(|&local_ccx| local_ccx.n_llvm_insns.get()) + .zip(range(0, self.local_ccxs.len())) + .min_by(|&(local_ccx, _idx)| local_ccx.n_llvm_insns.get()) .unwrap(); CrateContext { shared: self, local: local_ccx, + index: index, } } @@ -426,6 +463,7 @@ impl LocalCrateContext { CrateContext { shared: shared, local: self, + index: -1 as uint, } } } @@ -446,6 +484,22 @@ impl<'b> CrateContext<'b> { self.shared.get_smallest_ccx() } + /// Either iterate over only `self`, or iterate over all `CrateContext`s in + /// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)` + /// pairs, where `is_origin` is `true` if `ccx` is `self` and `false` + /// otherwise. This method is useful for avoiding code duplication in + /// cases where it may or may not be necessary to translate code into every + /// context. + pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b> { + CrateContextMaybeIterator { + shared: self.shared, + index: if iter_all { 0 } else { self.index }, + single: !iter_all, + origin: self.index, + } + } + + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt { &self.shared.tcx } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index e0ef867c23eb3..c8a47532a923a 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -669,7 +669,7 @@ fn make_generic_glue(ccx: &CrateContext, let bcx = init_function(&fcx, false, ty::mk_nil()); - update_linkage(ccx, llfn, None); + update_linkage(ccx, llfn, None, OriginalTranslation); ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u); // All glue functions take values passed *by alias*; this is a diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index f1101060d979d..c002f3e72c89f 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -38,7 +38,7 @@ use util::ppaux::Repr; use std::c_str::ToCStr; use syntax::abi::{Rust, RustCall}; use syntax::parse::token; -use syntax::{ast, ast_map, visit}; +use syntax::{ast, ast_map, attr, visit}; use syntax::ast_util::PostExpansionMethod; // drop_glue pointer, size, align. @@ -77,15 +77,21 @@ pub fn trans_impl(ccx: &CrateContext, match *impl_item { ast::MethodImplItem(method) => { if method.pe_generics().ty_params.len() == 0u { - let llfn = get_item_val(ccx, method.id); - trans_fn(ccx, - &*method.pe_fn_decl(), - &*method.pe_body(), - llfn, - ¶m_substs::empty(), - method.id, - []); - update_linkage(ccx, llfn, Some(method.id)); + let trans_everywhere = attr::requests_inline(method.attrs.as_slice()); + for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) { + let llfn = get_item_val(ccx, method.id); + trans_fn(ccx, + &*method.pe_fn_decl(), + &*method.pe_body(), + llfn, + ¶m_substs::empty(), + method.id, + []); + update_linkage(ccx, + llfn, + Some(method.id), + if is_origin { OriginalTranslation } else { InlinedCopy }); + } } let mut v = TransItemVisitor { ccx: ccx, diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 1dd0de3904d82..1cf3e55967d4e 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -11,6 +11,7 @@ use back::link::exported_name; use driver::session; use llvm::ValueRef; +use llvm; use middle::subst; use middle::subst::Subst; use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint}; @@ -27,6 +28,7 @@ use syntax::abi; use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, PostExpansionMethod}; +use syntax::attr; use std::hash::{sip, Hash}; pub fn monomorphic_fn(ccx: &CrateContext, @@ -150,6 +152,25 @@ pub fn monomorphic_fn(ccx: &CrateContext, ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); lldecl }; + let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| { + base::update_linkage(ccx, lldecl, None, base::OriginalTranslation); + set_llvm_fn_attrs(attrs, lldecl); + + let is_first = !ccx.available_monomorphizations().borrow().contains(&s); + if is_first { + ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + } + + let trans_everywhere = attr::requests_inline(attrs); + if trans_everywhere && !is_first { + llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage); + } + + // If `true`, then `lldecl` should be given a function body. + // Otherwise, it should be left as a declaration of an external + // function, with no definition in the current compilation unit. + trans_everywhere || is_first + }; let lldecl = match map_node { ast_map::NodeItem(i) => { @@ -159,11 +180,8 @@ pub fn monomorphic_fn(ccx: &CrateContext, .. } => { let d = mk_lldecl(abi); - base::update_linkage(ccx, d, None); - set_llvm_fn_attrs(i.attrs.as_slice(), d); - - if !ccx.available_monomorphizations().borrow().contains(&s) { - ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + let needs_body = setup_lldecl(d, i.attrs.as_slice()); + if needs_body { if abi != abi::Rust { foreign::trans_rust_fn_with_foreign_abi( ccx, &**decl, &**body, [], d, &psubsts, fn_id.node, @@ -205,17 +223,15 @@ pub fn monomorphic_fn(ccx: &CrateContext, match *ii { ast::MethodImplItem(mth) => { let d = mk_lldecl(abi::Rust); - base::update_linkage(ccx, d, None); - set_llvm_fn_attrs(mth.attrs.as_slice(), d); - if !ccx.available_monomorphizations().borrow().contains(&s) { - ccx.available_monomorphizations().borrow_mut().insert(s.clone()); - trans_fn(ccx, - &*mth.pe_fn_decl(), - &*mth.pe_body(), - d, - &psubsts, - mth.id, - []); + let needs_body = setup_lldecl(d, mth.attrs.as_slice()); + if needs_body { + trans_fn(ccx, + &*mth.pe_fn_decl(), + &*mth.pe_body(), + d, + &psubsts, + mth.id, + []); } d } @@ -225,10 +241,8 @@ pub fn monomorphic_fn(ccx: &CrateContext, match *method { ast::ProvidedMethod(mth) => { let d = mk_lldecl(abi::Rust); - base::update_linkage(ccx, d, None); - set_llvm_fn_attrs(mth.attrs.as_slice(), d); - if !ccx.available_monomorphizations().borrow().contains(&s) { - ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + let needs_body = setup_lldecl(d, mth.attrs.as_slice()); + if needs_body { trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, &psubsts, mth.id, []); } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index c234bea0a331e..dd422d021493f 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -280,7 +280,7 @@ pub enum InlineAttr { InlineNever, } -/// True if something like #[inline] is found in the list of attrs. +/// Determine what `#[inline]` attribute is present in `attrs`, if any. pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr { // FIXME (#2809)---validate the usage of #[inline] and #[inline] attrs.iter().fold(InlineNone, |ia,attr| { @@ -304,6 +304,14 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr { }) } +/// True if `#[inline]` or `#[inline(always)]` is present in `attrs`. +pub fn requests_inline(attrs: &[Attribute]) -> bool { + match find_inline_attr(attrs) { + InlineHint | InlineAlways => true, + InlineNone | InlineNever => false, + } +} + /// Tests if any `cfg(...)` meta items in `metas` match `cfg`. e.g. /// /// test_cfg(`[foo="a", bar]`, `[cfg(foo), cfg(bar)]`) == true From 4b70269854a701668ba47641201c4403228db06b Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 1 Aug 2014 15:45:24 -0700 Subject: [PATCH 10/14] add tests for separate compilation --- src/test/auxiliary/sepcomp-extern-lib.rs | 14 +++++++ src/test/auxiliary/sepcomp_cci_lib.rs | 17 ++++++++ src/test/auxiliary/sepcomp_lib.rs | 31 ++++++++++++++ src/test/compile-fail/sepcomp-lib-lto.rs | 28 +++++++++++++ src/test/run-make/sepcomp-cci-copies/Makefile | 10 +++++ .../run-make/sepcomp-cci-copies/cci_lib.rs | 19 +++++++++ src/test/run-make/sepcomp-cci-copies/foo.rs | 36 ++++++++++++++++ src/test/run-make/sepcomp-inlining/Makefile | 13 ++++++ src/test/run-make/sepcomp-inlining/foo.rs | 35 ++++++++++++++++ src/test/run-make/sepcomp-separate/Makefile | 9 ++++ src/test/run-make/sepcomp-separate/foo.rs | 27 ++++++++++++ src/test/run-pass/sepcomp-cci.rs | 41 ++++++++++++++++++ src/test/run-pass/sepcomp-extern.rs | 42 +++++++++++++++++++ src/test/run-pass/sepcomp-fns-backwards.rs | 41 ++++++++++++++++++ src/test/run-pass/sepcomp-fns.rs | 38 +++++++++++++++++ src/test/run-pass/sepcomp-lib.rs | 24 +++++++++++ src/test/run-pass/sepcomp-statics.rs | 39 +++++++++++++++++ src/test/run-pass/sepcomp-unwind.rs | 38 +++++++++++++++++ 18 files changed, 502 insertions(+) create mode 100644 src/test/auxiliary/sepcomp-extern-lib.rs create mode 100644 src/test/auxiliary/sepcomp_cci_lib.rs create mode 100644 src/test/auxiliary/sepcomp_lib.rs create mode 100644 src/test/compile-fail/sepcomp-lib-lto.rs create mode 100644 src/test/run-make/sepcomp-cci-copies/Makefile create mode 100644 src/test/run-make/sepcomp-cci-copies/cci_lib.rs create mode 100644 src/test/run-make/sepcomp-cci-copies/foo.rs create mode 100644 src/test/run-make/sepcomp-inlining/Makefile create mode 100644 src/test/run-make/sepcomp-inlining/foo.rs create mode 100644 src/test/run-make/sepcomp-separate/Makefile create mode 100644 src/test/run-make/sepcomp-separate/foo.rs create mode 100644 src/test/run-pass/sepcomp-cci.rs create mode 100644 src/test/run-pass/sepcomp-extern.rs create mode 100644 src/test/run-pass/sepcomp-fns-backwards.rs create mode 100644 src/test/run-pass/sepcomp-fns.rs create mode 100644 src/test/run-pass/sepcomp-lib.rs create mode 100644 src/test/run-pass/sepcomp-statics.rs create mode 100644 src/test/run-pass/sepcomp-unwind.rs diff --git a/src/test/auxiliary/sepcomp-extern-lib.rs b/src/test/auxiliary/sepcomp-extern-lib.rs new file mode 100644 index 0000000000000..8f5d3b5768a1a --- /dev/null +++ b/src/test/auxiliary/sepcomp-extern-lib.rs @@ -0,0 +1,14 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_mangle] +pub extern "C" fn foo() -> uint { + 1234 +} diff --git a/src/test/auxiliary/sepcomp_cci_lib.rs b/src/test/auxiliary/sepcomp_cci_lib.rs new file mode 100644 index 0000000000000..1cb7ead2cff05 --- /dev/null +++ b/src/test/auxiliary/sepcomp_cci_lib.rs @@ -0,0 +1,17 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[inline] +pub fn cci_fn() -> uint { + 1200 +} + +#[inline] +pub static CCI_STATIC: uint = 34; diff --git a/src/test/auxiliary/sepcomp_lib.rs b/src/test/auxiliary/sepcomp_lib.rs new file mode 100644 index 0000000000000..d1d9e3b8ff3ac --- /dev/null +++ b/src/test/auxiliary/sepcomp_lib.rs @@ -0,0 +1,31 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 --crate-type=rlib,dylib + +pub mod a { + pub fn one() -> uint { + 1 + } +} + +pub mod b { + pub fn two() -> uint { + 2 + } +} + +pub mod c { + use a::one; + use b::two; + pub fn three() -> uint { + one() + two() + } +} diff --git a/src/test/compile-fail/sepcomp-lib-lto.rs b/src/test/compile-fail/sepcomp-lib-lto.rs new file mode 100644 index 0000000000000..59706e20bede2 --- /dev/null +++ b/src/test/compile-fail/sepcomp-lib-lto.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure we give a sane error message when the user requests LTO with a +// library built with -C codegen-units > 1. + +// aux-build:sepcomp_lib.rs +// compile-flags: -Z lto +// error-pattern:missing compressed bytecode +// no-prefer-dynamic + +extern crate sepcomp_lib; +use sepcomp_lib::a::one; +use sepcomp_lib::b::two; +use sepcomp_lib::c::three; + +fn main() { + assert_eq!(one(), 1); + assert_eq!(two(), 2); + assert_eq!(three(), 3); +} diff --git a/src/test/run-make/sepcomp-cci-copies/Makefile b/src/test/run-make/sepcomp-cci-copies/Makefile new file mode 100644 index 0000000000000..fdb39f851970f --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/Makefile @@ -0,0 +1,10 @@ +-include ../tools.mk + +# Check that cross-crate inlined items are inlined in all compilation units +# that refer to them, and not in any other compilation units. + +all: + $(RUSTC) cci_lib.rs + $(RUSTC) foo.rs --emit=ir -C codegen-units=3 + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*cci_fn)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c CCI_STATIC.*=.*constant)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-cci-copies/cci_lib.rs b/src/test/run-make/sepcomp-cci-copies/cci_lib.rs new file mode 100644 index 0000000000000..099101d6f2679 --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/cci_lib.rs @@ -0,0 +1,19 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] + +#[inline] +pub fn cci_fn() -> uint { + 1234 +} + +#[inline] +pub static CCI_STATIC: uint = 2345; diff --git a/src/test/run-make/sepcomp-cci-copies/foo.rs b/src/test/run-make/sepcomp-cci-copies/foo.rs new file mode 100644 index 0000000000000..c702e578c0965 --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/foo.rs @@ -0,0 +1,36 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate cci_lib; +use cci_lib::{cci_fn, CCI_STATIC}; + +fn call1() -> uint { + cci_fn() + CCI_STATIC +} + +mod a { + use cci_lib::cci_fn; + pub fn call2() -> uint { + cci_fn() + } +} + +mod b { + use cci_lib::CCI_STATIC; + pub fn call3() -> uint { + CCI_STATIC + } +} + +fn main() { + call1(); + a::call2(); + b::call3(); +} diff --git a/src/test/run-make/sepcomp-inlining/Makefile b/src/test/run-make/sepcomp-inlining/Makefile new file mode 100644 index 0000000000000..6cb9f9a3f31bc --- /dev/null +++ b/src/test/run-make/sepcomp-inlining/Makefile @@ -0,0 +1,13 @@ +-include ../tools.mk + +# Test that #[inline(always)] functions still get inlined across compilation +# unit boundaries. Compilation should produce three IR files, with each one +# containing a definition of the inlined function. Also, the non-#[inline] +# function should be defined in only one compilation unit. + +all: + $(RUSTC) foo.rs --emit=ir -C codegen-units=3 + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "1" ] + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ available_externally\ i32\ .*inlined)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*normal)" -eq "1" ] + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ i32\ .*normal)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-inlining/foo.rs b/src/test/run-make/sepcomp-inlining/foo.rs new file mode 100644 index 0000000000000..20fd18b829562 --- /dev/null +++ b/src/test/run-make/sepcomp-inlining/foo.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[inline] +fn inlined() -> u32 { + 1234 +} + +fn normal() -> u32 { + 2345 +} + +mod a { + pub fn f() -> u32 { + ::inlined() + ::normal() + } +} + +mod b { + pub fn f() -> u32 { + ::inlined() + ::normal() + } +} + +fn main() { + a::f(); + b::f(); +} diff --git a/src/test/run-make/sepcomp-separate/Makefile b/src/test/run-make/sepcomp-separate/Makefile new file mode 100644 index 0000000000000..265bd68bd2e82 --- /dev/null +++ b/src/test/run-make/sepcomp-separate/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +# Test that separate compilation actually puts code into separate compilation +# units. `foo.rs` defines `magic_fn` in three different modules, which should +# wind up in three different compilation units. + +all: + $(RUSTC) foo.rs --emit=ir -C codegen-units=3 + [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*magic_fn)" -eq "3" ] diff --git a/src/test/run-make/sepcomp-separate/foo.rs b/src/test/run-make/sepcomp-separate/foo.rs new file mode 100644 index 0000000000000..fe6a7b5a18f27 --- /dev/null +++ b/src/test/run-make/sepcomp-separate/foo.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn magic_fn() -> uint { + 1234 +} + +mod a { + pub fn magic_fn() -> uint { + 2345 + } +} + +mod b { + pub fn magic_fn() -> uint { + 3456 + } +} + +fn main() { } diff --git a/src/test/run-pass/sepcomp-cci.rs b/src/test/run-pass/sepcomp-cci.rs new file mode 100644 index 0000000000000..0178b5e786d65 --- /dev/null +++ b/src/test/run-pass/sepcomp-cci.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 +// aux-build:sepcomp_cci_lib.rs + +// Test accessing cross-crate inlined items from multiple compilation units. + +extern crate sepcomp_cci_lib; +use sepcomp_cci_lib::{cci_fn, CCI_STATIC}; + +fn call1() -> uint { + cci_fn() + CCI_STATIC +} + +mod a { + use sepcomp_cci_lib::{cci_fn, CCI_STATIC}; + pub fn call2() -> uint { + cci_fn() + CCI_STATIC + } +} + +mod b { + use sepcomp_cci_lib::{cci_fn, CCI_STATIC}; + pub fn call3() -> uint { + cci_fn() + CCI_STATIC + } +} + +fn main() { + assert_eq!(call1(), 1234); + assert_eq!(a::call2(), 1234); + assert_eq!(b::call3(), 1234); +} diff --git a/src/test/run-pass/sepcomp-extern.rs b/src/test/run-pass/sepcomp-extern.rs new file mode 100644 index 0000000000000..a5506e3fc761d --- /dev/null +++ b/src/test/run-pass/sepcomp-extern.rs @@ -0,0 +1,42 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 +// aux-build:sepcomp-extern-lib.rs + +// Test accessing external items from multiple compilation units. + +#[link(name = "sepcomp-extern-lib")] +extern { + #[allow(ctypes)] + fn foo() -> uint; +} + +fn call1() -> uint { + unsafe { foo() } +} + +mod a { + pub fn call2() -> uint { + unsafe { ::foo() } + } +} + +mod b { + pub fn call3() -> uint { + unsafe { ::foo() } + } +} + +fn main() { + assert_eq!(call1(), 1234); + assert_eq!(a::call2(), 1234); + assert_eq!(b::call3(), 1234); +} diff --git a/src/test/run-pass/sepcomp-fns-backwards.rs b/src/test/run-pass/sepcomp-fns-backwards.rs new file mode 100644 index 0000000000000..61f008ad854eb --- /dev/null +++ b/src/test/run-pass/sepcomp-fns-backwards.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 + +// Test references to items that haven't been translated yet. + +// Generate some code in the first compilation unit before declaring any +// modules. This ensures that the first module doesn't go into the same +// compilation unit as the top-level module. +fn pad() -> uint { 0 } + +mod b { + pub fn three() -> uint { + ::one() + ::a::two() + } +} + +mod a { + pub fn two() -> uint { + ::one() + ::one() + } +} + +fn one() -> uint { + 1 +} + +fn main() { + assert_eq!(one(), 1); + assert_eq!(a::two(), 2); + assert_eq!(b::three(), 3); +} + diff --git a/src/test/run-pass/sepcomp-fns.rs b/src/test/run-pass/sepcomp-fns.rs new file mode 100644 index 0000000000000..09f2a4281be08 --- /dev/null +++ b/src/test/run-pass/sepcomp-fns.rs @@ -0,0 +1,38 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 + +// Test basic separate compilation functionality. The functions should be able +// to call each other even though they will be placed in different compilation +// units. + +// Generate some code in the first compilation unit before declaring any +// modules. This ensures that the first module doesn't go into the same +// compilation unit as the top-level module. +fn one() -> uint { 1 } + +mod a { + pub fn two() -> uint { + ::one() + ::one() + } +} + +mod b { + pub fn three() -> uint { + ::one() + ::a::two() + } +} + +fn main() { + assert_eq!(one(), 1); + assert_eq!(a::two(), 2); + assert_eq!(b::three(), 3); +} diff --git a/src/test/run-pass/sepcomp-lib.rs b/src/test/run-pass/sepcomp-lib.rs new file mode 100644 index 0000000000000..28adb55399b44 --- /dev/null +++ b/src/test/run-pass/sepcomp-lib.rs @@ -0,0 +1,24 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:sepcomp_lib.rs + +// Test linking against a library built with -C codegen-units > 1 + +extern crate sepcomp_lib; +use sepcomp_lib::a::one; +use sepcomp_lib::b::two; +use sepcomp_lib::c::three; + +fn main() { + assert_eq!(one(), 1); + assert_eq!(two(), 2); + assert_eq!(three(), 3); +} diff --git a/src/test/run-pass/sepcomp-statics.rs b/src/test/run-pass/sepcomp-statics.rs new file mode 100644 index 0000000000000..26a652ae0ea4f --- /dev/null +++ b/src/test/run-pass/sepcomp-statics.rs @@ -0,0 +1,39 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 + +// Test references to static items across compilation units. + +fn pad() -> uint { 0 } + +static ONE: uint = 1; + +mod b { + // Separate compilation always switches to the LLVM module with the fewest + // instructions. Make sure we have some instructions in this module so + // that `a` and `b` don't go into the same compilation unit. + fn pad() -> uint { 0 } + + pub static THREE: uint = ::ONE + ::a::TWO; +} + +mod a { + fn pad() -> uint { 0 } + + pub static TWO: uint = ::ONE + ::ONE; +} + +fn main() { + assert_eq!(ONE, 1); + assert_eq!(a::TWO, 2); + assert_eq!(b::THREE, 3); +} + diff --git a/src/test/run-pass/sepcomp-unwind.rs b/src/test/run-pass/sepcomp-unwind.rs new file mode 100644 index 0000000000000..5d154e02af670 --- /dev/null +++ b/src/test/run-pass/sepcomp-unwind.rs @@ -0,0 +1,38 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C codegen-units=3 + +// Test unwinding through multiple compilation units. + +// According to acrichto, in the distant past `ld -r` (which is used during +// linking when codegen-units > 1) was known to produce object files with +// damaged unwinding tables. This may be related to GNU binutils bug #6893 +// ("Partial linking results in corrupt .eh_frame_hdr"), but I'm not certain. +// In any case, this test should let us know if enabling parallel codegen ever +// breaks unwinding. + +fn pad() -> uint { 0 } + +mod a { + pub fn f() { + fail!(); + } +} + +mod b { + pub fn g() { + ::a::f(); + } +} + +fn main() { + std::task::try(proc() { ::b::g() }).unwrap_err(); +} From b5a0b700c64639043bce0a2ba0a8b40dd853d469 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Tue, 26 Aug 2014 15:53:56 -0700 Subject: [PATCH 11/14] use target-specific linker args when combining compilation units --- src/librustc/back/write.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index 076338ccef84f..2760ae80110bc 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -604,17 +604,34 @@ pub fn run_passes(sess: &Session, }; let link_obj = |output_path: &Path| { - let mut cmd = Command::new("ld"); + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.args(sess.targ_cfg.target_strs.cc_args.as_slice()); + cmd.arg("-nostdlib"); for index in range(0, trans.modules.len()) { cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice())); } cmd.arg("-r").arg("-o").arg(output_path); + + if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { + println!("{}", &cmd); + } + cmd.stdin(::std::io::process::Ignored) .stdout(::std::io::process::InheritFd(1)) .stderr(::std::io::process::InheritFd(2)); - cmd.status().unwrap(); + match cmd.status() { + Ok(_) => {}, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + }, + } }; // Flag to indicate whether the user explicitly requested bitcode. From 1b676fb7603cd2aa4e0506cb5b67d48c9da1123f Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Wed, 27 Aug 2014 14:49:17 -0700 Subject: [PATCH 12/14] don't leave unwanted temporary files with --emit=ir/asm --- src/librustc/back/write.rs | 9 +++-- .../output-type-permutations/Makefile | 39 ++++++++++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index 2760ae80110bc..d87dd84bb32b2 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -588,12 +588,15 @@ pub fn run_passes(sess: &Session, // to copy `foo.0.x` to `foo.x`. fs::copy(&crate_output.with_extension(ext), &crate_output.path(output_type)).unwrap(); + if !sess.opts.cg.save_temps { + // The user just wants `foo.x`, not `foo.0.x`. + remove(sess, &crate_output.with_extension(ext)); + } } else { if crate_output.single_output_file.is_some() { // 2) Multiple codegen units, with `-o some_name`. We have // no good solution for this case, so warn the user. - sess.warn(format!("ignoring specified output filename \ - because multiple .{} files were produced", + sess.warn(format!("ignoring -o because multiple .{} files were produced", ext).as_slice()); } else { // 3) Multiple codegen units, but no `-o some_name`. We @@ -670,7 +673,7 @@ pub fn run_passes(sess: &Session, // - crate.metadata.bc // - crate.metadata.o // - crate.o (linked from crate.##.o) - // - crate.bc (copied from crate.0.bc, or an empty bitcode file) + // - crate.bc (copied from crate.0.bc) // We may create additional files if requested by the user (through // `-C save-temps` or `--emit=` flags). diff --git a/src/test/run-make/output-type-permutations/Makefile b/src/test/run-make/output-type-permutations/Makefile index c163a5bec086f..fed071d1a43c2 100644 --- a/src/test/run-make/output-type-permutations/Makefile +++ b/src/test/run-make/output-type-permutations/Makefile @@ -5,40 +5,69 @@ all: $(call REMOVE_RLIBS,bar) $(call REMOVE_DYLIBS,bar) rm $(TMPDIR)/$(call STATICLIB_GLOB,bar) + # Check that $(TMPDIR) is empty. + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --crate-type=bin rm $(TMPDIR)/$(call BIN,bar) + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=asm,ir,bc,obj,link rm $(TMPDIR)/bar.ll rm $(TMPDIR)/bar.bc rm $(TMPDIR)/bar.s rm $(TMPDIR)/bar.o rm $(TMPDIR)/$(call BIN,bar) - $(RUSTC) foo.rs --emit=asm,ir,bc,obj,link --crate-type=staticlib - rm $(TMPDIR)/bar.ll - rm $(TMPDIR)/bar.s - rm $(TMPDIR)/bar.o - rm $(TMPDIR)/$(call STATICLIB_GLOB,bar) + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=asm -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=bc -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=ir -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=obj -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --emit=link -o $(TMPDIR)/foo rm $(TMPDIR)/$(call BIN,foo) + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --crate-type=dylib -o $(TMPDIR)/foo rm $(TMPDIR)/$(call BIN,foo) # FIXME 13794 + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo rm $(TMPDIR)/foo + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + $(RUSTC) foo.rs --crate-type=bin -o $(TMPDIR)/foo rm $(TMPDIR)/$(call BIN,foo) + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] + + $(RUSTC) foo.rs --emit=asm,ir,bc,obj,link --crate-type=staticlib + rm $(TMPDIR)/bar.ll + rm $(TMPDIR)/bar.s + rm $(TMPDIR)/bar.o + rm $(TMPDIR)/$(call STATICLIB_GLOB,bar) mv $(TMPDIR)/bar.bc $(TMPDIR)/foo.bc + # Don't check that the $(TMPDIR) is empty - we left `foo.bc` for later + # comparison. + $(RUSTC) foo.rs --emit=bc,link --crate-type=rlib cmp $(TMPDIR)/foo.bc $(TMPDIR)/bar.bc rm $(TMPDIR)/bar.bc rm $(TMPDIR)/foo.bc $(call REMOVE_RLIBS,bar) + [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] From 4d9a4786163a9a9831bf4e283b4e408be03a169b Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 29 Aug 2014 12:46:04 -0700 Subject: [PATCH 13/14] add workaround for mingw `ld --force-exe-suffix` behavior --- src/librustc/back/write.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index d87dd84bb32b2..506d358501696 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -607,6 +607,19 @@ pub fn run_passes(sess: &Session, }; let link_obj = |output_path: &Path| { + // Some builds of MinGW GCC will pass --force-exe-suffix to ld, which + // will automatically add a .exe extension if the extension is not + // already .exe or .dll. To ensure consistent behavior on Windows, we + // add the .exe suffix explicitly and then rename the output file to + // the desired path. This will give the correct behavior whether or + // not GCC adds --force-exe-suffix. + let windows_output_path = + if sess.targ_cfg.os == abi::OsWindows { + Some(output_path.with_extension("o.exe")) + } else { + None + }; + let pname = get_cc_prog(sess); let mut cmd = Command::new(pname.as_slice()); @@ -617,7 +630,9 @@ pub fn run_passes(sess: &Session, cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice())); } - cmd.arg("-r").arg("-o").arg(output_path); + cmd.arg("-r") + .arg("-o") + .arg(windows_output_path.as_ref().unwrap_or(output_path)); if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { println!("{}", &cmd); @@ -635,6 +650,15 @@ pub fn run_passes(sess: &Session, sess.abort_if_errors(); }, } + + match windows_output_path { + Some(ref windows_path) => { + fs::rename(windows_path, output_path).unwrap(); + }, + None => { + // The file is already named according to `output_path`. + } + } }; // Flag to indicate whether the user explicitly requested bitcode. From 6d2d47b2fc73b7beacced1f2a62037193ea1ed30 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Fri, 5 Sep 2014 14:30:36 -0700 Subject: [PATCH 14/14] don't use `ld -r` with `-C codegen-units=1` --- src/librustc/back/write.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index 506d358501696..627d455f06e11 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -476,6 +476,9 @@ pub fn run_passes(sess: &Session, sess.fatal("can't perform LTO when using multiple codegen units"); } + // Sanity check + assert!(trans.modules.len() == sess.opts.cg.codegen_units); + unsafe { configure_llvm(sess); } @@ -607,6 +610,15 @@ pub fn run_passes(sess: &Session, }; let link_obj = |output_path: &Path| { + // Running `ld -r` on a single input is kind of pointless. + if sess.opts.cg.codegen_units == 1 { + fs::copy(&crate_output.with_extension("0.o"), + output_path).unwrap(); + // Leave the .0.o file around, to mimic the behavior of the normal + // code path. + return; + } + // Some builds of MinGW GCC will pass --force-exe-suffix to ld, which // will automatically add a .exe extension if the extension is not // already .exe or .dll. To ensure consistent behavior on Windows, we