diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs index a73f5ff90d9..2c3e9694844 100644 --- a/src/librustc_trans/trans/abi.rs +++ b/src/librustc_trans/trans/abi.rs @@ -23,6 +23,7 @@ use trans::cabi_powerpc; use trans::cabi_powerpc64; use trans::cabi_mips; use trans::cabi_asmjs; +use trans::machine::llsize_of_alloc; use trans::type_::Type; use trans::type_of; @@ -146,22 +147,14 @@ impl FnType { let cconv = match ccx.sess().target.target.adjust_abi(abi) { RustIntrinsic => { // Intrinsics are emitted at the call site - ccx.sess().bug("asked to register intrinsic fn"); + ccx.sess().bug("asked to compute FnType of intrinsic"); } PlatformIntrinsic => { // Intrinsics are emitted at the call site - ccx.sess().bug("asked to register platform intrinsic fn"); + ccx.sess().bug("asked to compute FnType of platform intrinsic"); } - Rust => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with Rust ABI"); - } - - RustCall => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with RustCall ABI"); - } + Rust | RustCall => llvm::CCallConv, // It's the ABI's job to select this, not us. System => ccx.sess().bug("system abi should be selected elsewhere"), @@ -184,8 +177,27 @@ impl FnType { _ => Type::void(ccx) }; - let mut args = Vec::with_capacity(sig.inputs.len() + extra_args.len()); - for ty in sig.inputs.iter().chain(extra_args.iter()) { + let mut inputs = &sig.inputs[..]; + let extra_args = if abi == RustCall { + assert!(!sig.variadic && extra_args.is_empty()); + + match inputs[inputs.len() - 1].sty { + ty::TyTuple(ref tupled_arguments) => { + inputs = &inputs[..inputs.len() - 1]; + &tupled_arguments[..] + } + _ => { + unreachable!("argument to function with \"rust-call\" ABI \ + is not a tuple"); + } + } + } else { + assert!(sig.variadic || extra_args.is_empty()); + extra_args + }; + + let mut args = Vec::with_capacity(inputs.len() + extra_args.len()); + for ty in inputs.iter().chain(extra_args.iter()) { let llty = c_type_of(ccx, ty); if type_is_fat_ptr(ccx.tcx(), ty) { args.extend(llty.field_types().into_iter().map(|llty| { @@ -203,6 +215,35 @@ impl FnType { cconv: cconv }; + if abi == Rust || abi == RustCall { + let fixup = |arg: &mut ArgType| { + if !arg.ty.is_aggregate() { + // Scalars and vectors, always immediate. + return; + } + let size = llsize_of_alloc(ccx, arg.ty); + if size > llsize_of_alloc(ccx, ccx.int_type()) { + arg.kind = Indirect; + } else if size > 0 { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast = Some(Type::ix(ccx, size * 8)); + } + }; + if let ty::FnConverging(ret_ty) = sig.output { + // Fat pointers are returned by-value. + if !type_is_fat_ptr(ccx.tcx(), ret_ty) && + fty.ret.ty != Type::void(ccx) { + fixup(&mut fty.ret); + } + }; + for arg in &mut fty.args { + fixup(arg); + } + return fty; + } + match &ccx.sess().target.target.arch[..] { "x86" => cabi_x86::compute_abi_info(ccx, &mut fty), "x86_64" => if ccx.sess().target.target.options.is_like_windows { diff --git a/src/librustc_trans/trans/cabi_x86_win64.rs b/src/librustc_trans/trans/cabi_x86_win64.rs index 7e887b2ad70..9bdf371968f 100644 --- a/src/librustc_trans/trans/cabi_x86_win64.rs +++ b/src/librustc_trans/trans/cabi_x86_win64.rs @@ -31,7 +31,9 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { } }; - fixup(&mut fty.ret, Some(Attribute::StructRet)); + if fty.ret.ty != Type::void(ccx) { + fixup(&mut fty.ret, Some(Attribute::StructRet)); + } for arg in &mut fty.args { fixup(arg, None); } diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 7bee4803935..76195f19e9f 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -26,7 +26,6 @@ use trans::abi::{Abi, FnType}; use trans::attributes; use trans::context::CrateContext; use trans::type_::Type; -use trans::type_of; use std::ffi::CString; use libc::c_uint; @@ -103,17 +102,8 @@ pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, let sig = infer::normalize_associated_type(ccx.tcx(), &sig); debug!("declare_rust_fn (after region erasure) sig={:?}", sig); - let (cconv, llfty) = if f.abi == Abi::Rust || f.abi == Abi::RustCall { - (llvm::CCallConv, type_of::type_of_rust_fn(ccx, &sig, f.abi)) - } else { - let fty = FnType::new(ccx, f.abi, &sig, &[]); - (fty.cconv, fty.to_llvm(ccx)) - }; - - // it is ok to directly access sig.0.output because we erased all - // late-bound-regions above - debug!("declare_rust_fn llfty={:?}", llfty); - let llfn = declare_raw_fn(ccx, name, cconv, llfty); + let fty = FnType::new(ccx, f.abi, &sig, &[]); + let llfn = declare_raw_fn(ccx, name, fty.cconv, fty.to_llvm(ccx)); if sig.output == ty::FnDiverging { llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturn); @@ -122,7 +112,7 @@ pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, if f.abi == Abi::Rust || f.abi == Abi::RustCall { attributes::from_fn_type(ccx, fn_type).apply_llfn(llfn); } else { - FnType::new(ccx, f.abi, &sig, &[]).add_attributes(llfn); + fty.add_attributes(llfn); } llfn diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index b4f431f09e8..662471241d5 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -13,7 +13,7 @@ use middle::def_id::DefId; use middle::infer; use middle::subst; -use trans::abi::{Abi, FnType}; +use trans::abi::FnType; use trans::adt; use trans::common::*; use trans::machine; @@ -87,59 +87,6 @@ pub fn untuple_arguments<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, result } -pub fn type_of_rust_fn<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - sig: &ty::FnSig<'tcx>, - abi: Abi) - -> Type -{ - debug!("type_of_rust_fn(sig={:?}, abi={:?})", sig, abi); - - assert!(!sig.variadic); // rust fns are never variadic - - let mut atys: Vec = Vec::new(); - - // First, munge the inputs, if this has the `rust-call` ABI. - let inputs_temp; - let inputs = if abi == Abi::RustCall { - inputs_temp = untuple_arguments(cx, &sig.inputs); - &inputs_temp - } else { - &sig.inputs - }; - - // Arg 0: Output pointer. - // (if the output type is non-immediate) - let lloutputtype = match sig.output { - ty::FnConverging(output) => { - let use_out_pointer = return_uses_outptr(cx, output); - let lloutputtype = arg_type_of(cx, output); - // Use the output as the actual return value if it's immediate. - if use_out_pointer { - atys.push(lloutputtype.ptr_to()); - Type::void(cx) - } else if return_type_is_void(cx, output) { - Type::void(cx) - } else { - lloutputtype - } - } - ty::FnDiverging => Type::void(cx) - }; - - // ... then explicit args. - for input in inputs { - let arg_ty = type_of_explicit_arg(cx, input); - - if type_is_fat_ptr(cx.tcx(), input) { - atys.extend(arg_ty.field_types()); - } else { - atys.push(arg_ty); - } - } - - Type::func(&atys[..], &lloutputtype) -} - // A "sizing type" is an LLVM type, the size and alignment of which are // guaranteed to be equivalent to what you would get out of `type_of()`. It's // useful because: @@ -375,11 +322,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ty::TyFnPtr(f) => { let sig = cx.tcx().erase_late_bound_regions(&f.sig); let sig = infer::normalize_associated_type(cx.tcx(), &sig); - if f.abi == Abi::Rust || f.abi == Abi::RustCall { - type_of_rust_fn(cx, &sig, f.abi).ptr_to() - } else { - FnType::new(cx, f.abi, &sig, &[]).to_llvm(cx).ptr_to() - } + FnType::new(cx, f.abi, &sig, &[]).to_llvm(cx).ptr_to() } ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx), ty::TyTuple(..) => {