// 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;
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
//! # 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;
let temp_scope = fcx.push_custom_cleanup_scope();
+ let take_datum = |mut bcx: Block<'blk, 'tcx>,
+ arg_datum: Datum<'tcx, Expr>,
+ llargs: &mut Vec<ValueRef>|
+ -> 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();
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());
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());
}
}
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[..]);
}
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)
#![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::*;
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)
#![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;
}
}
- 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 {
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;
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;
use syntax::errors;
use syntax::ptr::P;
+use std::cmp;
+
+#[derive(Debug)]
pub enum CalleeData {
/// Constructor for enum variant/tuple-like-struct.
NamedTupleConstructor(Disr),
Virtual(usize)
}
+#[derive(Debug)]
pub struct Callee<'tcx> {
pub data: CalleeData,
pub ty: Ty<'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)
+ 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);
+
+ 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()
}
- _ => panic!("expected fn item or ptr in Callee::call")
+ _ => vec![]
};
+ let fn_ty = callee.direct_fn_type(ccx, &extra_args);
- match callee.data {
+ let mut callee = match callee.data {
Intrinsic => {
assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic);
assert!(dest.is_some());
};
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);
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)
}
}
});
- 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 arg_cleanup_scope = fcx.push_custom_cleanup_scope();
+ let mut llargs = Vec::new();
- // 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();
-
- 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);
- }
+ 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_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")
- };
+ 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();
- if !bcx.unreachable.get() {
- fn_ty.apply_attrs_callsite(llret);
- }
+ let llfn = match callee {
+ Fn(f) => f,
+ _ => unreachable!("expected fn pointer callee, found {:?}", callee)
+ };
- bcx = b;
- llresult = llret;
+ let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc);
+ if !bcx.unreachable.get() {
+ fn_ty.apply_attrs_callsite(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())
- }
+ // 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());
}
- } 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> {
fn trans_args_under_call_abi<'blk, 'tcx>(
mut bcx: Block<'blk, 'tcx>,
arg_exprs: &[P<hir::Expr>],
- fn_ty: Ty<'tcx>,
+ callee: &mut CalleeData,
+ fn_ty: &FnType,
llargs: &mut Vec<ValueRef>,
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);
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);
}
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<ValueRef>,
- 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<ValueRef>,
- 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
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<ValueRef>)
- -> 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<ValueRef>)
+ -> 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));
+ }
+
+ // Determine whether we want a by-ref datum even if not appropriate.
+ let want_by_ref = arg.is_indirect() || arg.cast.is_some();
- 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
+ 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.
// 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
}
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<Ty<'tcx>>,
- 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
//
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::*;
/// 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,
// 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();
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
// 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));
-
- let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
+ debug!("get_virtual_method(vtable_index={}, llvtable={:?})",
+ vtable_index, Value(llvtable));
- // Replace the self type (&Self or Box<Self>) 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
.collect()
}
-/// Replace the self type (&Self or Box<Self>) 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<ty::Method<'tcx>>,
// 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) {
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::<Vec<_>>();
+ 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());
-
- // 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);
- }
- }
+ 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)
+ }
- 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());
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 {
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 {
}
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<ValueRef>,
+ 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<ValueRef>,
+ 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 {
// 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
}
}
- pub fn trans_operand_untupled(&mut self,
- bcx: &BlockAndBuilder<'bcx, 'tcx>,
- operand: &mir::Operand<'tcx>)
- -> Vec<OperandRef<'tcx>>
- {
- // 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>) {