trans: Handle type_of for Rust fn's via abi::FnType.

This commit is contained in:
Eduard Burtescu 2016-02-24 19:37:22 +02:00
parent f6bbbe1070
commit 03942056aa
4 changed files with 62 additions and 86 deletions

View File

@ -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 {

View File

@ -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);
}

View File

@ -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

View File

@ -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<Type> = 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(..) => {