From aec63821d04872f9190c3d8606d0a58428005222 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 6 Mar 2016 16:30:21 +0200 Subject: [PATCH] trans: Handle all function setup for all ABIs via FnType. --- src/librustc_trans/trans/abi.rs | 24 + src/librustc_trans/trans/base.rs | 948 +++++++++------------- src/librustc_trans/trans/callee.rs | 71 +- src/librustc_trans/trans/closure.rs | 147 ++-- src/librustc_trans/trans/common.rs | 41 +- src/librustc_trans/trans/controlflow.rs | 13 +- src/librustc_trans/trans/debuginfo/mod.rs | 16 +- src/librustc_trans/trans/declare.rs | 13 +- src/librustc_trans/trans/expr.rs | 4 +- src/librustc_trans/trans/foreign.rs | 386 +-------- src/librustc_trans/trans/glue.rs | 33 +- src/librustc_trans/trans/intrinsic.rs | 110 ++- src/librustc_trans/trans/meth.rs | 53 +- src/librustc_trans/trans/mir/block.rs | 3 +- src/librustc_trans/trans/mir/lvalue.rs | 11 +- src/librustc_trans/trans/mir/mod.rs | 90 +- src/librustc_trans/trans/mir/rvalue.rs | 8 +- src/librustc_trans/trans/monomorphize.rs | 16 +- src/librustc_trans/trans/type_.rs | 6 +- src/librustc_trans/trans/type_of.rs | 61 +- 20 files changed, 712 insertions(+), 1342 deletions(-) diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs index 91e2aef26ef6..62b6c24d3874 100644 --- a/src/librustc_trans/trans/abi.rs +++ b/src/librustc_trans/trans/abi.rs @@ -29,6 +29,8 @@ use trans::type_of; use rustc_front::hir; use middle::ty::{self, Ty}; +use libc::c_uint; + pub use syntax::abi::Abi; /// The first half of a fat pointer. @@ -129,6 +131,16 @@ impl ArgType { self.kind == ArgKind::Ignore } + /// Get the LLVM type for an lvalue of the original Rust type of + /// this argument/return, i.e. the result of `type_of::type_of`. + pub fn memory_ty(&self, ccx: &CrateContext) -> Type { + if self.original_ty == Type::i1(ccx) { + Type::i8(ccx) + } else { + self.original_ty + } + } + /// Store a direct/indirect value described by this ArgType into a /// lvalue for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables @@ -156,6 +168,18 @@ impl ArgType { Store(bcx, val, dst); } } + + pub fn store_fn_arg(&self, bcx: Block, idx: &mut usize, dst: ValueRef) { + if self.pad.is_some() { + *idx += 1; + } + if self.is_ignore() { + return; + } + let val = llvm::get_param(bcx.fcx.llfn, *idx as c_uint); + *idx += 1; + self.store(bcx, val, dst); + } } /// Metadata describing how the arguments to a native function diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index dbcbdbf56028..6d760069cc1f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -52,7 +52,7 @@ use rustc::mir::mir_map::MirMap; use session::config::{self, NoDebugInfo, FullDebugInfo}; use session::Session; use trans::_match; -use trans::abi::{self, Abi}; +use trans::abi::{self, Abi, FnType}; use trans::adt; use trans::assert_dep_graph; use trans::attributes; @@ -66,7 +66,7 @@ use trans::collector::{self, TransItem, TransItemState, TransItemCollectionMode} use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef}; use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext}; use trans::common::{Result, NodeIdAndSpan, VariantInfo}; -use trans::common::{node_id_type, return_type_is_void, fulfill_obligation}; +use trans::common::{node_id_type, fulfill_obligation}; use trans::common::{type_is_immediate, type_is_zero_size, val_ty}; use trans::common; use trans::consts; @@ -80,7 +80,7 @@ use trans::foreign; use trans::glue; use trans::intrinsic; use trans::machine; -use trans::machine::{llsize_of, llsize_of_real}; +use trans::machine::{llalign_of_min, llsize_of, llsize_of_real}; use trans::meth; use trans::mir; use trans::monomorphize::{self, Instance}; @@ -915,23 +915,12 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> return C_undef(type_of::type_of(cx.ccx(), t)); } - let ptr = to_arg_ty_ptr(cx, ptr, t); - let align = type_of::align_of(cx.ccx(), t); - - if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() { - let load = Load(cx, ptr); - unsafe { - llvm::LLVMSetAlignment(load, align); - } - return load; - } - unsafe { let global = llvm::LLVMIsAGlobalVariable(ptr); if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { let val = llvm::LLVMGetInitializer(global); if !val.is_null() { - return to_arg_ty(cx, val, t); + return to_immediate(cx, val, t); } } } @@ -948,11 +937,7 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> Load(cx, ptr) }; - unsafe { - llvm::LLVMSetAlignment(val, align); - } - - to_arg_ty(cx, val, t) + to_immediate(cx, val, t) } /// Helper for storing values in memory. Does the necessary conversion if the in-memory type @@ -972,10 +957,7 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t ExtractValue(cx, v, abi::FAT_PTR_EXTRA), expr::get_meta(cx, dst)); } else { - let store = Store(cx, from_arg_ty(cx, v, t), to_arg_ty_ptr(cx, dst, t)); - unsafe { - llvm::LLVMSetAlignment(store, type_of::align_of(cx.ccx(), t)); - } + Store(cx, from_immediate(cx, v), dst); } } @@ -998,15 +980,15 @@ pub fn load_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>, Load(cx, expr::get_meta(cx, src))) } -pub fn from_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef { - if ty.is_bool() { +pub fn from_immediate(bcx: Block, val: ValueRef) -> ValueRef { + if val_ty(val) == Type::i1(bcx.ccx()) { ZExt(bcx, val, Type::i8(bcx.ccx())) } else { val } } -pub fn to_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef { +pub fn to_immediate(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef { if ty.is_bool() { Trunc(bcx, val, Type::i1(bcx.ccx())) } else { @@ -1014,17 +996,6 @@ pub fn to_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef { } } -pub fn to_arg_ty_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ptr: ValueRef, ty: Ty<'tcx>) -> ValueRef { - if type_is_immediate(bcx.ccx(), ty) && type_of::type_of(bcx.ccx(), ty).is_aggregate() { - // We want to pass small aggregates as immediate values, but using an aggregate LLVM type - // for this leads to bad optimizations, so its arg type is an appropriately sized integer - // and we have to convert it - BitCast(bcx, ptr, type_of::arg_type_of(bcx.ccx(), ty).ptr_to()) - } else { - ptr - } -} - pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &hir::Local) -> Block<'blk, 'tcx> { debug!("init_local(bcx={}, local.id={})", bcx.to_str(), local.id); let _indenter = indenter(); @@ -1309,41 +1280,6 @@ pub fn set_value_name(val: ValueRef, name: &str) { } } -// Creates the alloca slot which holds the pointer to the slot for the final return value -pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, - output_type: Ty<'tcx>) - -> ValueRef { - let lloutputtype = type_of::type_of(fcx.ccx, output_type); - - // We create an alloca to hold a pointer of type `output_type` - // which will hold the pointer to the right alloca which has the - // final ret value - if fcx.needs_ret_allocas { - // Let's create the stack slot - let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr"); - - // and if we're using an out pointer, then store that in our newly made slot - if type_of::return_uses_outptr(fcx.ccx, output_type) { - let outptr = get_param(fcx.llfn, 0); - - let b = fcx.ccx.builder(); - b.position_before(fcx.alloca_insert_pt.get().unwrap()); - b.store(outptr, slot); - } - - slot - - // But if there are no nested returns, we skip the indirection and have a single - // retslot - } else { - if type_of::return_uses_outptr(fcx.ccx, output_type) { - get_param(fcx.llfn, 0) - } else { - AllocaFcx(fcx, lloutputtype, "sret_slot") - } - } -} - struct FindNestedReturn { found: bool, } @@ -1450,419 +1386,389 @@ fn has_nested_returns(tcx: &TyCtxt, cfg: &cfg::CFG, blk_id: ast::NodeId) -> bool return false; } -// NB: must keep 4 fns in sync: -// -// - type_of_fn -// - create_datums_for_fn_args. -// - new_fn_ctxt -// - trans_args -// -// Be warned! You must call `init_function` before doing anything with the -// returned function context. -pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - llfndecl: ValueRef, - id: ast::NodeId, - has_env: bool, - output_type: ty::FnOutput<'tcx>, - param_substs: &'tcx Substs<'tcx>, - sp: Option, - block_arena: &'a TypedArena>) - -> FunctionContext<'a, 'tcx> { - common::validate_substs(param_substs); +impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { + /// Create a function context for the given function. + /// Beware that you must call `fcx.init` or `fcx.bind_args` + /// before doing anything with the returned function context. + pub fn new(ccx: &'blk CrateContext<'blk, 'tcx>, + llfndecl: ValueRef, + fn_ty: FnType, + id: ast::NodeId, + param_substs: &'tcx Substs<'tcx>, + sp: Option, + block_arena: &'blk TypedArena>) + -> FunctionContext<'blk, 'tcx> { + common::validate_substs(param_substs); - debug!("new_fn_ctxt(path={}, id={}, param_substs={:?})", - if id == !0 { - "".to_string() - } else { - ccx.tcx().map.path_to_string(id).to_string() - }, - id, - param_substs); + debug!("FunctionContext::new(path={}, id={}, param_substs={:?})", + if id == !0 { + "".to_string() + } else { + ccx.tcx().map.path_to_string(id).to_string() + }, + id, + param_substs); - let uses_outptr = match output_type { - ty::FnConverging(output_type) => { - let substd_output_type = monomorphize::apply_param_substs(ccx.tcx(), - param_substs, - &output_type); - type_of::return_uses_outptr(ccx, substd_output_type) - } - ty::FnDiverging => false, - }; - let debug_context = debuginfo::create_function_debug_context(ccx, id, - param_substs, - llfndecl); - let (blk_id, cfg) = build_cfg(ccx.tcx(), id); - let nested_returns = if let Some(ref cfg) = cfg { - has_nested_returns(ccx.tcx(), cfg, blk_id) - } else { - false - }; + let debug_context = debuginfo::create_function_debug_context(ccx, id, + param_substs, + llfndecl); + let (blk_id, cfg) = build_cfg(ccx.tcx(), id); + let nested_returns = if let Some(ref cfg) = cfg { + has_nested_returns(ccx.tcx(), cfg, blk_id) + } else { + false + }; - let mir = ccx.mir_map().map.get(&id); - - let mut fcx = FunctionContext { - mir: mir, - llfn: llfndecl, - llenv: None, - llretslotptr: Cell::new(None), - param_env: ccx.tcx().empty_parameter_environment(), - alloca_insert_pt: Cell::new(None), - llreturn: Cell::new(None), - needs_ret_allocas: nested_returns, - landingpad_alloca: Cell::new(None), - caller_expects_out_pointer: uses_outptr, - lllocals: RefCell::new(NodeMap()), - llupvars: RefCell::new(NodeMap()), - lldropflag_hints: RefCell::new(DropFlagHintsMap::new()), - id: id, - param_substs: param_substs, - span: sp, - block_arena: block_arena, - lpad_arena: TypedArena::new(), - ccx: ccx, - debug_context: debug_context, - scopes: RefCell::new(Vec::new()), - cfg: cfg, - }; - - if has_env { - fcx.llenv = Some(get_param(fcx.llfn, fcx.env_arg_pos() as c_uint)) - } - - fcx -} - -/// Performs setup on a newly created function, creating the entry scope block -/// and allocating space for the return pointer. -pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, - skip_retptr: bool, - output: ty::FnOutput<'tcx>) - -> Block<'a, 'tcx> { - let entry_bcx = fcx.new_temp_block("entry-block"); - - // Use a dummy instruction as the insertion point for all allocas. - // This is later removed in FunctionContext::cleanup. - fcx.alloca_insert_pt.set(Some(unsafe { - Load(entry_bcx, C_null(Type::i8p(fcx.ccx))); - llvm::LLVMGetFirstInstruction(entry_bcx.llbb) - })); - - if let ty::FnConverging(output_type) = output { - // This shouldn't need to recompute the return type, - // as new_fn_ctxt did it already. - let substd_output_type = fcx.monomorphize(&output_type); - if !return_type_is_void(fcx.ccx, substd_output_type) { - // If the function returns nil/bot, there is no real return - // value, so do not set `llretslotptr`. - if !skip_retptr || fcx.caller_expects_out_pointer { - // Otherwise, we normally allocate the llretslotptr, unless we - // have been instructed to skip it for immediate return - // values. - fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); - } + FunctionContext { + mir: ccx.mir_map().map.get(&id), + llfn: llfndecl, + llretslotptr: Cell::new(None), + param_env: ccx.tcx().empty_parameter_environment(), + alloca_insert_pt: Cell::new(None), + llreturn: Cell::new(None), + needs_ret_allocas: nested_returns, + landingpad_alloca: Cell::new(None), + lllocals: RefCell::new(NodeMap()), + llupvars: RefCell::new(NodeMap()), + lldropflag_hints: RefCell::new(DropFlagHintsMap::new()), + fn_ty: fn_ty, + id: id, + param_substs: param_substs, + span: sp, + block_arena: block_arena, + lpad_arena: TypedArena::new(), + ccx: ccx, + debug_context: debug_context, + scopes: RefCell::new(Vec::new()), + cfg: cfg, } } - // Create the drop-flag hints for every unfragmented path in the function. - let tcx = fcx.ccx.tcx(); - let fn_did = tcx.map.local_def_id(fcx.id); - let tables = tcx.tables.borrow(); - let mut hints = fcx.lldropflag_hints.borrow_mut(); - let fragment_infos = tcx.fragment_infos.borrow(); + /// Performs setup on a newly created function, creating the entry + /// scope block and allocating space for the return pointer. + pub fn init(&'blk self, skip_retptr: bool) -> Block<'blk, 'tcx> { + let entry_bcx = self.new_temp_block("entry-block"); - // Intern table for drop-flag hint datums. - let mut seen = HashMap::new(); + // Use a dummy instruction as the insertion point for all allocas. + // This is later removed in FunctionContext::cleanup. + self.alloca_insert_pt.set(Some(unsafe { + Load(entry_bcx, C_null(Type::i8p(self.ccx))); + llvm::LLVMGetFirstInstruction(entry_bcx.llbb) + })); - if let Some(fragment_infos) = fragment_infos.get(&fn_did) { - for &info in fragment_infos { + if !self.fn_ty.ret.is_ignore() && !skip_retptr { + // We normally allocate the llretslotptr, unless we + // have been instructed to skip it for immediate return + // values, or there is nothing to return at all. - let make_datum = |id| { - let init_val = C_u8(fcx.ccx, adt::DTOR_NEEDED_HINT); - let llname = &format!("dropflag_hint_{}", id); - debug!("adding hint {}", llname); - let ty = tcx.types.u8; - let ptr = alloc_ty(entry_bcx, ty, llname); - Store(entry_bcx, init_val, ptr); - let flag = datum::Lvalue::new_dropflag_hint("base::init_function"); - datum::Datum::new(ptr, ty, flag) + // We create an alloca to hold a pointer of type `ret.original_ty` + // which will hold the pointer to the right alloca which has the + // final ret value + let llty = self.fn_ty.ret.memory_ty(self.ccx); + let slot = if self.needs_ret_allocas { + // Let's create the stack slot + let slot = AllocaFcx(self, llty.ptr_to(), "llretslotptr"); + + // and if we're using an out pointer, then store that in our newly made slot + if self.fn_ty.ret.is_indirect() { + let outptr = get_param(self.llfn, 0); + + let b = self.ccx.builder(); + b.position_before(self.alloca_insert_pt.get().unwrap()); + b.store(outptr, slot); + } + + slot + } else { + // But if there are no nested returns, we skip the indirection + // and have a single retslot + if self.fn_ty.ret.is_indirect() { + get_param(self.llfn, 0) + } else { + AllocaFcx(self, llty, "sret_slot") + } }; - let (var, datum) = match info { - ty::FragmentInfo::Moved { var, .. } | - ty::FragmentInfo::Assigned { var, .. } => { - let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| { - let ty = tables.node_types[&var]; - if fcx.type_needs_drop(ty) { - let datum = make_datum(var); - seen.insert(var, Some(datum.clone())); - Some(datum) + self.llretslotptr.set(Some(slot)); + } + + // Create the drop-flag hints for every unfragmented path in the function. + let tcx = self.ccx.tcx(); + let fn_did = tcx.map.local_def_id(self.id); + let tables = tcx.tables.borrow(); + let mut hints = self.lldropflag_hints.borrow_mut(); + let fragment_infos = tcx.fragment_infos.borrow(); + + // Intern table for drop-flag hint datums. + let mut seen = HashMap::new(); + + if let Some(fragment_infos) = fragment_infos.get(&fn_did) { + for &info in fragment_infos { + + let make_datum = |id| { + let init_val = C_u8(self.ccx, adt::DTOR_NEEDED_HINT); + let llname = &format!("dropflag_hint_{}", id); + debug!("adding hint {}", llname); + let ty = tcx.types.u8; + let ptr = alloc_ty(entry_bcx, ty, llname); + Store(entry_bcx, init_val, ptr); + let flag = datum::Lvalue::new_dropflag_hint("FunctionContext::init"); + datum::Datum::new(ptr, ty, flag) + }; + + let (var, datum) = match info { + ty::FragmentInfo::Moved { var, .. } | + ty::FragmentInfo::Assigned { var, .. } => { + let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| { + let ty = tables.node_types[&var]; + if self.type_needs_drop(ty) { + let datum = make_datum(var); + seen.insert(var, Some(datum.clone())); + Some(datum) + } else { + // No drop call needed, so we don't need a dropflag hint + None + } + }); + if let Some(datum) = opt_datum { + (var, datum) } else { - // No drop call needed, so we don't need a dropflag hint - None + continue } - }); - if let Some(datum) = opt_datum { - (var, datum) - } else { - continue + } + }; + match info { + ty::FragmentInfo::Moved { move_expr: expr_id, .. } => { + debug!("FragmentInfo::Moved insert drop hint for {}", expr_id); + hints.insert(expr_id, DropHint::new(var, datum)); + } + ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => { + debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id); + hints.insert(expr_id, DropHint::new(var, datum)); } } - }; - match info { - ty::FragmentInfo::Moved { move_expr: expr_id, .. } => { - debug!("FragmentInfo::Moved insert drop hint for {}", expr_id); - hints.insert(expr_id, DropHint::new(var, datum)); - } - ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => { - debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id); - hints.insert(expr_id, DropHint::new(var, datum)); - } } } + + entry_bcx } - entry_bcx -} + /// Creates lvalue datums for each of the incoming function arguments, + /// matches all argument patterns against them to produce bindings, + /// and returns the entry block (see FunctionContext::init). + fn bind_args(&'blk self, + args: &[hir::Arg], + abi: Abi, + closure_env: closure::ClosureEnv, + arg_scope: cleanup::CustomScopeIndex) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("FunctionContext::bind_args"); + let mut bcx = self.init(false); + let arg_scope_id = cleanup::CustomScope(arg_scope); -// NB: must keep 4 fns in sync: -// -// - type_of_fn -// - create_datums_for_fn_args. -// - new_fn_ctxt -// - trans_args + let mut idx = 0; + let mut llarg_idx = self.fn_ty.ret.is_indirect() as usize; -pub fn arg_kind<'a, 'tcx>(cx: &FunctionContext<'a, 'tcx>, t: Ty<'tcx>) -> datum::Rvalue { - use trans::datum::{ByRef, ByValue}; - - datum::Rvalue { - mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue } - } -} - -// create_datums_for_fn_args: creates lvalue datums for each of the -// incoming function arguments. -pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>, - args: &[hir::Arg], - arg_tys: &[Ty<'tcx>], - has_tupled_arg: bool, - arg_scope: cleanup::CustomScopeIndex) - -> Block<'a, 'tcx> { - let _icx = push_ctxt("create_datums_for_fn_args"); - let fcx = bcx.fcx; - let arg_scope_id = cleanup::CustomScope(arg_scope); - - debug!("create_datums_for_fn_args"); - - // Return an array wrapping the ValueRefs that we get from `get_param` for - // each argument into datums. - // - // For certain mode/type combinations, the raw llarg values are passed - // by value. However, within the fn body itself, we want to always - // have all locals and arguments be by-ref so that we can cancel the - // cleanup and for better interaction with LLVM's debug info. So, if - // the argument would be passed by value, we store it into an alloca. - // This alloca should be optimized away by LLVM's mem-to-reg pass in - // the event it's not truly needed. - let mut idx = fcx.arg_offset() as c_uint; - let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor"); - for (i, &arg_ty) in arg_tys.iter().enumerate() { - let arg_datum = if !has_tupled_arg || i < arg_tys.len() - 1 { - if type_of::arg_is_indirect(bcx.ccx(), arg_ty) && - bcx.sess().opts.debuginfo != FullDebugInfo { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up, unless - // we emit extra-debug-info, which requires local allocas :(. - let llarg = get_param(fcx.llfn, idx); + let has_tupled_arg = match closure_env { + closure::ClosureEnv::NotClosure => abi == Abi::RustCall, + closure::ClosureEnv::Closure(..) => { + closure_env.load(bcx, arg_scope_id); + let env_arg = &self.fn_ty.args[idx]; idx += 1; - bcx.fcx.schedule_lifetime_end(arg_scope_id, llarg); - bcx.fcx.schedule_drop_mem(arg_scope_id, llarg, arg_ty, None); - - datum::Datum::new(llarg, - arg_ty, - datum::Lvalue::new("create_datum_for_fn_args")) - } else if common::type_is_fat_ptr(bcx.tcx(), arg_ty) { - let data = get_param(fcx.llfn, idx); - let extra = get_param(fcx.llfn, idx + 1); - idx += 2; - unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "", uninit_reason, - arg_scope_id, (data, extra), - |(data, extra), bcx, dst| { - debug!("populate call for create_datum_for_fn_args \ - early fat arg, on arg[{}] ty={:?}", i, arg_ty); - - Store(bcx, data, expr::get_dataptr(bcx, dst)); - Store(bcx, extra, expr::get_meta(bcx, dst)); - bcx - })) - } else { - let llarg = get_param(fcx.llfn, idx); - idx += 1; - let tmp = datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty)); - unpack_datum!(bcx, - datum::lvalue_scratch_datum(bcx, - arg_ty, - "", - uninit_reason, - arg_scope_id, - tmp, - |tmp, bcx, dst| { - - debug!("populate call for create_datum_for_fn_args \ - early thin arg, on arg[{}] ty={:?}", i, arg_ty); - - tmp.store_to(bcx, dst) - })) + if env_arg.pad.is_some() { + llarg_idx += 1; + } + if !env_arg.is_ignore() { + llarg_idx += 1; + } + false } + }; + let tupled_arg_id = if has_tupled_arg { + args[args.len() - 1].id } else { - // FIXME(pcwalton): Reduce the amount of code bloat this is responsible for. - match arg_ty.sty { - ty::TyTuple(ref tupled_arg_tys) => { - unpack_datum!(bcx, - datum::lvalue_scratch_datum(bcx, - arg_ty, - "tupled_args", - uninit_reason, - arg_scope_id, - (), - |(), - mut bcx, - llval| { - debug!("populate call for create_datum_for_fn_args \ - tupled_args, on arg[{}] ty={:?}", i, arg_ty); - for (j, &tupled_arg_ty) in - tupled_arg_tys.iter().enumerate() { - let lldest = StructGEP(bcx, llval, j); - if common::type_is_fat_ptr(bcx.tcx(), tupled_arg_ty) { - let data = get_param(bcx.fcx.llfn, idx); - let extra = get_param(bcx.fcx.llfn, idx + 1); - Store(bcx, data, expr::get_dataptr(bcx, lldest)); - Store(bcx, extra, expr::get_meta(bcx, lldest)); - idx += 2; - } else { - let datum = datum::Datum::new( - get_param(bcx.fcx.llfn, idx), - tupled_arg_ty, - arg_kind(bcx.fcx, tupled_arg_ty)); - idx += 1; - bcx = datum.store_to(bcx, lldest); - }; + ast::DUMMY_NODE_ID + }; + + // Return an array wrapping the ValueRefs that we get from `get_param` for + // each argument into datums. + // + // For certain mode/type combinations, the raw llarg values are passed + // by value. However, within the fn body itself, we want to always + // have all locals and arguments be by-ref so that we can cancel the + // cleanup and for better interaction with LLVM's debug info. So, if + // the argument would be passed by value, we store it into an alloca. + // This alloca should be optimized away by LLVM's mem-to-reg pass in + // the event it's not truly needed. + let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor"); + for hir_arg in args { + let arg_ty = node_id_type(bcx, hir_arg.id); + let arg_datum = if hir_arg.id != tupled_arg_id { + let arg = &self.fn_ty.args[idx]; + idx += 1; + if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + let llarg = get_param(self.llfn, llarg_idx as c_uint); + llarg_idx += 1; + self.schedule_lifetime_end(arg_scope_id, llarg); + self.schedule_drop_mem(arg_scope_id, llarg, arg_ty, None); + + datum::Datum::new(llarg, + arg_ty, + datum::Lvalue::new("FunctionContext::bind_args")) + } else { + unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "", + uninit_reason, + arg_scope_id, |bcx, dst| { + debug!("FunctionContext::bind_args: {:?}: {:?}", hir_arg, arg_ty); + if common::type_is_fat_ptr(bcx.tcx(), arg_ty) { + let meta = &self.fn_ty.args[idx]; + idx += 1; + arg.store_fn_arg(bcx, &mut llarg_idx, expr::get_dataptr(bcx, dst)); + meta.store_fn_arg(bcx, &mut llarg_idx, expr::get_meta(bcx, dst)); + } else { + arg.store_fn_arg(bcx, &mut llarg_idx, dst); } bcx })) } - _ => { - bcx.tcx() - .sess - .bug("last argument of a function with `rust-call` ABI isn't a tuple?!") - } - } - }; - - let pat = &*args[i].pat; - bcx = if let Some(name) = simple_name(pat) { - // Generate nicer LLVM for the common case of fn a pattern - // like `x: T` - set_value_name(arg_datum.val, &bcx.name(name)); - bcx.fcx.lllocals.borrow_mut().insert(pat.id, arg_datum); - bcx - } else { - // General path. Copy out the values that are used in the - // pattern. - _match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id) - }; - debuginfo::create_argument_metadata(bcx, &args[i]); - } - - bcx -} - -// Ties up the llstaticallocas -> llloadenv -> lltop edges, -// and builds the return block. -pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, - last_bcx: Block<'blk, 'tcx>, - retty: ty::FnOutput<'tcx>, - ret_debug_loc: DebugLoc) { - let _icx = push_ctxt("finish_fn"); - - let ret_cx = match fcx.llreturn.get() { - Some(llreturn) => { - if !last_bcx.terminated.get() { - Br(last_bcx, llreturn, DebugLoc::None); - } - raw_block(fcx, llreturn) - } - None => last_bcx, - }; - - // This shouldn't need to recompute the return type, - // as new_fn_ctxt did it already. - let substd_retty = fcx.monomorphize(&retty); - build_return_block(fcx, ret_cx, substd_retty, ret_debug_loc); - - debuginfo::clear_source_location(fcx); - fcx.cleanup(); -} - -// Builds the return block for a function. -pub fn build_return_block<'blk, 'tcx>(fcx: &FunctionContext<'blk, 'tcx>, - ret_cx: Block<'blk, 'tcx>, - retty: ty::FnOutput<'tcx>, - ret_debug_location: DebugLoc) { - if fcx.llretslotptr.get().is_none() || - (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) { - return RetVoid(ret_cx, ret_debug_location); - } - - let retslot = if fcx.needs_ret_allocas { - Load(ret_cx, fcx.llretslotptr.get().unwrap()) - } else { - fcx.llretslotptr.get().unwrap() - }; - let retptr = Value(retslot); - match retptr.get_dominating_store(ret_cx) { - // If there's only a single store to the ret slot, we can directly return - // the value that was stored and omit the store and the alloca - Some(s) => { - let retval = s.get_operand(0).unwrap().get(); - s.erase_from_parent(); - - if retptr.has_no_uses() { - retptr.erase_from_parent(); - } - - let retval = if retty == ty::FnConverging(fcx.ccx.tcx().types.bool) { - Trunc(ret_cx, retval, Type::i1(fcx.ccx)) } else { - retval + // FIXME(pcwalton): Reduce the amount of code bloat this is responsible for. + let tupled_arg_tys = match arg_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => unreachable!("last argument of `rust-call` fn isn't a tuple?!") + }; + + unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, + arg_ty, + "tupled_args", + uninit_reason, + arg_scope_id, + |bcx, llval| { + debug!("FunctionContext::bind_args: tupled {:?}: {:?}", hir_arg, arg_ty); + for (j, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { + let dst = StructGEP(bcx, llval, j); + let arg = &self.fn_ty.args[idx]; + if common::type_is_fat_ptr(bcx.tcx(), tupled_arg_ty) { + let meta = &self.fn_ty.args[idx]; + idx += 1; + arg.store_fn_arg(bcx, &mut llarg_idx, expr::get_dataptr(bcx, dst)); + meta.store_fn_arg(bcx, &mut llarg_idx, expr::get_meta(bcx, dst)); + } else { + arg.store_fn_arg(bcx, &mut llarg_idx, dst); + } + } + bcx + })) }; - if fcx.caller_expects_out_pointer { - if let ty::FnConverging(retty) = retty { - store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); - } - RetVoid(ret_cx, ret_debug_location) + let pat = &hir_arg.pat; + bcx = if let Some(name) = simple_name(pat) { + // Generate nicer LLVM for the common case of fn a pattern + // like `x: T` + set_value_name(arg_datum.val, &bcx.name(name)); + self.lllocals.borrow_mut().insert(pat.id, arg_datum); + bcx } else { + // General path. Copy out the values that are used in the + // pattern. + _match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id) + }; + debuginfo::create_argument_metadata(bcx, hir_arg); + } + + bcx + } + + /// Ties up the llstaticallocas -> llloadenv -> lltop edges, + /// and builds the return block. + pub fn finish(&'blk self, last_bcx: Block<'blk, 'tcx>, + ret_debug_loc: DebugLoc) { + let _icx = push_ctxt("FunctionContext::finish"); + + let ret_cx = match self.llreturn.get() { + Some(llreturn) => { + if !last_bcx.terminated.get() { + Br(last_bcx, llreturn, DebugLoc::None); + } + raw_block(self, llreturn) + } + None => last_bcx, + }; + + self.build_return_block(ret_cx, ret_debug_loc); + + debuginfo::clear_source_location(self); + self.cleanup(); + } + + // Builds the return block for a function. + pub fn build_return_block(&self, ret_cx: Block<'blk, 'tcx>, + ret_debug_location: DebugLoc) { + if self.llretslotptr.get().is_none() || + ret_cx.unreachable.get() || + (!self.needs_ret_allocas && self.fn_ty.ret.is_indirect()) { + return RetVoid(ret_cx, ret_debug_location); + } + + let retslot = if self.needs_ret_allocas { + Load(ret_cx, self.llretslotptr.get().unwrap()) + } else { + self.llretslotptr.get().unwrap() + }; + let retptr = Value(retslot); + let llty = self.fn_ty.ret.original_ty; + match (retptr.get_dominating_store(ret_cx), self.fn_ty.ret.cast) { + // If there's only a single store to the ret slot, we can directly return + // the value that was stored and omit the store and the alloca. + // However, we only want to do this when there is no cast needed. + (Some(s), None) => { + let mut retval = s.get_operand(0).unwrap().get(); + s.erase_from_parent(); + + if retptr.has_no_uses() { + retptr.erase_from_parent(); + } + + if self.fn_ty.ret.is_indirect() { + Store(ret_cx, retval, get_param(self.llfn, 0)); + RetVoid(ret_cx, ret_debug_location) + } else { + if llty == Type::i1(self.ccx) { + retval = Trunc(ret_cx, retval, llty); + } + Ret(ret_cx, retval, ret_debug_location) + } + } + (_, cast_ty) if self.fn_ty.ret.is_indirect() => { + // Otherwise, copy the return value to the ret slot. + assert_eq!(cast_ty, None); + let llsz = llsize_of(self.ccx, self.fn_ty.ret.ty); + let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); + call_memcpy(ret_cx, get_param(self.llfn, 0), + retslot, llsz, llalign as u32); + RetVoid(ret_cx, ret_debug_location) + } + (_, Some(cast_ty)) => { + let load = Load(ret_cx, PointerCast(ret_cx, retslot, cast_ty.ptr_to())); + let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); + unsafe { + llvm::LLVMSetAlignment(load, llalign); + } + Ret(ret_cx, load, ret_debug_location) + } + (_, None) => { + let retval = if llty == Type::i1(self.ccx) { + let val = LoadRangeAssert(ret_cx, retslot, 0, 2, llvm::False); + Trunc(ret_cx, val, llty) + } else { + Load(ret_cx, retslot) + }; Ret(ret_cx, retval, ret_debug_location) } } - // Otherwise, copy the return value to the ret slot - None => match retty { - ty::FnConverging(retty) => { - if fcx.caller_expects_out_pointer { - memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); - RetVoid(ret_cx, ret_debug_location) - } else { - Ret(ret_cx, load_ty(ret_cx, retslot, retty), ret_debug_location) - } - } - ty::FnDiverging => { - if fcx.caller_expects_out_pointer { - RetVoid(ret_cx, ret_debug_location) - } else { - Ret(ret_cx, C_undef(Type::nil(fcx.ccx)), ret_debug_location) - } - } - }, } } @@ -1876,7 +1782,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, param_substs: &'tcx Substs<'tcx>, fn_ast_id: ast::NodeId, attributes: &[ast::Attribute], - output_type: ty::FnOutput<'tcx>, + fn_ty: FnType, abi: Abi, closure_env: closure::ClosureEnv<'b>) { ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); @@ -1888,27 +1794,13 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, debug!("trans_closure(..., param_substs={:?})", param_substs); - let has_env = match closure_env { - closure::ClosureEnv::Closure(..) => true, - closure::ClosureEnv::NotClosure => false, - }; - let (arena, fcx): (TypedArena<_>, FunctionContext); arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - llfndecl, - fn_ast_id, - has_env, - output_type, - param_substs, - Some(body.span), - &arena); - let mut bcx = init_function(&fcx, false, output_type); + fcx = FunctionContext::new(ccx, llfndecl, fn_ty, fn_ast_id, + param_substs, Some(body.span), &arena); if attributes.iter().any(|item| item.check_name("rustc_mir")) { - mir::trans_mir(bcx.build()); - fcx.cleanup(); - return; + return mir::trans_mir(&fcx); } // cleanup scope for the incoming arguments @@ -1918,50 +1810,26 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, true); let arg_scope = fcx.push_custom_cleanup_scope_with_debug_loc(fn_cleanup_debug_loc); - let block_ty = node_id_type(bcx, body.id); - // Set up arguments to the function. - let monomorphized_arg_types = decl.inputs - .iter() - .map(|arg| node_id_type(bcx, arg.id)) - .collect::>(); - for monomorphized_arg_type in &monomorphized_arg_types { - debug!("trans_closure: monomorphized_arg_type: {:?}", - monomorphized_arg_type); - } - debug!("trans_closure: function lltype: {:?}", Value(bcx.fcx.llfn)); - - let has_tupled_arg = match closure_env { - closure::ClosureEnv::NotClosure => abi == Abi::RustCall, - _ => false, - }; - - bcx = create_datums_for_fn_args(bcx, - &decl.inputs, - &monomorphized_arg_types, - has_tupled_arg, - arg_scope); - - bcx = closure_env.load(bcx, cleanup::CustomScope(arg_scope)); + debug!("trans_closure: function: {:?}", Value(fcx.llfn)); + let bcx = fcx.bind_args(&decl.inputs, abi, closure_env, arg_scope); // Up until here, IR instructions for this function have explicitly not been annotated with // source code location, so we don't step into call setup code. From here on, source location // emitting should be enabled. debuginfo::start_emitting_source_locations(&fcx); - let dest = match fcx.llretslotptr.get() { - Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(block_ty), "iret_slot")), - None => { - assert!(type_is_zero_size(bcx.ccx(), block_ty)); - expr::Ignore - } + let dest = if fcx.fn_ty.ret.is_ignore() { + expr::Ignore + } else { + expr::SaveIn(fcx.get_ret_slot(bcx, "iret_slot")) }; // This call to trans_block is the place where we bridge between // translation calls that don't have a return value (trans_crate, // trans_mod, trans_item, et cetera) and those that do // (trans_block, trans_expr, et cetera). - bcx = controlflow::trans_block(bcx, body, dest); + let mut bcx = controlflow::trans_block(bcx, body, dest); match dest { expr::SaveIn(slot) if fcx.needs_ret_allocas => { @@ -1994,7 +1862,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let ret_debug_loc = DebugLoc::At(fn_cleanup_debug_loc.id, fn_cleanup_debug_loc.span); // Insert the mandatory first few basic blocks before lltop. - finish_fn(&fcx, bcx, output_type, ret_debug_loc); + fcx.finish(bcx, ret_debug_loc); fn record_translation_item_as_generated<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, node_id: ast::NodeId, @@ -2032,11 +1900,10 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let _icx = push_ctxt("trans_fn"); let fn_ty = ccx.tcx().node_id_to_type(id); let fn_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fn_ty); - let sig = fn_ty.fn_sig(); - let sig = ccx.tcx().erase_late_bound_regions(&sig); + let sig = ccx.tcx().erase_late_bound_regions(fn_ty.fn_sig()); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); - let output_type = sig.output; let abi = fn_ty.fn_abi(); + let fn_ty = FnType::new(ccx, abi, &sig, &[]); trans_closure(ccx, decl, body, @@ -2044,7 +1911,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, param_substs, id, attrs, - output_type, + fn_ty, abi, closure::ClosureEnv::NotClosure); } @@ -2131,53 +1998,39 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let sig = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_sig()); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); - let arg_tys = sig.inputs; - let result_ty = sig.output; + let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); let (arena, fcx): (TypedArena<_>, FunctionContext); arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - llfndecl, - ctor_id, - false, - result_ty, - param_substs, - None, - &arena); - let bcx = init_function(&fcx, false, result_ty); + fcx = FunctionContext::new(ccx, llfndecl, fn_ty, ctor_id, + param_substs, None, &arena); + let bcx = fcx.init(false); assert!(!fcx.needs_ret_allocas); - if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) { - let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot"); + if !fcx.fn_ty.ret.is_ignore() { + let dest = fcx.get_ret_slot(bcx, "eret_slot"); let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value - let repr = adt::represent_type(ccx, result_ty.unwrap()); - let mut llarg_idx = fcx.arg_offset() as c_uint; - for (i, arg_ty) in arg_tys.into_iter().enumerate() { + let repr = adt::represent_type(ccx, sig.output.unwrap()); + let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; + let mut arg_idx = 0; + for (i, arg_ty) in sig.inputs.into_iter().enumerate() { let lldestptr = adt::trans_field_ptr(bcx, &repr, dest_val, Disr::from(disr), i); + let arg = &fcx.fn_ty.args[arg_idx]; + arg_idx += 1; if common::type_is_fat_ptr(bcx.tcx(), arg_ty) { - Store(bcx, - get_param(fcx.llfn, llarg_idx), - expr::get_dataptr(bcx, lldestptr)); - Store(bcx, - get_param(fcx.llfn, llarg_idx + 1), - expr::get_meta(bcx, lldestptr)); - llarg_idx += 2; + let meta = &fcx.fn_ty.args[arg_idx]; + arg_idx += 1; + arg.store_fn_arg(bcx, &mut llarg_idx, expr::get_dataptr(bcx, lldestptr)); + meta.store_fn_arg(bcx, &mut llarg_idx, expr::get_meta(bcx, lldestptr)); } else { - let arg = get_param(fcx.llfn, llarg_idx); - llarg_idx += 1; - - if arg_is_indirect(ccx, arg_ty) { - memcpy_ty(bcx, lldestptr, arg, arg_ty); - } else { - store_ty(bcx, arg, lldestptr, arg_ty); - } + arg.store_fn_arg(bcx, &mut llarg_idx, lldestptr); } } adt::trans_set_discr(bcx, &repr, dest, disr); } - finish_fn(&fcx, bcx, result_ty, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); } fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span, id: ast::NodeId) { @@ -2376,7 +2229,7 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) { let from_external = ccx.external_srcs().borrow().contains_key(&item.id); match item.node { - hir::ItemFn(ref decl, _, _, abi, ref generics, ref body) => { + hir::ItemFn(ref decl, _, _, _, ref generics, ref body) => { if !generics.is_type_parameterized() { let trans_everywhere = attr::requests_inline(&item.attrs); // Ignore `trans_everywhere` for cross-crate inlined items @@ -2387,24 +2240,7 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) { let empty_substs = tcx.mk_substs(Substs::trans_empty()); let def_id = tcx.map.local_def_id(item.id); let llfn = Callee::def(ccx, def_id, empty_substs).reify(ccx).val; - if abi != Abi::Rust { - foreign::trans_rust_fn_with_foreign_abi(ccx, - &decl, - &body, - &item.attrs, - llfn, - empty_substs, - item.id, - None); - } else { - trans_fn(ccx, - &decl, - &body, - llfn, - empty_substs, - item.id, - &item.attrs); - } + trans_fn(ccx, &decl, &body, llfn, empty_substs, item.id, &item.attrs); set_global_section(ccx, llfn, item); update_linkage(ccx, llfn, diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 3e515fd59377..042f81542d64 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -255,15 +255,18 @@ impl<'tcx> Callee<'tcx> { /// Turn the callee into a function pointer. pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> Datum<'tcx, Rvalue> { + let fn_ptr_ty = match self.ty.sty { + ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)), + _ => self.ty + }; match self.data { Fn(llfn) => { - let fn_ptr_ty = match self.ty.sty { - ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)), - _ => self.ty - }; immediate_rvalue(llfn, fn_ptr_ty) } - Virtual(idx) => meth::trans_object_shim(ccx, self.ty, idx), + Virtual(idx) => { + let llfn = meth::trans_object_shim(ccx, self.ty, idx); + immediate_rvalue(llfn, fn_ptr_ty) + } NamedTupleConstructor(_) => match self.ty.sty { ty::TyFnDef(def_id, substs, _) => { return get_fn(ccx, def_id, substs); @@ -313,6 +316,21 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true, ty::ClosureKind::FnOnce => false, }; + + let llfnpointer = match bare_fn_ty.sty { + ty::TyFnDef(def_id, substs, _) => { + // Function definitions have to be turned into a pointer. + let llfn = Callee::def(ccx, def_id, substs).reify(ccx).val; + if !is_by_ref { + // A by-value fn item is ignored, so the shim has + // the same signature as the original function. + return llfn; + } + Some(llfn) + } + _ => None + }; + let bare_fn_ty_maybe_ref = if is_by_ref { tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), bare_fn_ty) } else { @@ -347,15 +365,17 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( let sig = tcx.erase_late_bound_regions(sig); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); let tuple_input_ty = tcx.mk_tup(sig.inputs.to_vec()); + let sig = ty::FnSig { + inputs: vec![bare_fn_ty_maybe_ref, + tuple_input_ty], + output: sig.output, + variadic: false + }; + let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]); let tuple_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { unsafety: hir::Unsafety::Normal, abi: Abi::RustCall, - sig: ty::Binder(ty::FnSig { - inputs: vec![bare_fn_ty_maybe_ref, - tuple_input_ty], - output: sig.output, - variadic: false - }) + sig: ty::Binder(sig) }); debug!("tuple_fn_ty: {:?}", tuple_fn_ty); @@ -368,37 +388,26 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( let empty_substs = tcx.mk_substs(Substs::trans_empty()); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - llfn, - ast::DUMMY_NODE_ID, - false, - sig.output, - empty_substs, - None, - &block_arena); - let mut bcx = init_function(&fcx, false, sig.output); + fcx = FunctionContext::new(ccx, llfn, fn_ty, ast::DUMMY_NODE_ID, + empty_substs, None, &block_arena); + let mut bcx = fcx.init(false); let llargs = get_params(fcx.llfn); - let self_idx = fcx.arg_offset(); - let llfnpointer = match bare_fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - // Function definitions have to be turned into a pointer. - Callee::def(ccx, def_id, substs).reify(ccx).val - } - + let self_idx = fcx.fn_ty.ret.is_indirect() as usize; + let llfnpointer = llfnpointer.unwrap_or_else(|| { // the first argument (`self`) will be ptr to the fn pointer - _ => if is_by_ref { + if is_by_ref { Load(bcx, llargs[self_idx]) } else { llargs[self_idx] } - }; + }); assert!(!fcx.needs_ret_allocas); let dest = fcx.llretslotptr.get().map(|_| - expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")) + expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")) ); let callee = Callee { @@ -407,7 +416,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( }; bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx; - finish_fn(&fcx, bcx, sig.output, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index df8dd1a09b7a..10ed646cd01f 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -10,11 +10,11 @@ use arena::TypedArena; use back::link::{self, mangle_internal_name_by_path_and_seq}; -use llvm::{ValueRef, get_params}; +use llvm::{ValueRef, get_param, get_params}; use middle::def_id::DefId; use middle::infer; use middle::traits::ProjectionMode; -use trans::abi::Abi; +use trans::abi::{Abi, FnType}; use trans::adt; use trans::attributes; use trans::base::*; @@ -22,12 +22,12 @@ use trans::build::*; use trans::callee::{self, ArgVals, Callee}; use trans::cleanup::{CleanupMethods, CustomScope, ScopeId}; use trans::common::*; -use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue}; +use trans::datum::{ByRef, Datum, lvalue_scratch_datum}; +use trans::datum::{rvalue_scratch_datum, Rvalue}; use trans::debuginfo::{self, DebugLoc}; use trans::declare; use trans::expr; use trans::monomorphize::{Instance}; -use trans::type_of::*; use trans::value::Value; use trans::Disr; use middle::ty::{self, Ty, TyCtxt}; @@ -38,28 +38,26 @@ use syntax::attr::{ThinAttributes, ThinAttributesExt}; use rustc_front::hir; +use libc::c_uint; fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, closure_def_id: DefId, arg_scope_id: ScopeId, - freevars: &[ty::Freevar]) - -> Block<'blk, 'tcx> -{ + freevars: &[ty::Freevar]) { let _icx = push_ctxt("closure::load_closure_environment"); + let kind = kind_for_closure(bcx.ccx(), closure_def_id); + + let env_arg = &bcx.fcx.fn_ty.args[0]; + let mut env_idx = bcx.fcx.fn_ty.ret.is_indirect() as usize; // Special case for small by-value selfs. - let closure_ty = node_id_type(bcx, bcx.fcx.id); - let self_type = get_self_type(bcx.tcx(), closure_def_id, closure_ty); - let kind = kind_for_closure(bcx.ccx(), closure_def_id); - let llenv = if kind == ty::ClosureKind::FnOnce && - !arg_is_indirect(bcx.ccx(), self_type) { - let datum = rvalue_scratch_datum(bcx, - self_type, - "closure_env"); - store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type); - datum.val + let llenv = if kind == ty::ClosureKind::FnOnce && !env_arg.is_indirect() { + let closure_ty = node_id_type(bcx, bcx.fcx.id); + let llenv = rvalue_scratch_datum(bcx, closure_ty, "closure_env").val; + env_arg.store_fn_arg(bcx, &mut env_idx, llenv); + llenv } else { - bcx.fcx.llenv.unwrap() + get_param(bcx.fcx.llfn, env_idx as c_uint) }; // Store the pointer to closure data in an alloca for debug info because that's what the @@ -105,8 +103,6 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, freevar.span); } } - - bcx } pub enum ClosureEnv<'a> { @@ -115,26 +111,19 @@ pub enum ClosureEnv<'a> { } impl<'a> ClosureEnv<'a> { - pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) - -> Block<'blk, 'tcx> - { - match self { - ClosureEnv::NotClosure => bcx, - ClosureEnv::Closure(def_id, freevars) => { - if freevars.is_empty() { - bcx - } else { - load_closure_environment(bcx, def_id, arg_scope, freevars) - } + pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) { + if let ClosureEnv::Closure(def_id, freevars) = self { + if !freevars.is_empty() { + load_closure_environment(bcx, def_id, arg_scope, freevars); } } } } -pub fn get_self_type<'tcx>(tcx: &TyCtxt<'tcx>, - closure_id: DefId, - fn_ty: Ty<'tcx>) - -> Ty<'tcx> { +fn get_self_type<'tcx>(tcx: &TyCtxt<'tcx>, + closure_id: DefId, + fn_ty: Ty<'tcx>) + -> Ty<'tcx> { match tcx.closure_kind(closure_id) { ty::ClosureKind::Fn => { tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), fn_ty) @@ -148,10 +137,10 @@ pub fn get_self_type<'tcx>(tcx: &TyCtxt<'tcx>, /// Returns the LLVM function declaration for a closure, creating it if /// necessary. If the ID does not correspond to a closure ID, returns None. -pub fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - closure_id: DefId, - substs: &ty::ClosureSubsts<'tcx>) - -> ValueRef { +fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + closure_id: DefId, + substs: &ty::ClosureSubsts<'tcx>) + -> ValueRef { // Normalize type so differences in regions and typedefs don't cause // duplicate declarations let tcx = ccx.tcx(); @@ -246,6 +235,16 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, let sig = tcx.erase_late_bound_regions(&function_type.sig); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); + let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id, + Box::new(closure_substs.clone())); + let sig = ty::FnSig { + inputs: Some(get_self_type(tcx, closure_def_id, closure_type)) + .into_iter().chain(sig.inputs).collect(), + output: sig.output, + variadic: false + }; + let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]); + trans_closure(ccx, decl, body, @@ -253,8 +252,8 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, param_substs, id, closure_expr_attrs.as_attr_slice(), - sig.output, - function_type.abi, + fn_ty, + Abi::RustCall, ClosureEnv::Closure(closure_def_id, &freevars)); // Don't hoist this to the top of the function. It's perfectly legitimate @@ -373,17 +372,20 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", llref_fn_ty); - let ret_ty = tcx.erase_late_bound_regions(&sig.output()); - let ret_ty = infer::normalize_associated_type(ccx.tcx(), &ret_ty); // Make a version of the closure type with the same arguments, but // with argument #0 being by value. assert_eq!(abi, Abi::RustCall); sig.0.inputs[0] = closure_ty; + + let sig = tcx.erase_late_bound_regions(&sig); + let sig = infer::normalize_associated_type(ccx.tcx(), &sig); + let fn_ty = FnType::new(ccx, abi, &sig, &[]); + let llonce_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { unsafety: unsafety, abi: abi, - sig: sig + sig: ty::Binder(sig) }); // Create the by-value helper. @@ -392,36 +394,49 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - lloncefn, - ast::DUMMY_NODE_ID, - false, - ret_ty, - substs.func_substs, - None, - &block_arena); - let mut bcx = init_function(&fcx, false, ret_ty); + fcx = FunctionContext::new(ccx, lloncefn, fn_ty, ast::DUMMY_NODE_ID, + substs.func_substs, None, &block_arena); + let mut bcx = fcx.init(false); - let mut llargs = get_params(fcx.llfn); // the first argument (`self`) will be the (by value) closure env. let self_scope = fcx.push_custom_cleanup_scope(); let self_scope_id = CustomScope(self_scope); - let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty); - let self_idx = fcx.arg_offset(); - let llself = llargs[self_idx]; - let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode)); - let env_datum = unpack_datum!(bcx, - env_datum.to_lvalue_datum_in_scope(bcx, "self", - self_scope_id)); - debug!("trans_fn_once_adapter_shim: env_datum={:?}", - Value(env_datum.val)); - llargs[self_idx] = env_datum.val; + let mut llargs = get_params(fcx.llfn); + let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize; + let env_arg = &fcx.fn_ty.args[0]; + let llenv = if env_arg.is_indirect() { + Datum::new(llargs[self_idx], closure_ty, Rvalue::new(ByRef)) + .add_clean(&fcx, self_scope_id) + } else { + unpack_datum!(bcx, lvalue_scratch_datum(bcx, closure_ty, "self", + InitAlloca::Dropped, + self_scope_id, |bcx, llval| { + let mut llarg_idx = self_idx; + env_arg.store_fn_arg(bcx, &mut llarg_idx, llval); + bcx.fcx.schedule_lifetime_end(self_scope_id, llval); + bcx + })).val + }; + + debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv)); + // Adjust llargs such that llargs[self_idx..] has the call arguments. + // For zero-sized closures that means sneaking in a new argument. + if env_arg.is_ignore() { + if self_idx > 0 { + self_idx -= 1; + llargs[self_idx] = llenv; + } else { + llargs.insert(0, llenv); + } + } else { + llargs[self_idx] = llenv; + } let dest = fcx.llretslotptr.get().map( - |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot"))); + |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot"))); let callee = Callee { data: callee::Fn(llreffn), @@ -431,7 +446,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope); - finish_fn(&fcx, bcx, ret_ty, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); lloncefn } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 4d229541a835..e0a93c4916fc 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -22,7 +22,7 @@ use middle::def_id::DefId; use middle::infer; use middle::lang_items::LangItem; use middle::subst::Substs; -use trans::abi::Abi; +use trans::abi::{Abi, FnType}; use trans::base; use trans::build; use trans::builder::Builder; @@ -35,7 +35,6 @@ use trans::declare; use trans::machine; use trans::monomorphize; use trans::type_::Type; -use trans::type_of; use trans::value::Value; use middle::ty::{self, Ty, TyCtxt}; use middle::traits::{self, SelectionContext, ProjectionMode}; @@ -107,12 +106,6 @@ pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - llsize_of_alloc(ccx, llty) == 0 } -/// Identifies types which we declare to be equivalent to `void` in C for the purpose of function -/// return types. These are `()`, bot, uninhabited enums and all other zero-sized types. -pub fn return_type_is_void<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_nil() || ty.is_empty(ccx.tcx()) || type_is_zero_size(ccx, ty) -} - /// Generates a unique symbol based off the name given. This is used to create /// unique symbols for things like closures. pub fn gensym_name(name: &str) -> ast::Name { @@ -291,9 +284,6 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // always an empty parameter-environment NOTE: @jroesch another use of ParamEnv pub param_env: ty::ParameterEnvironment<'a, 'tcx>, - // The environment argument in a closure. - pub llenv: Option, - // A pointer to where to store the return value. If the return type is // immediate, this points to an alloca in the function. Otherwise, it's a // pointer to the hidden first parameter of the function. After function @@ -321,11 +311,6 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // Note that for cleanuppad-based exceptions this is not used. pub landingpad_alloca: Cell>, - // True if the caller expects this fn to use the out pointer to - // return. Either way, your code should write into the slot llretslotptr - // points to, but if this value is false, that slot will be a local alloca. - pub caller_expects_out_pointer: bool, - // Maps the DefId's for local variables to the allocas created for // them in llallocas. pub lllocals: RefCell>>, @@ -337,6 +322,9 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // paths) for the code being compiled. pub lldropflag_hints: RefCell>, + // Describes the return/argument LLVM types and their ABI handling. + pub fn_ty: FnType, + // The NodeId of the function, or -1 if it doesn't correspond to // a user-defined function. pub id: ast::NodeId, @@ -372,18 +360,6 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { self.mir.unwrap() } - pub fn arg_offset(&self) -> usize { - self.env_arg_pos() + if self.llenv.is_some() { 1 } else { 0 } - } - - pub fn env_arg_pos(&self) -> usize { - if self.caller_expects_out_pointer { - 1 - } else { - 0 - } - } - pub fn cleanup(&self) { unsafe { llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt @@ -404,14 +380,9 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { self.llreturn.get().unwrap() } - pub fn get_ret_slot(&self, bcx: Block<'a, 'tcx>, - output: ty::FnOutput<'tcx>, - name: &str) -> ValueRef { + pub fn get_ret_slot(&self, bcx: Block<'a, 'tcx>, name: &str) -> ValueRef { if self.needs_ret_allocas { - base::alloca(bcx, match output { - ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type), - ty::FnDiverging => Type::void(bcx.ccx()) - }, name) + base::alloca(bcx, self.fn_ty.ret.memory_ty(self.ccx), name) } else { self.llretslotptr.get().unwrap() } diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs index ef8d4cfff9dd..91454df15665 100644 --- a/src/librustc_trans/trans/controlflow.rs +++ b/src/librustc_trans/trans/controlflow.rs @@ -25,7 +25,6 @@ use trans::debuginfo::{DebugLoc, ToDebugLoc}; use trans::expr; use trans::machine; use trans; -use middle::ty; use rustc_front::hir; use rustc_front::util as ast_util; @@ -363,14 +362,12 @@ pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let fcx = bcx.fcx; let mut bcx = bcx; - let dest = match (fcx.llretslotptr.get(), retval_expr) { - (Some(_), Some(retval_expr)) => { - let ret_ty = expr_ty_adjusted(bcx, &retval_expr); - expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot")) - } - _ => expr::Ignore, - }; if let Some(x) = retval_expr { + let dest = if fcx.llretslotptr.get().is_some() { + expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")) + } else { + expr::Ignore + }; bcx = expr::trans_into(bcx, &x, dest); match dest { expr::SaveIn(slot) if fcx.needs_ret_allocas => { diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index e9a6aed70089..40eb29ed2505 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -35,7 +35,7 @@ use rustc_front::hir; use trans::abi::Abi; use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext, Block}; use trans; -use trans::{monomorphize, type_of}; +use trans::monomorphize; use middle::infer; use middle::ty::{self, Ty}; use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; @@ -456,10 +456,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::FnDiverging => diverging_type_metadata(cx) }); - let inputs = &if abi == Abi::RustCall { - type_of::untuple_arguments(cx, &sig.inputs) + let inputs = if abi == Abi::RustCall { + &sig.inputs[..sig.inputs.len()-1] } else { - sig.inputs + &sig.inputs[..] }; // Arguments types @@ -467,6 +467,14 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP)); } + if abi == Abi::RustCall && !sig.inputs.is_empty() { + if let ty::TyTuple(ref args) = sig.inputs[sig.inputs.len() - 1].sty { + for &argument_type in args { + signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP)); + } + } + } + return create_DIArray(DIB(cx), &signature[..]); } diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 307c511b2a8c..e63f17770bce 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -92,24 +92,19 @@ pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type) -> ValueRef { pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> ValueRef { debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); - - let f = match fn_type.sty { - ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => f, - _ => unreachable!("expected fn type for {:?}, found {:?}", name, fn_type) - }; - - let sig = ccx.tcx().erase_late_bound_regions(&f.sig); + let abi = fn_type.fn_abi(); + let sig = ccx.tcx().erase_late_bound_regions(fn_type.fn_sig()); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); debug!("declare_rust_fn (after region erasure) sig={:?}", sig); - let fty = FnType::new(ccx, f.abi, &sig, &[]); + let fty = FnType::new(ccx, abi, &sig, &[]); let llfn = declare_raw_fn(ccx, name, fty.cconv, fty.llvm_type(ccx)); if sig.output == ty::FnDiverging { llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturn); } - if f.abi != Abi::Rust && f.abi != Abi::RustCall { + if abi != Abi::Rust && abi != Abi::RustCall { attributes::unwind(llfn, false); } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index d03f4a3013c3..c10bb76fb17d 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1898,8 +1898,8 @@ fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let t_out = node_id_type(bcx, id); debug!("trans_cast({:?} as {:?})", t_in, t_out); - let mut ll_t_in = type_of::arg_type_of(ccx, t_in); - let ll_t_out = type_of::arg_type_of(ccx, t_out); + let mut ll_t_in = type_of::immediate_type_of(ccx, t_in); + let ll_t_out = type_of::immediate_type_of(ccx, t_out); // Convert the value to be cast into a ValueRef, either by-ref or // by-value as appropriate given its type: let mut datum = unpack_datum!(bcx, trans(bcx, expr)); diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 193ff31c5c85..92a50ac3181f 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -9,32 +9,17 @@ // except according to those terms. -use back::link; -use llvm::{ValueRef, get_param}; +use llvm::{ValueRef}; use llvm; use middle::weak_lang_items; -use trans::abi::{Abi, FnType}; -use trans::attributes; -use trans::base::{llvm_linkage_by_name, push_ctxt}; -use trans::base; -use trans::build::*; +use trans::base::{llvm_linkage_by_name}; use trans::common::*; -use trans::debuginfo::DebugLoc; use trans::declare; -use trans::machine; -use trans::monomorphize; -use trans::type_::Type; use trans::type_of; -use trans::value::Value; -use middle::infer; -use middle::ty::{self, Ty, TyCtxt}; -use middle::subst::Substs; +use middle::ty; -use std::cmp; -use std::iter::once; -use libc::c_uint; use syntax::attr; -use syntax::parse::token::{InternedString, special_idents}; +use syntax::parse::token::{InternedString}; use syntax::ast; use syntax::attr::AttrMetaMethods; @@ -95,369 +80,6 @@ pub fn register_static(ccx: &CrateContext, } None => // Generate an external declaration. declare::declare_global(ccx, &ident[..], llty), - }; - - // Handle thread-local external statics. - for attr in foreign_item.attrs.iter() { - if attr.check_name("thread_local") { - llvm::set_thread_local(c, true); - } - } - - return c; -} - -/////////////////////////////////////////////////////////////////////////// -// Rust functions with foreign ABIs -// -// These are normal Rust functions defined with foreign ABIs. For -// now, and perhaps forever, we translate these using a "layer of -// indirection". That is, given a Rust declaration like: -// -// extern "C" fn foo(i: u32) -> u32 { ... } -// -// we will generate a function like: -// -// S foo(T i) { -// S r; -// foo0(&r, NULL, i); -// return r; -// } -// -// #[inline_always] -// void foo0(uint32_t *r, void *env, uint32_t i) { ... } -// -// Here the (internal) `foo0` function follows the Rust ABI as normal, -// where the `foo` function follows the C ABI. We rely on LLVM to -// inline the one into the other. Of course we could just generate the -// correct code in the first place, but this is much simpler. - -pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - decl: &hir::FnDecl, - body: &hir::Block, - attrs: &[ast::Attribute], - llwrapfn: ValueRef, - param_substs: &'tcx Substs<'tcx>, - id: ast::NodeId, - hash: Option<&str>) { - let _icx = push_ctxt("foreign::build_foreign_fn"); - - let fnty = ccx.tcx().node_id_to_type(id); - let mty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fnty); - let f = match mty.sty { - ty::TyFnDef(_, _, f) => f, - _ => ccx.sess().bug("trans_rust_fn_with_foreign_abi called on non-function type") - }; - assert!(f.abi != Abi::Rust); - assert!(f.abi != Abi::RustIntrinsic); - assert!(f.abi != Abi::PlatformIntrinsic); - - let fn_sig = ccx.tcx().erase_late_bound_regions(&f.sig); - let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig); - let rust_fn_ty = ccx.tcx().mk_fn_ptr(ty::BareFnTy { - unsafety: f.unsafety, - abi: Abi::Rust, - sig: ty::Binder(fn_sig.clone()) - }); - let fty = FnType::new(ccx, f.abi, &fn_sig, &[]); - let rust_fty = FnType::new(ccx, Abi::Rust, &fn_sig, &[]); - - unsafe { // unsafe because we call LLVM operations - // Build up the Rust function (`foo0` above). - let llrustfn = build_rust_fn(ccx, decl, body, param_substs, - attrs, id, rust_fn_ty, hash); - - // Build up the foreign wrapper (`foo` above). - return build_wrap_fn(ccx, llrustfn, llwrapfn, &fn_sig, &fty, &rust_fty); - } - - fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - decl: &hir::FnDecl, - body: &hir::Block, - param_substs: &'tcx Substs<'tcx>, - attrs: &[ast::Attribute], - id: ast::NodeId, - rust_fn_ty: Ty<'tcx>, - hash: Option<&str>) - -> ValueRef - { - let _icx = push_ctxt("foreign::foreign::build_rust_fn"); - let tcx = ccx.tcx(); - - let path = - tcx.map.def_path(tcx.map.local_def_id(id)) - .into_iter() - .map(|e| e.data.as_interned_str()) - .chain(once(special_idents::clownshoe_abi.name.as_str())); - let ps = link::mangle(path, hash); - - - debug!("build_rust_fn: path={} id={} ty={:?}", - ccx.tcx().map.path_to_string(id), - id, rust_fn_ty); - - let llfn = declare::define_internal_fn(ccx, &ps, rust_fn_ty); - attributes::from_fn_attrs(ccx, attrs, llfn); - base::trans_fn(ccx, decl, body, llfn, param_substs, id, attrs); - llfn - } - - unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - llrustfn: ValueRef, - llwrapfn: ValueRef, - fn_sig: &ty::FnSig<'tcx>, - fn_ty: &FnType, - rust_fty: &FnType) { - let _icx = push_ctxt( - "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn"); - - debug!("build_wrap_fn(llrustfn={:?}, llwrapfn={:?})", - Value(llrustfn), - Value(llwrapfn)); - - // Avoid all the Rust generation stuff and just generate raw - // LLVM here. - // - // We want to generate code like this: - // - // S foo(T i) { - // S r; - // foo0(&r, NULL, i); - // return r; - // } - - if llvm::LLVMCountBasicBlocks(llwrapfn) != 0 { - ccx.sess().bug("wrapping a function inside non-empty wrapper, most likely cause is \ - multiple functions being wrapped"); - } - - let ptr = "the block\0".as_ptr(); - let the_block = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, - ptr as *const _); - - let builder = ccx.builder(); - builder.position_at_end(the_block); - - // Array for the arguments we will pass to the rust function. - let mut llrust_args = Vec::new(); - let mut next_foreign_arg_counter: c_uint = 0; - let mut next_foreign_arg = |pad: bool| -> c_uint { - next_foreign_arg_counter += if pad { - 2 - } else { - 1 - }; - next_foreign_arg_counter - 1 - }; - - // If there is an out pointer on the foreign function - let foreign_outptr = { - if fn_ty.ret.is_indirect() { - Some(get_param(llwrapfn, next_foreign_arg(false))) - } else { - None - } - }; - - let rustfn_ty = Type::from_ref(llvm::LLVMTypeOf(llrustfn)).element_type(); - let mut rust_param_tys = rustfn_ty.func_params().into_iter(); - // Push Rust return pointer, using null if it will be unused. - let rust_uses_outptr = match fn_sig.output { - ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty), - ty::FnDiverging => false - }; - let return_alloca: Option; - let llrust_ret_ty = if rust_uses_outptr { - rust_param_tys.next().expect("Missing return type!").element_type() - } else { - rustfn_ty.return_type() - }; - if rust_uses_outptr { - // Rust expects to use an outpointer. If the foreign fn - // also uses an outpointer, we can reuse it, but the types - // may vary, so cast first to the Rust type. If the - // foreign fn does NOT use an outpointer, we will have to - // alloca some scratch space on the stack. - match foreign_outptr { - Some(llforeign_outptr) => { - debug!("out pointer, foreign={:?}", - Value(llforeign_outptr)); - let llrust_retptr = - builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); - debug!("out pointer, foreign={:?} (casted)", - Value(llrust_retptr)); - llrust_args.push(llrust_retptr); - return_alloca = None; - } - - None => { - let slot = builder.alloca(llrust_ret_ty, "return_alloca"); - debug!("out pointer, \ - allocad={:?}, \ - llrust_ret_ty={:?}, \ - return_ty={:?}", - Value(slot), - llrust_ret_ty, - fn_sig.output); - llrust_args.push(slot); - return_alloca = Some(slot); - } - } - } else { - // Rust does not expect an outpointer. If the foreign fn - // does use an outpointer, then we will do a store of the - // value that the Rust fn returns. - return_alloca = None; - }; - - // Build up the arguments to the call to the rust function. - // Careful to adapt for cases where the native convention uses - // a pointer and Rust does not or vice versa. - let mut tys = fn_ty.args.iter().zip(rust_param_tys); - for i in 0..fn_sig.inputs.len() { - let rust_ty = fn_sig.inputs[i]; - let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty); - let (llforeign_arg_ty, llty) = tys.next().expect("Not enough parameter types!"); - let llrust_ty = if rust_indirect { - llty.element_type() - } else { - llty - }; - let foreign_indirect = llforeign_arg_ty.is_indirect(); - - if llforeign_arg_ty.is_ignore() { - debug!("skipping ignored arg #{}", i); - llrust_args.push(C_undef(llrust_ty)); - continue; - } - - // skip padding - let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some()); - let mut llforeign_arg = get_param(llwrapfn, foreign_index); - - if type_is_fat_ptr(ccx.tcx(), rust_ty) { - // Fat pointers are one pointer and one integer or pointer. - let a = llforeign_arg_ty; - let (b, _) = tys.next().expect("Not enough parameter types!"); - assert_eq!((a.cast, b.cast), (None, None)); - assert!(!a.is_indirect() && !b.is_indirect()); - - llrust_args.push(llforeign_arg); - let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some()); - llrust_args.push(get_param(llwrapfn, foreign_index)); - continue; - } - - debug!("llforeign_arg {}{}: {:?}", "#", - i, Value(llforeign_arg)); - debug!("rust_indirect = {}, foreign_indirect = {}", - rust_indirect, foreign_indirect); - - // Ensure that the foreign argument is indirect (by - // pointer). It makes adapting types easier, since we can - // always just bitcast pointers. - if !foreign_indirect { - llforeign_arg = if rust_ty.is_bool() { - let lltemp = builder.alloca(Type::bool(ccx), ""); - builder.store(builder.zext(llforeign_arg, Type::bool(ccx)), lltemp); - lltemp - } else { - let lltemp = builder.alloca(val_ty(llforeign_arg), ""); - builder.store(llforeign_arg, lltemp); - lltemp - } - } - - // If the types in the ABI and the Rust types don't match, - // bitcast the llforeign_arg pointer so it matches the types - // Rust expects. - if llforeign_arg_ty.cast.is_some() && !type_is_fat_ptr(ccx.tcx(), rust_ty){ - assert!(!foreign_indirect); - llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to()); - } - - let llrust_arg = if rust_indirect || type_is_fat_ptr(ccx.tcx(), rust_ty) { - llforeign_arg - } else { - if rust_ty.is_bool() { - let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False); - builder.trunc(tmp, Type::i1(ccx)) - } else if type_of::type_of(ccx, rust_ty).is_aggregate() { - // We want to pass small aggregates as immediate values, but using an aggregate - // LLVM type for this leads to bad optimizations, so its arg type is an - // appropriately sized integer and we have to convert it - let tmp = builder.bitcast(llforeign_arg, - type_of::arg_type_of(ccx, rust_ty).ptr_to()); - let load = builder.load(tmp); - llvm::LLVMSetAlignment(load, type_of::align_of(ccx, rust_ty)); - load - } else { - builder.load(llforeign_arg) - } - }; - - debug!("llrust_arg {}{}: {:?}", "#", - i, Value(llrust_arg)); - llrust_args.push(llrust_arg); - } - - // Perform the call itself - debug!("calling llrustfn = {:?}", Value(llrustfn)); - let llrust_ret_val = builder.call(llrustfn, &llrust_args, None); - rust_fty.apply_attrs_callsite(llrust_ret_val); - - // Get the return value where the foreign fn expects it. - let llforeign_ret_ty = fn_ty.ret.cast.unwrap_or(fn_ty.ret.original_ty); - match foreign_outptr { - None if llforeign_ret_ty == Type::void(ccx) => { - // Function returns `()` or `bot`, which in Rust is the LLVM - // type "{}" but in foreign ABIs is "Void". - builder.ret_void(); - } - - None if rust_uses_outptr => { - // Rust uses an outpointer, but the foreign ABI does not. Load. - let llrust_outptr = return_alloca.unwrap(); - let llforeign_outptr_casted = - builder.bitcast(llrust_outptr, llforeign_ret_ty.ptr_to()); - let llforeign_retval = builder.load(llforeign_outptr_casted); - builder.ret(llforeign_retval); - } - - None if llforeign_ret_ty != llrust_ret_ty => { - // Neither ABI uses an outpointer, but the types don't - // quite match. Must cast. Probably we should try and - // examine the types and use a concrete llvm cast, but - // right now we just use a temp memory location and - // bitcast the pointer, which is the same thing the - // old wrappers used to do. - let lltemp = builder.alloca(llforeign_ret_ty, ""); - let lltemp_casted = builder.bitcast(lltemp, llrust_ret_ty.ptr_to()); - builder.store(llrust_ret_val, lltemp_casted); - let llforeign_retval = builder.load(lltemp); - builder.ret(llforeign_retval); - } - - None => { - // Neither ABI uses an outpointer, and the types - // match. Easy peasy. - builder.ret(llrust_ret_val); - } - - Some(llforeign_outptr) if !rust_uses_outptr => { - // Foreign ABI requires an out pointer, but Rust doesn't. - // Store Rust return value. - let llforeign_outptr_casted = - builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); - builder.store(llrust_ret_val, llforeign_outptr_casted); - builder.ret_void(); - } - - Some(_) => { - // Both ABIs use outpointers. Easy peasy. - builder.ret_void(); - } - } } } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 6e31e7c571ab..3e9cc19bf757 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -21,6 +21,7 @@ use middle::lang_items::ExchangeFreeFnLangItem; use middle::subst::{Substs}; use middle::traits; use middle::ty::{self, Ty, TyCtxt}; +use trans::abi::{Abi, FnType}; use trans::adt; use trans::adt::GetDtorType; // for tcx.dtor_type() use trans::base::*; @@ -40,7 +41,6 @@ use trans::type_::Type; use trans::value::Value; use arena::TypedArena; -use libc::c_uint; use syntax::ast; use syntax::codemap::DUMMY_SP; @@ -240,13 +240,17 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } let t = g.ty(); - let llty = if type_is_sized(ccx.tcx(), t) { - type_of(ccx, t).ptr_to() - } else { - type_of(ccx, ccx.tcx().mk_box(t)).ptr_to() + let tcx = ccx.tcx(); + let sig = ty::FnSig { + inputs: vec![tcx.mk_mut_ptr(tcx.types.i8)], + output: ty::FnOutput::FnConverging(tcx.mk_nil()), + variadic: false, }; - - let llfnty = Type::glue_fn(ccx, llty); + // Create a FnType for fn(*mut i8) and substitute the real type in + // later - that prevents FnType from splitting fat pointers up. + let mut fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); + fn_ty.args[0].original_ty = type_of(ccx, t).ptr_to(); + let llfnty = fn_ty.llvm_type(ccx); // To avoid infinite recursion, don't `make_drop_glue` until after we've // added the entry to the `drop_glues` cache. @@ -260,17 +264,17 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert!(declare::get_defined_value(ccx, &fn_nm).is_none()); let llfn = declare::declare_cfn(ccx, &fn_nm, llfnty); ccx.available_drop_glues().borrow_mut().insert(g, fn_nm); + ccx.drop_glues().borrow_mut().insert(g, llfn); let _s = StatRecorder::new(ccx, format!("drop {:?}", t)); - let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); + let empty_substs = tcx.mk_substs(Substs::trans_empty()); let (arena, fcx): (TypedArena<_>, FunctionContext); arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, - ty::FnConverging(ccx.tcx().mk_nil()), - empty_substs, None, &arena); + fcx = FunctionContext::new(ccx, llfn, fn_ty, ast::DUMMY_NODE_ID, + empty_substs, None, &arena); - let bcx = init_function(&fcx, false, ty::FnConverging(ccx.tcx().mk_nil())); + let bcx = fcx.init(false); update_linkage(ccx, llfn, None, OriginalTranslation); @@ -283,9 +287,8 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // llfn is expected be declared to take a parameter of the appropriate // type, so we don't need to explicitly cast the function parameter. - let llrawptr0 = get_param(llfn, fcx.arg_offset() as c_uint); - let bcx = make_drop_glue(bcx, llrawptr0, g); - finish_fn(&fcx, bcx, ty::FnConverging(ccx.tcx().mk_nil()), DebugLoc::None); + let bcx = make_drop_glue(bcx, get_param(llfn, 0), g); + fcx.finish(bcx, DebugLoc::None); llfn } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index b200ff1ff036..df8bdb05fcd8 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -270,7 +270,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let val = if datum.kind.is_by_ref() { load_ty(bcx, datum.val, datum.ty) } else { - from_arg_ty(bcx, datum.val, datum.ty) + from_immediate(bcx, datum.val) }; let cast_val = BitCast(bcx, val, llret_ty); @@ -509,14 +509,14 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } (_, "init_dropped") => { let tp_ty = *substs.types.get(FnSpace, 0); - if !return_type_is_void(ccx, tp_ty) { + if !type_is_zero_size(ccx, tp_ty) { drop_done_fill_mem(bcx, llresult, tp_ty); } C_nil(ccx) } (_, "init") => { let tp_ty = *substs.types.get(FnSpace, 0); - if !return_type_is_void(ccx, tp_ty) { + if !type_is_zero_size(ccx, tp_ty) { // Just zero out the stack slot. (See comment on base::memzero for explanation) init_zero_mem(bcx, llresult, tp_ty); } @@ -603,21 +603,24 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } (_, "volatile_load") => { let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); + let mut ptr = llargs[0]; + if let Some(ty) = fn_ty.ret.cast { + ptr = PointerCast(bcx, ptr, ty.ptr_to()); + } let load = VolatileLoad(bcx, ptr); unsafe { llvm::LLVMSetAlignment(load, type_of::align_of(ccx, tp_ty)); } - to_arg_ty(bcx, load, tp_ty) + to_immediate(bcx, load, tp_ty) }, (_, "volatile_store") => { let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); let val = if type_is_immediate(bcx.ccx(), tp_ty) { - from_arg_ty(bcx, llargs[1], tp_ty) + from_immediate(bcx, llargs[1]) } else { Load(bcx, llargs[1]) }; + let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); let store = VolatileStore(bcx, val, ptr); unsafe { llvm::LLVMSetAlignment(store, type_of::align_of(ccx, tp_ty)); @@ -684,7 +687,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, (_, "return_address") => { - if !fcx.caller_expects_out_pointer { + if !fcx.fn_ty.ret.is_indirect() { span_err!(tcx.sess, call_info.span, E0510, "invalid use of `return_address` intrinsic: function \ does not use out pointer"); @@ -746,19 +749,17 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, match split[1] { "cxchg" => { - let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); - let cmp = from_arg_ty(bcx, llargs[1], tp_ty); - let src = from_arg_ty(bcx, llargs[2], tp_ty); + let cmp = from_immediate(bcx, llargs[1]); + let src = from_immediate(bcx, llargs[2]); + let ptr = PointerCast(bcx, llargs[0], val_ty(src).ptr_to()); let res = AtomicCmpXchg(bcx, ptr, cmp, src, order, failorder, llvm::False); ExtractValue(bcx, res, 0) } "cxchgweak" => { - let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); - let cmp = from_arg_ty(bcx, llargs[1], tp_ty); - let src = from_arg_ty(bcx, llargs[2], tp_ty); + let cmp = from_immediate(bcx, llargs[1]); + let src = from_immediate(bcx, llargs[2]); + let ptr = PointerCast(bcx, llargs[0], val_ty(src).ptr_to()); let val = AtomicCmpXchg(bcx, ptr, cmp, src, order, failorder, llvm::True); let result = ExtractValue(bcx, val, 0); let success = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); @@ -769,13 +770,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, "load" => { let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); - to_arg_ty(bcx, AtomicLoad(bcx, ptr, order), tp_ty) + let mut ptr = llargs[0]; + if let Some(ty) = fn_ty.ret.cast { + ptr = PointerCast(bcx, ptr, ty.ptr_to()); + } + to_immediate(bcx, AtomicLoad(bcx, ptr, order), tp_ty) } "store" => { - let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); - let val = from_arg_ty(bcx, llargs[1], tp_ty); + let val = from_immediate(bcx, llargs[1]); + let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); AtomicStore(bcx, val, ptr, order); C_nil(ccx) } @@ -807,9 +810,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, _ => ccx.sess().fatal("unknown atomic operation") }; - let tp_ty = *substs.types.get(FnSpace, 0); - let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty); - let val = from_arg_ty(bcx, llargs[1], tp_ty); + let val = from_immediate(bcx, llargs[1]); + let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); AtomicRMW(bcx, atom_op, ptr, val, order) } } @@ -1279,21 +1281,33 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // This is currently primarily used for the `try` intrinsic functions above. fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, name: &str, - ty: Ty<'tcx>, + inputs: Vec>, output: ty::FnOutput<'tcx>, trans: &mut for<'b> FnMut(Block<'b, 'tcx>)) -> ValueRef { let ccx = fcx.ccx; - let llfn = declare::define_internal_fn(ccx, name, ty); + let sig = ty::FnSig { + inputs: inputs, + output: output, + variadic: false, + }; + let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); + + let rust_fn_ty = ccx.tcx().mk_fn_ptr(ty::BareFnTy { + unsafety: hir::Unsafety::Unsafe, + abi: Abi::Rust, + sig: ty::Binder(sig) + }); + let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty); let (fcx, block_arena); block_arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, - output, ccx.tcx().mk_substs(Substs::trans_empty()), - None, &block_arena); - let bcx = init_function(&fcx, true, output); + fcx = FunctionContext::new(ccx, llfn, fn_ty, ast::DUMMY_NODE_ID, + ccx.tcx().mk_substs(Substs::trans_empty()), + None, &block_arena); + let bcx = fcx.init(true); trans(bcx); fcx.cleanup(); - return llfn + llfn } // Helper function used to get a handle to the `__rust_try` function used to @@ -1321,17 +1335,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, }), }); let output = ty::FnOutput::FnConverging(tcx.types.i32); - let try_fn_ty = ty::BareFnTy { - unsafety: hir::Unsafety::Unsafe, - abi: Abi::Rust, - sig: ty::Binder(ty::FnSig { - inputs: vec![fn_ty, i8p, i8p], - output: output, - variadic: false, - }), - }; - let rust_try = gen_fn(fcx, "__rust_try", tcx.mk_fn_ptr(try_fn_ty), output, - trans); + let rust_try = gen_fn(fcx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans); ccx.rust_try_fn().set(Some(rust_try)); return rust_try } @@ -1399,16 +1403,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, // going on here, all I can say is that there's a few tests cases in // LLVM's test suite which follow this pattern of instructions, so we // just do the same. - let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { - unsafety: hir::Unsafety::Unsafe, - abi: Abi::Rust, - sig: ty::Binder(ty::FnSig { - inputs: vec![], - output: output, - variadic: false, - }), - }); - gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| { + gen_fn(fcx, "__rustc_try_filter", vec![], output, &mut |bcx| { let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], dloc); let exn = InBoundsGEP(bcx, ebp, &[C_i32(ccx, -20)]); let exn = Load(bcx, BitCast(bcx, exn, Type::i8p(ccx).ptr_to())); @@ -1418,16 +1413,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, // Conveniently on x86_64 the EXCEPTION_POINTERS handle and base pointer // are passed in as arguments to the filter function, so we just pass // those along. - let filter_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { - unsafety: hir::Unsafety::Unsafe, - abi: Abi::Rust, - sig: ty::Binder(ty::FnSig { - inputs: vec![i8p, i8p], - output: output, - variadic: false, - }), - }); - gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| { + gen_fn(fcx, "__rustc_try_filter", vec![i8p, i8p], output, &mut |bcx| { let exn = llvm::get_param(bcx.fcx.llfn, 0); let rbp = llvm::get_param(bcx.fcx.llfn, 1); do_trans(bcx, exn, rbp); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 3aaab4a0d824..551bae8f4148 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -18,13 +18,13 @@ use middle::infer; use middle::subst::{Subst, Substs}; use middle::subst; use middle::traits::{self, ProjectionMode}; +use trans::abi::FnType; use trans::base::*; use trans::build::*; use trans::callee::{Callee, Virtual, ArgVals, trans_fn_pointer_shim}; use trans::closure; use trans::common::*; use trans::consts; -use trans::datum::*; use trans::debuginfo::DebugLoc; use trans::declare; use trans::expr; @@ -77,7 +77,7 @@ pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, method_ty: Ty<'tcx>, vtable_index: usize) - -> Datum<'tcx, Rvalue> { + -> ValueRef { let _icx = push_ctxt("trans_object_shim"); let tcx = ccx.tcx(); @@ -85,58 +85,41 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, vtable_index, method_ty); - let ret_ty = tcx.erase_late_bound_regions(&method_ty.fn_ret()); - let ret_ty = infer::normalize_associated_type(tcx, &ret_ty); + let sig = tcx.erase_late_bound_regions(&method_ty.fn_sig()); + let sig = infer::normalize_associated_type(tcx, &sig); + let fn_ty = FnType::new(ccx, method_ty.fn_abi(), &sig, &[]); - let shim_fn_ty = match method_ty.sty { - ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - _ => unreachable!("expected fn item type, found {}", method_ty) - }; - - // - let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim"); - let llfn = declare::define_internal_fn(ccx, &function_name, shim_fn_ty); + let function_name = link::mangle_internal_name_by_type_and_seq(ccx, method_ty, "object_shim"); + let llfn = declare::define_internal_fn(ccx, &function_name, method_ty); let empty_substs = tcx.mk_substs(Substs::trans_empty()); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - llfn, - ast::DUMMY_NODE_ID, - false, - ret_ty, - empty_substs, - None, - &block_arena); - let mut bcx = init_function(&fcx, false, ret_ty); - - let llargs = get_params(fcx.llfn); - - let self_idx = fcx.arg_offset(); - let llself = llargs[self_idx]; - let llvtable = llargs[self_idx + 1]; - - debug!("trans_object_shim: llself={:?}, llvtable={:?}", - Value(llself), Value(llvtable)); - + fcx = FunctionContext::new(ccx, llfn, fn_ty, ast::DUMMY_NODE_ID, + empty_substs, None, &block_arena); + let mut bcx = fcx.init(false); assert!(!fcx.needs_ret_allocas); + let dest = fcx.llretslotptr.get().map( - |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot"))); + |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot"))); debug!("trans_object_shim: method_offset_in_vtable={}", vtable_index); + let llargs = get_params(fcx.llfn); + let args = ArgVals(&llargs[fcx.fn_ty.ret.is_indirect() as usize..]); + let callee = Callee { data: Virtual(vtable_index), ty: method_ty }; - bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx; + bcx = callee.call(bcx, DebugLoc::None, args, dest).bcx; - finish_fn(&fcx, bcx, ret_ty, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); - immediate_rvalue(llfn, shim_fn_ty) + llfn } /// Creates a returns a dynamic vtable for the given type and vtable origin. diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index f305d8964b1f..0eea8fea8268 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -113,9 +113,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Return => { - let return_ty = bcx.monomorphize(&self.mir.return_ty); bcx.with_block(|bcx| { - base::build_return_block(self.fcx, bcx, return_ty, DebugLoc::None); + self.fcx.build_return_block(bcx, DebugLoc::None); }) } diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 14a36c42e6da..796a7c551f39 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -17,7 +17,6 @@ use trans::base; use trans::common::{self, BlockAndBuilder}; use trans::consts; use trans::machine; -use trans::type_of; use trans::mir::drop; use llvm; use trans::Disr; @@ -93,11 +92,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty) }, mir::Lvalue::ReturnPointer => { - let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); - let return_ty = fn_return_ty.unwrap(); - let llval = if !common::return_type_is_void(bcx.ccx(), return_ty) { + let llval = if !fcx.fn_ty.ret.is_ignore() { bcx.with_block(|bcx| { - fcx.get_ret_slot(bcx, fn_return_ty, "") + fcx.get_ret_slot(bcx, "") }) } else { // This is a void return; that is, there’s no place to store the value and @@ -105,11 +102,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Ergo, we return an undef ValueRef, so we do not have to special-case every // place using lvalues, and could use it the same way you use a regular // ReturnPointer LValue (i.e. store into it, load from it etc). - let llty = type_of::type_of(bcx.ccx(), return_ty).ptr_to(); + let llty = fcx.fn_ty.ret.original_ty.ptr_to(); unsafe { llvm::LLVMGetUndef(llty.to_ref()) } }; + let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); + let return_ty = fn_return_ty.unwrap(); LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) }, mir::Lvalue::Projection(ref projection) => { diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 4ad2e035945f..dac9310c8b9e 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -13,9 +13,8 @@ use llvm::{self, ValueRef}; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; use trans::base; -use trans::common::{self, Block, BlockAndBuilder}; +use trans::common::{self, Block, BlockAndBuilder, FunctionContext}; use trans::expr; -use trans::type_of; use self::lvalue::LvalueRef; use self::operand::OperandRef; @@ -77,11 +76,11 @@ enum TempRef<'tcx> { /////////////////////////////////////////////////////////////////////////// -pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) { - let fcx = bcx.fcx(); +pub fn trans_mir<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>) { + let bcx = fcx.init(false).build(); let mir = bcx.mir(); - let mir_blocks = bcx.mir().all_basic_blocks(); + let mir_blocks = mir.all_basic_blocks(); // Analyze the temps to determine which must be lvalues // FIXME @@ -111,7 +110,7 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) { let args = arg_value_refs(&bcx, mir); // Allocate a `Block` for every basic block - let block_bcxs: Vec> = + let block_bcxs: Vec> = mir_blocks.iter() .map(|&bb|{ // FIXME(#30941) this doesn't handle msvc-style exceptions @@ -138,6 +137,8 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: BlockAndBuilder<'bcx, 'tcx>) { for &bb in &mir_blocks { mircx.trans_block(bb); } + + fcx.cleanup(); } /// Produce, for each argument, a `ValueRef` pointing at the @@ -149,48 +150,41 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though let fcx = bcx.fcx(); let tcx = bcx.tcx(); - let mut idx = fcx.arg_offset() as c_uint; - mir.arg_decls - .iter() - .enumerate() - .map(|(arg_index, arg_decl)| { - let arg_ty = bcx.monomorphize(&arg_decl.ty); - let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up, unless - // we emit extra-debug-info, which requires local allocas :(. - // FIXME: lifetimes, debug info - let llarg = llvm::get_param(fcx.llfn, idx); - idx += 1; - llarg - } else if common::type_is_fat_ptr(tcx, arg_ty) { - // we pass fat pointers as two words, but we want to - // represent them internally as a pointer to two words, - // so make an alloca to store them in. - let lldata = llvm::get_param(fcx.llfn, idx); - let llextra = llvm::get_param(fcx.llfn, idx + 1); - idx += 2; - let (lltemp, dataptr, meta) = bcx.with_block(|bcx| { - let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); - (lltemp, expr::get_dataptr(bcx, lltemp), expr::get_meta(bcx, lltemp)) - }); - bcx.store(lldata, dataptr); - bcx.store(llextra, meta); - lltemp - } else { - // otherwise, arg is passed by value, so make a - // temporary and store it there - let llarg = llvm::get_param(fcx.llfn, idx); - idx += 1; - bcx.with_block(|bcx| { - let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); - base::store_ty(bcx, llarg, lltemp, arg_ty); - lltemp - }) - }; - LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) - }) - .collect() + let mut idx = 0; + let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; + mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| { + let arg = &fcx.fn_ty.args[idx]; + idx += 1; + let arg_ty = bcx.monomorphize(&arg_decl.ty); + let llval = if arg.is_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + // FIXME: lifetimes, debug info + let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + llarg_idx += 1; + llarg + } else { + bcx.with_block(|bcx| { + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + if common::type_is_fat_ptr(tcx, arg_ty) { + // we pass fat pointers as two words, but we want to + // represent them internally as a pointer to two words, + // so make an alloca to store them in. + let meta = &fcx.fn_ty.args[idx]; + idx += 1; + arg.store_fn_arg(bcx, &mut llarg_idx, expr::get_dataptr(bcx, lltemp)); + meta.store_fn_arg(bcx, &mut llarg_idx, expr::get_meta(bcx, lltemp)); + } else { + // otherwise, arg is passed by value, so make a + // temporary and store it there + arg.store_fn_arg(bcx, &mut llarg_idx, lltemp); + } + lltemp + }) + }; + LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) + }).collect() } mod analyze; diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index cf4c2dfd99b2..6c4fa9cd3bdd 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -246,8 +246,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty)); let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - let ll_t_in = type_of::arg_type_of(bcx.ccx(), operand.ty); - let ll_t_out = type_of::arg_type_of(bcx.ccx(), cast_ty); + let ll_t_in = type_of::immediate_type_of(bcx.ccx(), operand.ty); + let ll_t_out = type_of::immediate_type_of(bcx.ccx(), cast_ty); let (llval, ll_t_in, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in { let repr = adt::represent_type(bcx.ccx(), operand.ty); let llval = operand.immediate(); @@ -308,8 +308,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { OperandValue::Immediate(newval) } mir::CastKind::Misc => { // Casts from a fat-ptr. - let ll_cast_ty = type_of::arg_type_of(bcx.ccx(), cast_ty); - let ll_from_ty = type_of::arg_type_of(bcx.ccx(), operand.ty); + let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty); + let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty); if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val { if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { let ll_cft = ll_cast_ty.field_types(); diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 07dba8e6cc38..f4ae60b356bb 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -16,14 +16,12 @@ use middle::infer::normalize_associated_type; use middle::subst; use middle::subst::{Subst, Substs}; use middle::ty::fold::{TypeFolder, TypeFoldable}; -use trans::abi::Abi; use trans::attributes; use trans::base::{push_ctxt}; use trans::base::trans_fn; use trans::base; use trans::common::*; use trans::declare; -use trans::foreign; use middle::ty::{self, Ty, TyCtxt}; use trans::Disr; use rustc::front::map as hir_map; @@ -129,15 +127,15 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, }); match map_node { hir_map::NodeItem(&hir::Item { - ref attrs, node: hir::ItemFn(ref decl, _, _, abi, _, ref body), .. + ref attrs, node: hir::ItemFn(ref decl, _, _, _, _, ref body), .. }) | hir_map::NodeTraitItem(&hir::TraitItem { ref attrs, node: hir::MethodTraitItem( - hir::MethodSig { abi, ref decl, .. }, Some(ref body)), .. + hir::MethodSig { ref decl, .. }, Some(ref body)), .. }) | hir_map::NodeImplItem(&hir::ImplItem { ref attrs, node: hir::ImplItemKind::Method( - hir::MethodSig { abi, ref decl, .. }, ref body), .. + hir::MethodSig { ref decl, .. }, ref body), .. }) => { base::update_linkage(ccx, lldecl, None, base::OriginalTranslation); attributes::from_fn_attrs(ccx, attrs, lldecl); @@ -153,13 +151,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } if trans_everywhere || is_first { - if abi != Abi::Rust && abi != Abi::RustCall { - foreign::trans_rust_fn_with_foreign_abi( - ccx, decl, body, attrs, lldecl, psubsts, fn_node_id, - Some(&hash)); - } else { - trans_fn(ccx, decl, body, lldecl, psubsts, fn_node_id, attrs); - } + trans_fn(ccx, decl, body, lldecl, psubsts, fn_node_id, attrs); } } diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs index c14616935984..57bd0ba81581 100644 --- a/src/librustc_trans/trans/type_.rs +++ b/src/librustc_trans/trans/type_.rs @@ -183,10 +183,6 @@ impl Type { Type::struct_(ccx, &[], false) } - pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type { - Type::func(&[t], &Type::void(ccx)) - } - pub fn array(ty: &Type, len: u64) -> Type { ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) } @@ -206,7 +202,7 @@ impl Type { } pub fn vtable_ptr(ccx: &CrateContext) -> Type { - Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to() + Type::func(&[Type::i8p(ccx)], &Type::void(ccx)).ptr_to().ptr_to() } pub fn kind(&self) -> TypeKind { diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index 7871ebccbd47..7d5218d84daf 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -35,58 +35,6 @@ fn ensure_array_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn arg_is_indirect<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - arg_ty: Ty<'tcx>) -> bool { - !type_is_immediate(ccx, arg_ty) && !type_is_fat_ptr(ccx.tcx(), arg_ty) -} - -pub fn return_uses_outptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - ty: Ty<'tcx>) -> bool { - arg_is_indirect(ccx, ty) -} - -pub fn type_of_explicit_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - arg_ty: Ty<'tcx>) -> Type { - let llty = arg_type_of(ccx, arg_ty); - if arg_is_indirect(ccx, arg_ty) { - llty.ptr_to() - } else { - llty - } -} - -/// Yields the types of the "real" arguments for a function using the `RustCall` -/// ABI by untupling the arguments of the function. -pub fn untuple_arguments<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - inputs: &[Ty<'tcx>]) - -> Vec> { - if inputs.is_empty() { - return Vec::new() - } - - let mut result = Vec::new(); - for (i, &arg_prior_to_tuple) in inputs.iter().enumerate() { - if i < inputs.len() - 1 { - result.push(arg_prior_to_tuple); - } - } - - match inputs[inputs.len() - 1].sty { - ty::TyTuple(ref tupled_arguments) => { - debug!("untuple_arguments(): untupling arguments"); - for &tupled_argument in tupled_arguments { - result.push(tupled_argument); - } - } - _ => { - ccx.tcx().sess.bug("argument to function with \"rust-call\" ABI \ - is neither a tuple nor unit") - } - } - - result -} - // A "sizing type" is an LLVM type, the size and alignment of which are // guaranteed to be equivalent to what you would get out of `type_of()`. It's // useful because: @@ -189,16 +137,9 @@ fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type } } -pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { +pub fn immediate_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { if t.is_bool() { Type::i1(cx) - } else if type_is_immediate(cx, t) && type_of(cx, t).is_aggregate() { - // We want to pass small aggregates as immediate values, but using an aggregate LLVM type - // for this leads to bad optimizations, so its arg type is an appropriately sized integer - match machine::llsize_of_alloc(cx, sizing_type_of(cx, t)) { - 0 => type_of(cx, t), - n => Type::ix(cx, n * 8), - } } else { type_of(cx, t) }