From 03993882d634b923d9f8e467b7a991f3b07b6453 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 6 Mar 2016 13:23:20 +0200 Subject: [PATCH] trans: Handle calls for all ABIs through FnType. --- src/librustc_trans/trans/abi.rs | 30 ++ src/librustc_trans/trans/asm.rs | 45 ++- src/librustc_trans/trans/base.rs | 4 +- src/librustc_trans/trans/build.rs | 16 +- src/librustc_trans/trans/builder.rs | 11 +- src/librustc_trans/trans/callee.rs | 460 +++++++++++------------- src/librustc_trans/trans/foreign.rs | 205 ----------- src/librustc_trans/trans/intrinsic.rs | 20 +- src/librustc_trans/trans/meth.rs | 42 +-- src/librustc_trans/trans/mir/block.rs | 299 ++++++++------- src/librustc_trans/trans/mir/operand.rs | 47 +-- 11 files changed, 477 insertions(+), 702 deletions(-) diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs index 6345395d441..91e2aef26ef 100644 --- a/src/librustc_trans/trans/abi.rs +++ b/src/librustc_trans/trans/abi.rs @@ -9,6 +9,8 @@ // except according to those terms. use llvm::{self, ValueRef}; +use trans::base; +use trans::build::*; use trans::common::{type_is_fat_ptr, Block}; use trans::context::CrateContext; use trans::cabi_x86; @@ -126,6 +128,34 @@ impl ArgType { pub fn is_ignore(&self) -> bool { self.kind == ArgKind::Ignore } + + /// 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 + /// or results of call/invoke instructions into their destinations. + pub fn store(&self, bcx: Block, mut val: ValueRef, dst: ValueRef) { + if self.is_ignore() { + return; + } + if self.is_indirect() { + let llsz = llsize_of(bcx.ccx(), self.ty); + let llalign = llalign_of_min(bcx.ccx(), self.ty); + base::call_memcpy(bcx, dst, val, llsz, llalign as u32); + } else if let Some(ty) = self.cast { + let store = Store(bcx, val, PointerCast(bcx, dst, ty.ptr_to())); + let llalign = llalign_of_min(bcx.ccx(), self.ty); + if !bcx.unreachable.get() { + unsafe { + llvm::LLVMSetAlignment(store, llalign); + } + } + } else { + if self.original_ty == Type::i1(bcx.ccx()) { + val = ZExt(bcx, val, Type::i8(bcx.ccx())); + } + Store(bcx, val, dst); + } + } } /// Metadata describing how the arguments to a native function diff --git a/src/librustc_trans/trans/asm.rs b/src/librustc_trans/trans/asm.rs index 98e9a1c98ad..27c3dfc4c97 100644 --- a/src/librustc_trans/trans/asm.rs +++ b/src/librustc_trans/trans/asm.rs @@ -10,12 +10,12 @@ //! # Translation of inline assembly. -use llvm; +use llvm::{self, ValueRef}; use trans::build::*; -use trans::callee; use trans::common::*; use trans::cleanup; use trans::cleanup::CleanupMethods; +use trans::datum::{Datum, Expr}; use trans::expr; use trans::type_of; use trans::type_::Type; @@ -35,6 +35,29 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) let temp_scope = fcx.push_custom_cleanup_scope(); + let take_datum = |mut bcx: Block<'blk, 'tcx>, + arg_datum: Datum<'tcx, Expr>, + llargs: &mut Vec| + -> Block<'blk, 'tcx> { + // Make this an rvalue, since we are going to be + // passing ownership. + let arg_datum = unpack_datum!( + bcx, arg_datum.to_rvalue_datum(bcx, "arg")); + + // Now that arg_datum is owned, get it into the appropriate + // mode (ref vs value). + let arg_datum = unpack_datum!( + bcx, arg_datum.to_appropriate_datum(bcx)); + + // Technically, ownership of val passes to the callee. + // However, we must cleanup should we panic before the + // callee is actually invoked. + let val = arg_datum.add_clean(bcx.fcx, + cleanup::CustomScope(temp_scope)); + llargs.push(val); + bcx + }; + let mut ext_inputs = Vec::new(); let mut ext_constraints = Vec::new(); @@ -46,11 +69,7 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) let out_datum = unpack_datum!(bcx, expr::trans(bcx, &out.expr)); if out.is_indirect { - bcx = callee::trans_arg_datum(bcx, - expr_ty(bcx, &out.expr), - out_datum, - cleanup::CustomScope(temp_scope), - &mut inputs); + bcx = take_datum(bcx, out_datum, &mut inputs); if out.is_rw { ext_inputs.push(*inputs.last().unwrap()); ext_constraints.push(i.to_string()); @@ -59,11 +78,7 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty)); outputs.push(out_datum.val); if out.is_rw { - bcx = callee::trans_arg_datum(bcx, - expr_ty(bcx, &out.expr), - out_datum, - cleanup::CustomScope(temp_scope), - &mut ext_inputs); + bcx = take_datum(bcx, out_datum, &mut ext_inputs); ext_constraints.push(i.to_string()); } } @@ -74,11 +89,7 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) constraints.push((*c).clone()); let in_datum = unpack_datum!(bcx, expr::trans(bcx, &input)); - bcx = callee::trans_arg_datum(bcx, - expr_ty(bcx, &input), - in_datum, - cleanup::CustomScope(temp_scope), - &mut inputs); + bcx = take_datum(bcx, in_datum, &mut inputs); } inputs.extend_from_slice(&ext_inputs[..]); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4b268ba57ca..dbcbdbf5602 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1488,7 +1488,9 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, } ty::FnDiverging => false, }; - let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl); + 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) diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs index ac7e3e48fb5..22536f2dc43 100644 --- a/src/librustc_trans/trans/build.rs +++ b/src/librustc_trans/trans/build.rs @@ -12,7 +12,7 @@ #![allow(non_snake_case)] use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; +use llvm::{AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; use llvm::{Opcode, IntPredicate, RealPredicate}; use llvm::{ValueRef, BasicBlockRef}; use trans::common::*; @@ -920,20 +920,6 @@ pub fn Call(cx: Block, B(cx).call(fn_, args, bundle) } -pub fn CallWithConv(cx: Block, - fn_: ValueRef, - args: &[ValueRef], - conv: CallConv, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _UndefReturn(cx, fn_); - } - debug_loc.apply(cx.fcx); - let bundle = cx.lpad.get().and_then(|b| b.bundle()); - B(cx).call_with_conv(fn_, args, conv, bundle) -} - pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) { if cx.unreachable.get() { return; } B(cx).atomic_fence(order, scope) diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs index da5042b0caf..7f8e8393e8c 100644 --- a/src/librustc_trans/trans/builder.rs +++ b/src/librustc_trans/trans/builder.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] // FFI wrappers use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; +use llvm::{AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; use trans::base; @@ -843,15 +843,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef], - conv: CallConv, - bundle: Option<&OperandBundleDef>) -> ValueRef { - self.count_insn("callwithconv"); - let v = self.call(llfn, args, bundle); - llvm::SetInstructionCallConv(v, conv); - v - } - pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { self.count_insn("select"); unsafe { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 1d302b65f9c..3e515fd5937 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -37,6 +37,7 @@ use trans::cleanup; use trans::cleanup::CleanupMethods; use trans::closure; use trans::common::{self, Block, Result, NodeIdAndSpan, CrateContext, FunctionContext}; +use trans::common::{C_uint, C_undef}; use trans::consts; use trans::datum::*; use trans::debuginfo::DebugLoc; @@ -46,6 +47,7 @@ use trans::glue; use trans::inline; use trans::foreign; use trans::intrinsic; +use trans::machine::{llalign_of_min, llsize_of_store}; use trans::meth; use trans::monomorphize::{self, Instance}; use trans::type_::Type; @@ -60,6 +62,9 @@ use syntax::codemap::DUMMY_SP; use syntax::errors; use syntax::ptr::P; +use std::cmp; + +#[derive(Debug)] pub enum CalleeData { /// Constructor for enum variant/tuple-like-struct. NamedTupleConstructor(Disr), @@ -73,6 +78,7 @@ pub enum CalleeData { Virtual(usize) } +#[derive(Debug)] pub struct Callee<'tcx> { pub data: CalleeData, pub ty: Ty<'tcx> @@ -591,16 +597,22 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let fcx = bcx.fcx; let ccx = fcx.ccx; - let (abi, ret_ty) = match callee.ty.sty { - ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - (f.abi, sig.output) - } - _ => panic!("expected fn item or ptr in Callee::call") - }; + let abi = callee.ty.fn_abi(); + let sig = callee.ty.fn_sig(); + let output = bcx.tcx().erase_late_bound_regions(&sig.output()); + let output = infer::normalize_associated_type(bcx.tcx(), &output); - match callee.data { + let extra_args = match args { + ArgExprs(args) if abi != Abi::RustCall => { + args[sig.0.inputs.len()..].iter().map(|expr| { + common::expr_ty_adjusted(bcx, expr) + }).collect() + } + _ => vec![] + }; + let fn_ty = callee.direct_fn_type(ccx, &extra_args); + + let mut callee = match callee.data { Intrinsic => { assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic); assert!(dest.is_some()); @@ -613,7 +625,7 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, }; let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); - return intrinsic::trans_intrinsic_call(bcx, callee.ty, + return intrinsic::trans_intrinsic_call(bcx, callee.ty, &fn_ty, arg_cleanup_scope, args, dest.unwrap(), call_info); @@ -628,34 +640,25 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, dest.unwrap(), debug_loc); } - _ => {} - } - - // Intrinsics should not become actual functions. - // We trans them in place in `trans_intrinsic_call` - assert!(abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic); - - let is_rust_fn = abi == Abi::Rust || abi == Abi::RustCall; + f => f + }; // Generate a location to store the result. If the user does // not care about the result, just make a stack slot. let opt_llretslot = dest.and_then(|dest| match dest { expr::SaveIn(dst) => Some(dst), expr::Ignore => { - let ret_ty = match ret_ty { - ty::FnConverging(ret_ty) => ret_ty, - ty::FnDiverging => ccx.tcx().mk_nil() + let needs_drop = || match output { + ty::FnConverging(ret_ty) => bcx.fcx.type_needs_drop(ret_ty), + ty::FnDiverging => false }; - if !is_rust_fn || - type_of::return_uses_outptr(ccx, ret_ty) || - bcx.fcx.type_needs_drop(ret_ty) { + if fn_ty.ret.is_indirect() || fn_ty.ret.cast.is_some() || needs_drop() { // Push the out-pointer if we use an out-pointer for this // return type, otherwise push "undef". - if common::type_is_zero_size(ccx, ret_ty) { - let llty = type_of::type_of(ccx, ret_ty); - Some(common::C_undef(llty.ptr_to())) + if fn_ty.ret.is_ignore() { + Some(C_undef(fn_ty.ret.original_ty.ptr_to())) } else { - let llresult = alloc_ty(bcx, ret_ty, "__llret"); + let llresult = alloca(bcx, fn_ty.ret.original_ty, "__llret"); call_lifetime_start(bcx, llresult); Some(llresult) } @@ -665,134 +668,95 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } }); - let mut llresult = unsafe { - llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref()) - }; + // If there no destination, return must be direct, with no cast. + if opt_llretslot.is_none() { + assert!(!fn_ty.ret.is_indirect() && fn_ty.ret.cast.is_none()); + } + + let mut llargs = Vec::new(); + + if fn_ty.ret.is_indirect() { + let mut llretslot = opt_llretslot.unwrap(); + if let Some(ty) = fn_ty.ret.cast { + llretslot = PointerCast(bcx, llretslot, ty.ptr_to()); + } + llargs.push(llretslot); + } let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); + bcx = trans_args(bcx, abi, &fn_ty, &mut callee, args, &mut llargs, + cleanup::CustomScope(arg_cleanup_scope)); + fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - // The code below invokes the function, using either the Rust - // conventions (if it is a rust fn) or the native conventions - // (otherwise). The important part is that, when all is said - // and done, either the return value of the function will have been - // written in opt_llretslot (if it is Some) or `llresult` will be - // set appropriately (otherwise). - if is_rust_fn { - let mut llargs = Vec::new(); + let llfn = match callee { + Fn(f) => f, + _ => unreachable!("expected fn pointer callee, found {:?}", callee) + }; - if let (ty::FnConverging(ret_ty), Some(mut llretslot)) = (ret_ty, opt_llretslot) { - if type_of::return_uses_outptr(ccx, ret_ty) { - let llformal_ret_ty = type_of::type_of(ccx, ret_ty).ptr_to(); - let llret_ty = common::val_ty(llretslot); - if llformal_ret_ty != llret_ty { - // this could happen due to e.g. subtyping - debug!("casting actual return type ({:?}) to match formal ({:?})", - llret_ty, llformal_ret_ty); - llretslot = PointerCast(bcx, llretslot, llformal_ret_ty); - } - llargs.push(llretslot); - } + let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc); + if !bcx.unreachable.get() { + fn_ty.apply_attrs_callsite(llret); + } + + // If the function we just called does not use an outpointer, + // store the result into the rust outpointer. Cast the outpointer + // type to match because some ABIs will use a different type than + // the Rust type. e.g., a {u32,u32} struct could be returned as + // u64. + if !fn_ty.ret.is_ignore() && !fn_ty.ret.is_indirect() { + if let Some(llforeign_ret_ty) = fn_ty.ret.cast { + let llrust_ret_ty = fn_ty.ret.original_ty; + let llretslot = opt_llretslot.unwrap(); + + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); + base::call_lifetime_start(bcx, llscratch); + Store(bcx, llret, llscratch); + let llscratch_i8 = PointerCast(bcx, llscratch, Type::i8(ccx).ptr_to()); + let llretptr_i8 = PointerCast(bcx, llretslot, Type::i8(ccx).ptr_to()); + let llrust_size = llsize_of_store(ccx, llrust_ret_ty); + let llforeign_align = llalign_of_min(ccx, llforeign_ret_ty); + let llrust_align = llalign_of_min(ccx, llrust_ret_ty); + let llalign = cmp::min(llforeign_align, llrust_align); + debug!("llrust_size={}", llrust_size); + base::call_memcpy(bcx, llretptr_i8, llscratch_i8, + C_uint(ccx, llrust_size), llalign as u32); + base::call_lifetime_end(bcx, llscratch); + } else if let Some(llretslot) = opt_llretslot { + base::store_ty(bcx, llret, llretslot, output.unwrap()); } - - let arg_start = llargs.len(); - - // Push the arguments. - bcx = trans_args(bcx, - args, - callee.ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - abi); - - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - - let datum = match callee.data { - Fn(f) => immediate_rvalue(f, callee.ty), - Virtual(idx) => { - // The data and vtable pointers were split by trans_arg_datum. - let vtable = llargs.remove(arg_start + 1); - meth::get_virtual_method(bcx, vtable, idx, callee.ty) - } - _ => unreachable!() - }; - - // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, datum.val, &llargs, debug_loc); - - let fn_ty = match datum.ty.sty { - ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - FnType::new(bcx.ccx(), f.abi, &sig, &[]) - } - _ => unreachable!("expected fn type") - }; - - if !bcx.unreachable.get() { - fn_ty.apply_attrs_callsite(llret); - } - - bcx = b; - llresult = llret; - - // If the Rust convention for this type is return via - // the return value, copy it into llretslot. - if let Some(llretslot) = opt_llretslot { - let llty = fn_ty.ret.original_ty; - if !fn_ty.ret.is_indirect() && llty != Type::void(bcx.ccx()) { - store_ty(bcx, llret, llretslot, ret_ty.unwrap()) - } - } - } else { - // Lang items are the only case where dest is None, and - // they are always Rust fns. - assert!(dest.is_some()); - - let mut llargs = Vec::new(); - let (llfn, arg_tys) = match (callee.data, &args) { - (Fn(f), &ArgExprs(a)) => { - (f, a.iter().map(|x| common::expr_ty_adjusted(bcx, &x)).collect()) - } - _ => panic!("expected fn ptr and arg exprs.") - }; - bcx = trans_args(bcx, - args, - callee.ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - abi); - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - - bcx = foreign::trans_native_call(bcx, - callee.ty, - llfn, - opt_llretslot.unwrap(), - &llargs[..], - arg_tys, - debug_loc); } fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_cleanup_scope); // If the caller doesn't care about the result of this fn call, // drop the temporary slot we made. - match (dest, opt_llretslot, ret_ty) { + match (dest, opt_llretslot, output) { (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => { // drop the value if it is not being saved. - bcx = glue::drop_ty(bcx, - llretslot, - ret_ty, - debug_loc); + bcx = glue::drop_ty(bcx, llretslot, ret_ty, debug_loc); call_lifetime_end(bcx, llretslot); } _ => {} } - if ret_ty == ty::FnDiverging { + if output == ty::FnDiverging { Unreachable(bcx); } - Result::new(bcx, llresult) + Result::new(bcx, llret) } pub enum CallArgs<'a, 'tcx> { @@ -818,20 +782,19 @@ pub enum CallArgs<'a, 'tcx> { fn trans_args_under_call_abi<'blk, 'tcx>( mut bcx: Block<'blk, 'tcx>, arg_exprs: &[P], - fn_ty: Ty<'tcx>, + callee: &mut CalleeData, + fn_ty: &FnType, llargs: &mut Vec, arg_cleanup_scope: cleanup::ScopeId) -> Block<'blk, 'tcx> { - let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - let args = sig.inputs; + let mut arg_idx = 0; // Translate the `self` argument first. let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0])); bcx = trans_arg_datum(bcx, - args[0], arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); @@ -858,8 +821,8 @@ fn trans_args_under_call_abi<'blk, 'tcx>( adt::trans_field_ptr(bcx, repr_ptr, srcval, Disr(0), i) }).to_expr_datum(); bcx = trans_arg_datum(bcx, - field_type, arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); } @@ -873,64 +836,20 @@ fn trans_args_under_call_abi<'blk, 'tcx>( bcx } -fn trans_overloaded_call_args<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - arg_exprs: Vec<&hir::Expr>, - fn_ty: Ty<'tcx>, - llargs: &mut Vec, - arg_cleanup_scope: cleanup::ScopeId) - -> Block<'blk, 'tcx> { - // Translate the `self` argument first. - let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - let arg_tys = sig.inputs; - - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0])); - bcx = trans_arg_datum(bcx, - arg_tys[0], - arg_datum, - arg_cleanup_scope, - llargs); - - // Now untuple the rest of the arguments. - let tuple_type = arg_tys[1]; - match tuple_type.sty { - ty::TyTuple(ref field_types) => { - for (i, &field_type) in field_types.iter().enumerate() { - let arg_datum = - unpack_datum!(bcx, expr::trans(bcx, arg_exprs[i + 1])); - bcx = trans_arg_datum(bcx, - field_type, - arg_datum, - arg_cleanup_scope, - llargs); - } - } - _ => { - bcx.sess().span_bug(arg_exprs[0].span, - "argument to `.call()` wasn't a tuple?!") - } - }; - - bcx -} - -pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, +pub fn trans_args<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + abi: Abi, + fn_ty: &FnType, + callee: &mut CalleeData, args: CallArgs<'a, 'tcx>, - fn_ty: Ty<'tcx>, llargs: &mut Vec, - arg_cleanup_scope: cleanup::ScopeId, - abi: Abi) + arg_cleanup_scope: cleanup::ScopeId) -> Block<'blk, 'tcx> { debug!("trans_args(abi={})", abi); let _icx = push_ctxt("trans_args"); - let sig = cx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(cx.tcx(), &sig); - let arg_tys = sig.inputs; - let variadic = sig.variadic; - let mut bcx = cx; + let mut bcx = bcx; + let mut arg_idx = 0; // First we figure out the caller's view of the types of the arguments. // This will be needed if this is a generic call, because the callee has @@ -940,78 +859,90 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, if abi == Abi::RustCall { // This is only used for direct calls to the `call`, // `call_mut` or `call_once` functions. - return trans_args_under_call_abi(cx, - arg_exprs, - fn_ty, + return trans_args_under_call_abi(bcx, + arg_exprs, callee, fn_ty, llargs, arg_cleanup_scope) } - let num_formal_args = arg_tys.len(); - for (i, arg_expr) in arg_exprs.iter().enumerate() { - let arg_ty = if i >= num_formal_args { - assert!(variadic); - common::expr_ty_adjusted(cx, &arg_expr) - } else { - arg_tys[i] - }; - + for arg_expr in arg_exprs { let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_expr)); - bcx = trans_arg_datum(bcx, arg_ty, arg_datum, + bcx = trans_arg_datum(bcx, + arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); } } ArgOverloadedCall(arg_exprs) => { - return trans_overloaded_call_args(cx, - arg_exprs, - fn_ty, - llargs, - arg_cleanup_scope) + for expr in arg_exprs { + let arg_datum = + unpack_datum!(bcx, expr::trans(bcx, expr)); + bcx = trans_arg_datum(bcx, + arg_datum, + callee, fn_ty, &mut arg_idx, + arg_cleanup_scope, + llargs); + } } ArgOverloadedOp(lhs, rhs) => { - assert!(!variadic); - - bcx = trans_arg_datum(bcx, arg_tys[0], lhs, + bcx = trans_arg_datum(bcx, lhs, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); if let Some(rhs) = rhs { - assert_eq!(arg_tys.len(), 2); - bcx = trans_arg_datum(bcx, arg_tys[1], rhs, + bcx = trans_arg_datum(bcx, rhs, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); - } else { - assert_eq!(arg_tys.len(), 1); } } ArgVals(vs) => { - llargs.extend_from_slice(vs); + match *callee { + Virtual(idx) => { + llargs.push(vs[0]); + + let fn_ptr = meth::get_virtual_method(bcx, vs[1], idx); + let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + *callee = Fn(PointerCast(bcx, fn_ptr, llty)); + llargs.extend_from_slice(&vs[2..]); + } + _ => llargs.extend_from_slice(vs) + } } } bcx } -pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - formal_arg_ty: Ty<'tcx>, - arg_datum: Datum<'tcx, Expr>, - arg_cleanup_scope: cleanup::ScopeId, - llargs: &mut Vec) - -> Block<'blk, 'tcx> { +fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + arg_datum: Datum<'tcx, Expr>, + callee: &mut CalleeData, + fn_ty: &FnType, + next_idx: &mut usize, + arg_cleanup_scope: cleanup::ScopeId, + llargs: &mut Vec) + -> Block<'blk, 'tcx> { let _icx = push_ctxt("trans_arg_datum"); let mut bcx = bcx; - let ccx = bcx.ccx(); - debug!("trans_arg_datum({:?})", formal_arg_ty); + debug!("trans_arg_datum({:?})", arg_datum); - let arg_datum_ty = arg_datum.ty; + let arg = &fn_ty.args[*next_idx]; + *next_idx += 1; - debug!(" arg datum: {:?}", arg_datum); + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(C_undef(ty)); + } - let mut val = if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) && - !bcx.fcx.type_needs_drop(arg_datum_ty) { - arg_datum.val + // Determine whether we want a by-ref datum even if not appropriate. + let want_by_ref = arg.is_indirect() || arg.cast.is_some(); + + let fat_ptr = common::type_is_fat_ptr(bcx.tcx(), arg_datum.ty); + let (by_ref, val) = if fat_ptr && !bcx.fcx.type_needs_drop(arg_datum.ty) { + (true, arg_datum.val) } else { // Make this an rvalue, since we are going to be // passing ownership. @@ -1020,33 +951,70 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Now that arg_datum is owned, get it into the appropriate // mode (ref vs value). - let arg_datum = unpack_datum!( - bcx, arg_datum.to_appropriate_datum(bcx)); + let arg_datum = unpack_datum!(bcx, if want_by_ref { + arg_datum.to_ref_datum(bcx) + } else { + arg_datum.to_appropriate_datum(bcx) + }); // Technically, ownership of val passes to the callee. // However, we must cleanup should we panic before the // callee is actually invoked. - arg_datum.add_clean(bcx.fcx, arg_cleanup_scope) + (arg_datum.kind.is_by_ref(), + arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)) }; - if type_of::arg_is_indirect(ccx, formal_arg_ty) && formal_arg_ty != arg_datum_ty { - // this could happen due to e.g. subtyping - let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); - debug!("casting actual type ({:?}) to match formal ({:?})", - Value(val), llformal_arg_ty); - debug!("Rust types: {:?}; {:?}", arg_datum_ty, - formal_arg_ty); - val = PointerCast(bcx, val, llformal_arg_ty); + if arg.is_ignore() { + return bcx; } debug!("--- trans_arg_datum passing {:?}", Value(val)); - if common::type_is_fat_ptr(bcx.tcx(), formal_arg_ty) { + if fat_ptr { + // Fat pointers should be passed without any transformations. + assert!(!arg.is_indirect() && arg.cast.is_none()); llargs.push(Load(bcx, expr::get_dataptr(bcx, val))); - llargs.push(Load(bcx, expr::get_meta(bcx, val))); - } else { - llargs.push(val); + + let info_arg = &fn_ty.args[*next_idx]; + *next_idx += 1; + assert!(!info_arg.is_indirect() && info_arg.cast.is_none()); + let info = Load(bcx, expr::get_meta(bcx, val)); + + if let Virtual(idx) = *callee { + // We have to grab the fn pointer from the vtable when + // handling the first argument, ensure that here. + assert_eq!(*next_idx, 2); + assert!(info_arg.is_ignore()); + let fn_ptr = meth::get_virtual_method(bcx, info, idx); + let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + *callee = Fn(PointerCast(bcx, fn_ptr, llty)); + } else { + assert!(!info_arg.is_ignore()); + llargs.push(info); + } + return bcx; } + let mut val = val; + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if arg.original_ty == Type::i1(bcx.ccx()) { + // We store bools as i8 so we need to truncate to i1. + val = LoadRangeAssert(bcx, val, 0, 2, llvm::False); + val = Trunc(bcx, val, arg.original_ty); + } else if let Some(ty) = arg.cast { + val = Load(bcx, PointerCast(bcx, val, ty.ptr_to())); + if !bcx.unreachable.get() { + let llalign = llalign_of_min(bcx.ccx(), arg.ty); + unsafe { + llvm::LLVMSetAlignment(val, llalign); + } + } + } else { + val = Load(bcx, val); + } + } + + llargs.push(val); bcx } diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 50b11515eff..193ff31c5c8 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -107,211 +107,6 @@ pub fn register_static(ccx: &CrateContext, return c; } -/// Prepares a call to a native function. This requires adapting -/// from the Rust argument passing rules to the native rules. -/// -/// # Parameters -/// -/// - `callee_ty`: Rust type for the function we are calling -/// - `llfn`: the function pointer we are calling -/// - `llretptr`: where to store the return value of the function -/// - `llargs_rust`: a list of the argument values, prepared -/// as they would be if calling a Rust function -/// - `passed_arg_tys`: Rust type for the arguments. Normally we -/// can derive these from callee_ty but in the case of variadic -/// functions passed_arg_tys will include the Rust type of all -/// the arguments including the ones not specified in the fn's signature. -pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - callee_ty: Ty<'tcx>, - llfn: ValueRef, - llretptr: ValueRef, - llargs_rust: &[ValueRef], - passed_arg_tys: Vec>, - call_debug_loc: DebugLoc) - -> Block<'blk, 'tcx> -{ - let ccx = bcx.ccx(); - - debug!("trans_native_call(callee_ty={:?}, llfn={:?}, llretptr={:?})", - callee_ty, Value(llfn), Value(llretptr)); - - let (fn_abi, fn_sig) = match callee_ty.sty { - ty::TyFnDef(_, _, ref fn_ty) | - ty::TyFnPtr(ref fn_ty) => (fn_ty.abi, &fn_ty.sig), - _ => ccx.sess().bug("trans_native_call called on non-function type") - }; - let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig); - let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig); - - let extra_args = &passed_arg_tys[fn_sig.inputs.len()..]; - let fn_type = FnType::new(ccx, fn_abi, &fn_sig, extra_args); - - let mut llargs_foreign = Vec::new(); - - // If the foreign ABI expects return value by pointer, supply the - // pointer that Rust gave us. Sometimes we have to bitcast - // because foreign fns return slightly different (but equivalent) - // views on the same type (e.g., i64 in place of {i32,i32}). - if fn_type.ret.is_indirect() { - match fn_type.ret.cast { - Some(ty) => { - let llcastedretptr = - BitCast(bcx, llretptr, ty.ptr_to()); - llargs_foreign.push(llcastedretptr); - } - None => { - llargs_foreign.push(llretptr); - } - } - } - - let mut i = 0; - for &passed_arg_ty in &passed_arg_tys { - let arg_ty = fn_type.args[i]; - - if arg_ty.is_ignore() { - i += 1; - continue; - } - - if type_is_fat_ptr(ccx.tcx(), passed_arg_ty) { - // Fat pointers are one pointer and one integer or pointer. - let (a, b) = (fn_type.args[i], fn_type.args[i + 1]); - assert_eq!((a.cast, b.cast), (None, None)); - assert!(!a.is_indirect() && !b.is_indirect()); - - if let Some(ty) = a.pad { - llargs_foreign.push(C_undef(ty)); - } - llargs_foreign.push(llargs_rust[i]); - i += 1; - - if let Some(ty) = b.pad { - llargs_foreign.push(C_undef(ty)); - } - llargs_foreign.push(llargs_rust[i]); - i += 1; - continue; - } - - // Does Rust pass this argument by pointer? - let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_ty); - - let mut llarg_rust = llargs_rust[i]; - i += 1; - debug!("argument {}, llarg_rust={:?}, rust_indirect={}, arg_ty={:?}", - i, - Value(llarg_rust), - rust_indirect, - arg_ty); - - // Ensure that we always have the Rust value indirectly, - // because it makes bitcasting easier. - if !rust_indirect { - let scratch = base::alloc_ty(bcx, passed_arg_ty, "__arg"); - base::store_ty(bcx, llarg_rust, scratch, passed_arg_ty); - llarg_rust = scratch; - } - - debug!("llarg_rust={:?} (after indirection)", - Value(llarg_rust)); - - // Check whether we need to do any casting - if let Some(ty) = arg_ty.cast { - llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()); - } - - debug!("llarg_rust={:?} (after casting)", - Value(llarg_rust)); - - // Finally, load the value if needed for the foreign ABI - let foreign_indirect = arg_ty.is_indirect(); - let llarg_foreign = if foreign_indirect { - llarg_rust - } else if passed_arg_ty.is_bool() { - let val = LoadRangeAssert(bcx, llarg_rust, 0, 2, llvm::False); - Trunc(bcx, val, Type::i1(bcx.ccx())) - } else { - Load(bcx, llarg_rust) - }; - - debug!("argument {}, llarg_foreign={:?}", - i, Value(llarg_foreign)); - - // fill padding with undef value - if let Some(ty) = arg_ty.pad { - llargs_foreign.push(C_undef(ty)); - } - llargs_foreign.push(llarg_foreign); - } - - // A function pointer is called without the declaration available, so we have to apply - // any attributes with ABI implications directly to the call instruction. - - - let llforeign_retval = CallWithConv(bcx, - llfn, - &llargs_foreign[..], - fn_type.cconv, - call_debug_loc); - if !bcx.unreachable.get() { - fn_type.apply_attrs_callsite(llforeign_retval); - } - - // If the function we just called does not use an outpointer, - // store the result into the rust outpointer. Cast the outpointer - // type to match because some ABIs will use a different type than - // the Rust type. e.g., a {u32,u32} struct could be returned as - // u64. - let llrust_ret_ty = fn_type.ret.original_ty; - if llrust_ret_ty != Type::void(ccx) && !fn_type.ret.is_indirect() { - let llforeign_ret_ty = fn_type.ret.cast.unwrap_or(llrust_ret_ty); - - debug!("llretptr={:?}", Value(llretptr)); - debug!("llforeign_retval={:?}", Value(llforeign_retval)); - debug!("llrust_ret_ty={:?}", llrust_ret_ty); - debug!("llforeign_ret_ty={:?}", llforeign_ret_ty); - - if llrust_ret_ty == llforeign_ret_ty { - match fn_sig.output { - ty::FnConverging(result_ty) => { - base::store_ty(bcx, llforeign_retval, llretptr, result_ty) - } - ty::FnDiverging => {} - } - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); - base::call_lifetime_start(bcx, llscratch); - Store(bcx, llforeign_retval, llscratch); - let llscratch_i8 = BitCast(bcx, llscratch, Type::i8(ccx).ptr_to()); - let llretptr_i8 = BitCast(bcx, llretptr, Type::i8(ccx).ptr_to()); - let llrust_size = machine::llsize_of_store(ccx, llrust_ret_ty); - let llforeign_align = machine::llalign_of_min(ccx, llforeign_ret_ty); - let llrust_align = machine::llalign_of_min(ccx, llrust_ret_ty); - let llalign = cmp::min(llforeign_align, llrust_align); - debug!("llrust_size={}", llrust_size); - base::call_memcpy(bcx, llretptr_i8, llscratch_i8, - C_uint(ccx, llrust_size), llalign as u32); - base::call_lifetime_end(bcx, llscratch); - } - } - - return bcx; -} - /////////////////////////////////////////////////////////////////////////// // Rust functions with foreign ABIs // diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 171a1fe977d..b200ff1ff03 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -18,7 +18,7 @@ use llvm::{ValueRef, TypeKind}; use middle::infer; use middle::subst; use middle::subst::FnSpace; -use trans::abi::Abi; +use trans::abi::{Abi, FnType}; use trans::adt; use trans::attributes; use trans::base::*; @@ -172,6 +172,7 @@ pub fn check_intrinsics(ccx: &CrateContext) { /// add them to librustc_trans/trans/context.rs pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, callee_ty: Ty<'tcx>, + fn_ty: &FnType, cleanup_scope: cleanup::CustomScopeIndex, args: callee::CallArgs<'a, 'tcx>, dest: expr::Dest, @@ -396,11 +397,12 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, // Push the arguments. let mut llargs = Vec::new(); bcx = callee::trans_args(bcx, + Abi::RustIntrinsic, + fn_ty, + &mut callee::Intrinsic, args, - callee_ty, &mut llargs, - cleanup::CustomScope(cleanup_scope), - Abi::RustIntrinsic); + cleanup::CustomScope(cleanup_scope)); fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); @@ -973,7 +975,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, if val_ty(llval) != Type::void(ccx) && machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { - store_ty(bcx, llval, llresult, ret_ty); + if let Some(ty) = fn_ty.ret.cast { + let ptr = PointerCast(bcx, llresult, ty.ptr_to()); + let store = Store(bcx, llval, ptr); + unsafe { + llvm::LLVMSetAlignment(store, type_of::align_of(ccx, ret_ty)); + } + } else { + store_ty(bcx, llval, llresult, ret_ty); + } } // If we made a temporary stack slot, let's clean it up diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 2a51c393717..3aaab4a0d82 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -41,29 +41,16 @@ use syntax::codemap::DUMMY_SP; // drop_glue pointer, size, align. const VTABLE_OFFSET: usize = 3; -/// Extracts a method from a trait object's vtable, at the -/// specified index, and casts it to the given type. +/// Extracts a method from a trait object's vtable, at the specified index. pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, llvtable: ValueRef, - vtable_index: usize, - method_ty: Ty<'tcx>) - -> Datum<'tcx, Rvalue> { - let _icx = push_ctxt("meth::get_virtual_method"); - let ccx = bcx.ccx(); - + vtable_index: usize) + -> ValueRef { // Load the data pointer from the object. - debug!("get_virtual_method(callee_ty={}, vtable_index={}, llvtable={:?})", - method_ty, vtable_index, Value(llvtable)); + debug!("get_virtual_method(vtable_index={}, llvtable={:?})", + vtable_index, Value(llvtable)); - let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET])); - - // Replace the self type (&Self or Box) with an opaque pointer. - if let ty::TyFnDef(_, _, fty) = method_ty.sty { - let opaque_ty = opaque_method_ty(ccx.tcx(), fty); - immediate_rvalue(PointerCast(bcx, mptr, type_of(ccx, opaque_ty)), opaque_ty) - } else { - immediate_rvalue(mptr, method_ty) - } + Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET])) } /// Generate a shim function that allows an object type like `SomeTrait` to @@ -323,23 +310,6 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, .collect() } -/// Replace the self type (&Self or Box) with an opaque pointer. -fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>) - -> Ty<'tcx> { - let mut inputs = method_ty.sig.0.inputs.clone(); - inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::IntTy::I8)); - - tcx.mk_fn_ptr(ty::BareFnTy { - unsafety: method_ty.unsafety, - abi: method_ty.abi, - sig: ty::Binder(ty::FnSig { - inputs: inputs, - output: method_ty.sig.0.output, - variadic: method_ty.sig.0.variadic, - }), - }) -} - #[derive(Debug)] pub struct ImplMethod<'tcx> { pub method: Rc>, diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index a91b2dfcf92..f305d8964b1 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -8,25 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::{BasicBlockRef, ValueRef, OperandBundleDef}; -use rustc::middle::{infer, ty}; +use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef}; +use rustc::middle::ty; use rustc::mir::repr as mir; use trans::abi::{Abi, FnType}; use trans::adt; use trans::base; use trans::build; -use trans::callee::{Callee, Fn, Virtual}; -use trans::common::{self, Block, BlockAndBuilder}; +use trans::callee::{Callee, CalleeData, Fn, Virtual}; +use trans::common::{self, Block, BlockAndBuilder, C_undef}; use trans::debuginfo::DebugLoc; use trans::Disr; -use trans::foreign; +use trans::machine::llalign_of_min; use trans::meth; use trans::type_of; use trans::glue; use trans::type_::Type; use super::{MirContext, drop}; -use super::operand::OperandValue::{FatPtr, Immediate, Ref}; +use super::lvalue::LvalueRef; +use super::operand::OperandValue::{self, FatPtr, Immediate, Ref}; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_block(&mut self, bb: mir::BasicBlock) { @@ -152,115 +153,78 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); - let debugloc = DebugLoc::None; - // The arguments we'll be passing. Plus one to account for outptr, if used. - let mut llargs = Vec::with_capacity(args.len() + 1); - // Types of the arguments. We do not preallocate, because this vector is only - // filled when `is_foreign` is `true` and foreign calls are minority of the cases. - let mut arg_tys = Vec::new(); - let (callee, fty) = match callee.ty.sty { + let (mut callee, abi, sig) = match callee.ty.sty { ty::TyFnDef(def_id, substs, f) => { - (Callee::def(bcx.ccx(), def_id, substs), f) + (Callee::def(bcx.ccx(), def_id, substs), f.abi, &f.sig) } ty::TyFnPtr(f) => { (Callee { data: Fn(callee.immediate()), ty: callee.ty - }, f) + }, f.abi, &f.sig) } _ => unreachable!("{} is not callable", callee.ty) }; // We do not translate intrinsics here (they shouldn’t be functions) - assert!(fty.abi != Abi::RustIntrinsic && fty.abi != Abi::PlatformIntrinsic); - // Foreign-ABI functions are translated differently - let is_foreign = fty.abi != Abi::Rust && fty.abi != Abi::RustCall; + assert!(abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic); + + let extra_args = &args[sig.0.inputs.len()..]; + let extra_args = extra_args.iter().map(|op_arg| { + self.mir.operand_ty(bcx.tcx(), op_arg) + }).collect::>(); + let fn_ty = callee.direct_fn_type(bcx.ccx(), &extra_args); + + // The arguments we'll be passing. Plus one to account for outptr, if used. + let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; + let mut llargs = Vec::with_capacity(arg_count); // Prepare the return value destination - let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination { + let ret_dest = if let Some((ref d, _)) = *destination { let dest = self.trans_lvalue(&bcx, d); - let ret_ty = dest.ty.to_ty(bcx.tcx()); - if !is_foreign && type_of::return_uses_outptr(bcx.ccx(), ret_ty) { + if fn_ty.ret.is_indirect() { llargs.push(dest.llval); - (Some((dest, ret_ty)), false) + None + } else if fn_ty.ret.is_ignore() { + None } else { - (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty)) + Some(dest) } } else { - (None, false) + None }; // Split the rust-call tupled arguments off. - let (args, rest) = if fty.abi == Abi::RustCall && !args.is_empty() { + let (args, untuple) = if abi == Abi::RustCall && !args.is_empty() { let (tup, args) = args.split_last().unwrap(); - // we can reorder safely because of MIR - (args, self.trans_operand_untupled(&bcx, tup)) + (args, Some(tup)) } else { - (&args[..], vec![]) + (&args[..], None) }; - let datum = { - let mut arg_ops = args.iter().map(|arg| { - self.trans_operand(&bcx, arg) - }).chain(rest.into_iter()); + let mut idx = 0; + for arg in args { + let val = self.trans_operand(&bcx, arg).val; + self.trans_argument(&bcx, val, &mut llargs, &fn_ty, + &mut idx, &mut callee.data); + } + if let Some(tup) = untuple { + self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty, + &mut idx, &mut callee.data) + } - // Get the actual pointer we can call. - // This can involve vtable accesses or reification. - let datum = if let Virtual(idx) = callee.data { - assert!(!is_foreign); - - // Grab the first argument which is a trait object. - let vtable = match arg_ops.next().unwrap().val { - FatPtr(data, vtable) => { - llargs.push(data); - vtable - } - _ => unreachable!("expected FatPtr for Virtual call") - }; - - bcx.with_block(|bcx| { - meth::get_virtual_method(bcx, vtable, idx, callee.ty) - }) - } else { - callee.reify(bcx.ccx()) - }; - - // Process the rest of the args. - for operand in arg_ops { - match operand.val { - Ref(llval) | Immediate(llval) => llargs.push(llval), - FatPtr(b, e) => { - llargs.push(b); - llargs.push(e); - } - } - if is_foreign { - arg_tys.push(operand.ty); - } - } - - datum - }; - - let fn_ty = match datum.ty.sty { - ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - FnType::new(bcx.ccx(), f.abi, &sig, &[]) - } - _ => unreachable!("expected fn type") - }; + let fn_ptr = callee.reify(bcx.ccx()).val; // Many different ways to call a function handled here - match (is_foreign, cleanup, destination) { + match (cleanup, destination) { // The two cases below are the only ones to use LLVM’s `invoke`. - (false, &Some(cleanup), &None) => { + (&Some(cleanup), &None) => { let cleanup = self.bcx(cleanup); let landingpad = self.make_landing_pad(cleanup); let unreachable_blk = self.unreachable_block(); - let cs = bcx.invoke(datum.val, - &llargs[..], + let cs = bcx.invoke(fn_ptr, + &llargs, unreachable_blk.llbb, landingpad.llbb(), cleanup_bundle.as_ref()); @@ -269,22 +233,20 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.set_operand_dropped(bcx, op); }); }, - (false, &Some(cleanup), &Some((_, success))) => { + (&Some(cleanup), &Some((_, success))) => { let cleanup = self.bcx(cleanup); let landingpad = self.make_landing_pad(cleanup); - let invokeret = bcx.invoke(datum.val, - &llargs[..], + let invokeret = bcx.invoke(fn_ptr, + &llargs, self.llblock(success), landingpad.llbb(), cleanup_bundle.as_ref()); fn_ty.apply_attrs_callsite(invokeret); - if must_copy_dest { - let (ret_dest, ret_ty) = ret_dest_ty - .expect("return destination and type not set"); + if let Some(ret_dest) = ret_dest { // We translate the copy straight into the beginning of the target // block. self.bcx(success).at_start(|bcx| bcx.with_block( |bcx| { - base::store_ty(bcx, invokeret, ret_dest.llval, ret_ty); + fn_ty.ret.store(bcx, invokeret, ret_dest.llval); })); } self.bcx(success).at_start(|bcx| for op in args { @@ -294,24 +256,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.set_operand_dropped(bcx, op); }); }, - (false, _, &None) => { - let cs = bcx.call(datum.val, - &llargs[..], - cleanup_bundle.as_ref()); + (&None, &None) => { + let cs = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref()); fn_ty.apply_attrs_callsite(cs); // no need to drop args, because the call never returns bcx.unreachable(); } - (false, _, &Some((_, target))) => { - let llret = bcx.call(datum.val, - &llargs[..], - cleanup_bundle.as_ref()); + (&None, &Some((_, target))) => { + let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref()); fn_ty.apply_attrs_callsite(llret); - if must_copy_dest { - let (ret_dest, ret_ty) = ret_dest_ty - .expect("return destination and type not set"); + if let Some(ret_dest) = ret_dest { bcx.with_block(|bcx| { - base::store_ty(bcx, llret, ret_dest.llval, ret_ty); + fn_ty.ret.store(bcx, llret, ret_dest.llval); }); } for op in args { @@ -319,31 +275,132 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } funclet_br(bcx, self.llblock(target)); } - // Foreign functions - (true, _, destination) => { - let (dest, _) = ret_dest_ty - .expect("return destination is not set"); - bcx = bcx.map_block(|bcx| { - foreign::trans_native_call(bcx, - datum.ty, - datum.val, - dest.llval, - &llargs[..], - arg_tys, - debugloc) - }); - if let Some((_, target)) = *destination { - for op in args { - self.set_operand_dropped(&bcx, op); - } - funclet_br(bcx, self.llblock(target)); - } - }, } } } } + fn trans_argument(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + val: OperandValue, + llargs: &mut Vec, + fn_ty: &FnType, + next_idx: &mut usize, + callee: &mut CalleeData) { + // Treat the values in a fat pointer separately. + if let FatPtr(ptr, meta) = val { + if *next_idx == 0 { + if let Virtual(idx) = *callee { + let llfn = bcx.with_block(|bcx| { + meth::get_virtual_method(bcx, meta, idx) + }); + let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + *callee = Fn(bcx.pointercast(llfn, llty)); + } + } + self.trans_argument(bcx, Immediate(ptr), llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, Immediate(meta), llargs, fn_ty, next_idx, callee); + return; + } + + let arg = &fn_ty.args[*next_idx]; + *next_idx += 1; + + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(C_undef(ty)); + } + + if arg.is_ignore() { + return; + } + + // Force by-ref if we have to load through a cast pointer. + let (mut llval, by_ref) = match val { + Immediate(llval) if arg.cast.is_some() => { + let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); + bcx.store(llval, llscratch); + (llscratch, true) + } + Immediate(llval) => (llval, false), + Ref(llval) => (llval, true), + FatPtr(_, _) => unreachable!("fat pointers handled above") + }; + + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if arg.original_ty == Type::i1(bcx.ccx()) { + // We store bools as i8 so we need to truncate to i1. + llval = bcx.load_range_assert(llval, 0, 2, llvm::False); + llval = bcx.trunc(llval, arg.original_ty); + } else if let Some(ty) = arg.cast { + llval = bcx.load(bcx.pointercast(llval, ty.ptr_to())); + let llalign = llalign_of_min(bcx.ccx(), arg.ty); + unsafe { + llvm::LLVMSetAlignment(llval, llalign); + } + } else { + llval = bcx.load(llval); + } + } + + llargs.push(llval); + } + + fn trans_arguments_untupled(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>, + llargs: &mut Vec, + fn_ty: &FnType, + next_idx: &mut usize, + callee: &mut CalleeData) { + // FIXME: consider having some optimization to avoid tupling/untupling + // (and storing/loading in the case of immediates) + + // avoid trans_operand for pointless copying + let lv = match *operand { + mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue), + mir::Operand::Constant(ref constant) => { + // FIXME: consider being less pessimized + if constant.ty.is_nil() { + return; + } + + let ty = bcx.monomorphize(&constant.ty); + let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca"); + let constant = self.trans_constant(bcx, constant); + self.store_operand(bcx, lv.llval, constant); + lv + } + }; + + let lv_ty = lv.ty.to_ty(bcx.tcx()); + let result_types = match lv_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => bcx.tcx().sess.span_bug( + self.mir.span, + &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty)) + }; + + let base_repr = adt::represent_type(bcx.ccx(), lv_ty); + let base = adt::MaybeSizedValue::sized(lv.llval); + for (n, &ty) in result_types.iter().enumerate() { + let ptr = bcx.with_block(|bcx| { + adt::trans_field_ptr(bcx, &base_repr, base, Disr(0), n) + }); + let val = if common::type_is_fat_ptr(bcx.tcx(), ty) { + let (lldata, llextra) = bcx.with_block(|bcx| { + base::load_fat_ptr(bcx, ptr, ty) + }); + FatPtr(lldata, llextra) + } else { + // Don't bother loading the value, trans_argument will. + Ref(ptr) + }; + self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee); + } + } + fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) -> ValueRef { let ccx = bcx.ccx(); if let Some(slot) = self.llpersonalityslot { diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 0cc21ded6f9..0871b2e394e 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -9,20 +9,17 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::{self, Ty}; +use rustc::middle::ty::Ty; use rustc::mir::repr as mir; -use trans::adt; use trans::base; use trans::common::{self, Block, BlockAndBuilder}; use trans::datum; use trans::value::Value; -use trans::Disr; use trans::glue; use std::fmt; use super::{MirContext, TempRef, drop}; -use super::lvalue::LvalueRef; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -190,48 +187,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - pub fn trans_operand_untupled(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, - operand: &mir::Operand<'tcx>) - -> Vec> - { - // FIXME: consider having some optimization to avoid tupling/untupling - // (and storing/loading in the case of immediates) - - // avoid trans_operand for pointless copying - let lv = match *operand { - mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue), - mir::Operand::Constant(ref constant) => { - // FIXME: consider being less pessimized - if constant.ty.is_nil() { - return vec![]; - } - - let ty = bcx.monomorphize(&constant.ty); - let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca"); - let constant = self.trans_constant(bcx, constant); - self.store_operand(bcx, lv.llval, constant); - lv - } - }; - - let lv_ty = lv.ty.to_ty(bcx.tcx()); - let result_types = match lv_ty.sty { - ty::TyTuple(ref tys) => tys, - _ => bcx.tcx().sess.span_bug( - self.mir.span, - &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty)) - }; - - let base_repr = adt::represent_type(bcx.ccx(), lv_ty); - let base = adt::MaybeSizedValue::sized(lv.llval); - result_types.iter().enumerate().map(|(n, &ty)| { - self.trans_load(bcx, bcx.with_block(|bcx| { - adt::trans_field_ptr(bcx, &base_repr, base, Disr(0), n) - }), ty) - }).collect() - } - pub fn set_operand_dropped(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, operand: &mir::Operand<'tcx>) {