pub use self::ArgKind::*;
-use llvm::Attribute;
-use std::option;
+use llvm::{self, AttrHelper, ValueRef};
+use trans::attributes;
+use trans::common::return_type_is_void;
use trans::context::CrateContext;
use trans::cabi_x86;
use trans::cabi_x86_64;
use trans::cabi_mips;
use trans::cabi_asmjs;
use trans::type_::Type;
+use trans::type_of;
+
+use middle::ty::{self, Ty};
+
+use syntax::abi::Abi;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ArgKind {
/// Original LLVM type
pub ty: Type,
/// Coerced LLVM Type
- pub cast: option::Option<Type>,
+ pub cast: Option<Type>,
/// Dummy argument, which is emitted before the real argument
- pub pad: option::Option<Type>,
+ pub pad: Option<Type>,
/// LLVM attribute of argument
- pub attr: option::Option<Attribute>
+ pub attr: Option<llvm::Attribute>
}
impl ArgType {
- pub fn direct(ty: Type, cast: option::Option<Type>,
- pad: option::Option<Type>,
- attr: option::Option<Attribute>) -> ArgType {
+ pub fn direct(ty: Type, cast: Option<Type>,
+ pad: Option<Type>,
+ attr: Option<llvm::Attribute>) -> ArgType {
ArgType {
kind: Direct,
ty: ty,
}
}
- pub fn indirect(ty: Type, attr: option::Option<Attribute>) -> ArgType {
+ pub fn indirect(ty: Type, attr: Option<llvm::Attribute>) -> ArgType {
ArgType {
kind: Indirect,
ty: ty,
- cast: option::Option::None,
- pad: option::Option::None,
+ cast: Option::None,
+ pad: Option::None,
attr: attr
}
}
}
}
+fn c_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
+ if ty.is_bool() {
+ Type::i1(cx)
+ } else {
+ type_of::type_of(cx, ty)
+ }
+}
+
/// Metadata describing how the arguments to a native function
/// should be passed in order to respect the native ABI.
///
/// comments are reverse-engineered and may be inaccurate. -NDM
pub struct FnType {
/// The LLVM types of each argument.
- pub arg_tys: Vec<ArgType> ,
+ pub args: Vec<ArgType>,
/// LLVM return type.
- pub ret_ty: ArgType,
+ pub ret: ArgType,
+
+ pub variadic: bool,
+
+ pub cconv: llvm::CallConv
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- match &ccx.sess().target.target.arch[..] {
- "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def),
- "x86_64" => if ccx.sess().target.target.options.is_like_windows {
- cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def)
+impl FnType {
+ pub fn new<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ abi: Abi,
+ sig: &ty::FnSig<'tcx>,
+ extra_args: &[Ty<'tcx>]) -> FnType {
+ use syntax::abi::Abi::*;
+ 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");
+ }
+ PlatformIntrinsic => {
+ // Intrinsics are emitted at the call site
+ ccx.sess().bug("asked to register platform intrinsic fn");
+ }
+
+ 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");
+ }
+
+ // It's the ABI's job to select this, not us.
+ System => ccx.sess().bug("system abi should be selected elsewhere"),
+
+ Stdcall => llvm::X86StdcallCallConv,
+ Fastcall => llvm::X86FastcallCallConv,
+ Vectorcall => llvm::X86_VectorCall,
+ C => llvm::CCallConv,
+ Win64 => llvm::X86_64_Win64,
+
+ // These API constants ought to be more specific...
+ Cdecl => llvm::CCallConv,
+ Aapcs => llvm::CCallConv,
+ };
+
+ let rty = match sig.output {
+ ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
+ c_type_of(ccx, ret_ty)
+ }
+ _ => Type::void(ccx)
+ };
+
+ let mut fty = FnType {
+ args: sig.inputs.iter().chain(extra_args.iter()).map(|&ty| {
+ ArgType::direct(c_type_of(ccx, ty), None, None, None)
+ }).collect(),
+ ret: ArgType::direct(rty, None, None, None),
+ variadic: sig.variadic,
+ cconv: cconv
+ };
+
+ 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 {
+ cabi_x86_win64::compute_abi_info(ccx, &mut fty);
+ } else {
+ cabi_x86_64::compute_abi_info(ccx, &mut fty);
+ },
+ "aarch64" => cabi_aarch64::compute_abi_info(ccx, &mut fty),
+ "arm" => {
+ let flavor = if ccx.sess().target.target.target_os == "ios" {
+ cabi_arm::Flavor::Ios
+ } else {
+ cabi_arm::Flavor::General
+ };
+ cabi_arm::compute_abi_info(ccx, &mut fty, flavor);
+ },
+ "mips" => cabi_mips::compute_abi_info(ccx, &mut fty),
+ "powerpc" => cabi_powerpc::compute_abi_info(ccx, &mut fty),
+ "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, &mut fty),
+ "asmjs" => cabi_asmjs::compute_abi_info(ccx, &mut fty),
+ a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a))
+ }
+
+ fty
+ }
+
+ pub fn to_llvm(&self, ccx: &CrateContext) -> Type {
+ let mut llargument_tys = Vec::new();
+
+ let llreturn_ty = if self.ret.is_indirect() {
+ llargument_tys.push(self.ret.ty.ptr_to());
+ Type::void(ccx)
} else {
- cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def)
- },
- "aarch64" => cabi_aarch64::compute_abi_info(ccx, atys, rty, ret_def),
- "arm" => {
- let flavor = if ccx.sess().target.target.target_os == "ios" {
- cabi_arm::Flavor::Ios
+ self.ret.cast.unwrap_or(self.ret.ty)
+ };
+
+ for arg in &self.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ // add padding
+ if let Some(ty) = arg.pad {
+ llargument_tys.push(ty);
+ }
+
+ let llarg_ty = if arg.is_indirect() {
+ arg.ty.ptr_to()
} else {
- cabi_arm::Flavor::General
+ arg.cast.unwrap_or(arg.ty)
};
- cabi_arm::compute_abi_info(ccx, atys, rty, ret_def, flavor)
- },
- "mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
- "powerpc" => cabi_powerpc::compute_abi_info(ccx, atys, rty, ret_def),
- "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, atys, rty, ret_def),
- "asmjs" => cabi_asmjs::compute_abi_info(ccx, atys, rty, ret_def),
- a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)
- ),
+
+ llargument_tys.push(llarg_ty);
+ }
+
+ if self.variadic {
+ Type::variadic_func(&llargument_tys, &llreturn_ty)
+ } else {
+ Type::func(&llargument_tys, &llreturn_ty)
+ }
+ }
+
+ pub fn add_attributes(&self, llfn: ValueRef) {
+ let mut i = if self.ret.is_indirect() {
+ 1
+ } else {
+ 0
+ };
+
+ if let Some(attr) = self.ret.attr {
+ attr.apply_llfn(i, llfn);
+ }
+
+ i += 1;
+
+ for arg in &self.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ // skip padding
+ if arg.pad.is_some() { i += 1; }
+
+ if let Some(attr) = arg.attr {
+ attr.apply_llfn(i, llfn);
+ }
+
+ i += 1;
+ }
+
+ attributes::unwind(llfn, false);
}
}
}
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let mut arg_tys = Vec::new();
- for &aty in atys {
- let ty = classify_arg_ty(ccx, aty);
- arg_tys.push(ty);
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty);
}
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty);
+ }
}
}
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool,
- flavor: Flavor) -> FnType {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) {
let align_fn = match flavor {
Flavor::General => general_ty_align as TyAlignFn,
Flavor::Ios => ios_ty_align as TyAlignFn,
};
- let mut arg_tys = Vec::new();
- for &aty in atys {
- let ty = classify_arg_ty(ccx, aty, align_fn);
- arg_tys.push(ty);
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty, align_fn);
}
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty, align_fn)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty, align_fn);
+ }
}
}
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let mut arg_tys = Vec::new();
- for &aty in atys {
- let ty = classify_arg_ty(ccx, aty);
- arg_tys.push(ty);
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty);
}
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty);
+ }
}
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- let sret = ret_ty.is_indirect();
- let mut arg_tys = Vec::new();
- let mut offset = if sret { 4 } else { 0 };
-
- for aty in atys {
- let ty = classify_arg_ty(ccx, *aty, &mut offset);
- arg_tys.push(ty);
- };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty);
+ }
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty, &mut offset);
+ }
}
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- let sret = ret_ty.is_indirect();
- let mut arg_tys = Vec::new();
- let mut offset = if sret { 4 } else { 0 };
-
- for aty in atys {
- let ty = classify_arg_ty(ccx, *aty, &mut offset);
- arg_tys.push(ty);
- };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty);
+ }
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty, &mut offset);
+ }
}
Type::struct_(ccx, &coerce_to_long(ccx, size), false)
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let ret_ty = if ret_def {
- classify_ret_ty(ccx, rty)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
-
- let mut arg_tys = Vec::new();
- for &aty in atys {
- let ty = classify_arg_ty(ccx, aty);
- arg_tys.push(ty);
- };
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = classify_ret_ty(ccx, fty.ret.ty);
+ }
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
+ for arg in &mut fty.args {
+ *arg = classify_arg_ty(ccx, arg.ty);
+ }
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use self::Strategy::*;
use llvm::*;
use trans::cabi::{ArgType, FnType};
use trans::type_::Type;
use super::common::*;
use super::machine::*;
-enum Strategy { RetValue(Type), RetPointer }
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let mut arg_tys = Vec::new();
-
- let ret_ty;
- if !ret_def {
- ret_ty = ArgType::direct(Type::void(ccx), None, None, None);
- } else if rty.kind() == Struct {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ if fty.ret.ty.kind() == Struct {
// Returning a structure. Most often, this will use
// a hidden first argument. On some platforms, though,
// small structs are returned as integers.
// Some links:
// http://www.angelcode.com/dev/callconv/callconv.html
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
+ let indirect = ArgType::indirect(fty.ret.ty, Some(Attribute::StructRet));
let t = &ccx.sess().target.target;
- let strategy = if t.options.is_like_osx || t.options.is_like_windows {
- match llsize_of_alloc(ccx, rty) {
- 1 => RetValue(Type::i8(ccx)),
- 2 => RetValue(Type::i16(ccx)),
- 4 => RetValue(Type::i32(ccx)),
- 8 => RetValue(Type::i64(ccx)),
- _ => RetPointer
+ if t.options.is_like_osx || t.options.is_like_windows {
+ match llsize_of_alloc(ccx, fty.ret.ty) {
+ 1 => fty.ret.cast = Some(Type::i8(ccx)),
+ 2 => fty.ret.cast = Some(Type::i16(ccx)),
+ 4 => fty.ret.cast = Some(Type::i32(ccx)),
+ 8 => fty.ret.cast = Some(Type::i64(ccx)),
+ _ => fty.ret = indirect
}
} else {
- RetPointer
- };
-
- match strategy {
- RetValue(t) => {
- ret_ty = ArgType::direct(rty, Some(t), None, None);
- }
- RetPointer => {
- ret_ty = ArgType::indirect(rty, Some(Attribute::StructRet));
- }
+ fty.ret = indirect;
}
- } else {
- let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
- ret_ty = ArgType::direct(rty, None, None, attr);
+ } else if fty.ret.ty == Type::i1(ccx) {
+ fty.ret.attr = Some(Attribute::ZExt);
}
- for &t in atys {
- let ty = match t.kind() {
- Struct => {
- let size = llsize_of_alloc(ccx, t);
- if size == 0 {
- ArgType::ignore(t)
- } else {
- ArgType::indirect(t, Some(Attribute::ByVal))
- }
- }
- _ => {
- let attr = if t == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
- ArgType::direct(t, None, None, attr)
- }
- };
- arg_tys.push(ty);
+ for arg in &mut fty.args {
+ if arg.ty.kind() == Struct {
+ *arg = if llsize_of_alloc(ccx, arg.ty) == 0 {
+ ArgType::ignore(arg.ty)
+ } else {
+ ArgType::indirect(arg.ty, Some(Attribute::ByVal))
+ };
+ } else if arg.ty == Type::i1(ccx) {
+ arg.attr = Some(Attribute::ZExt);
+ }
}
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
}
}
}
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
fn x86_64_ty<F>(ccx: &CrateContext,
ty: Type,
is_mem_cls: F,
let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
let mut sse_regs = 8; // XMM0-7
- let ret_ty = if ret_def {
- x86_64_ty(ccx, rty, |cls| {
+ if fty.ret.ty != Type::void(ccx) {
+ fty.ret = x86_64_ty(ccx, fty.ret.ty, |cls| {
if cls.is_ret_bysret() {
// `sret` parameter thus one less register available
int_regs -= 1;
} else {
false
}
- }, Attribute::StructRet)
- } else {
- ArgType::direct(Type::void(ccx), None, None, None)
- };
+ }, Attribute::StructRet);
+ }
- let mut arg_tys = Vec::new();
- for t in atys {
- let ty = x86_64_ty(ccx, *t, |cls| {
+ for arg in &mut fty.args {
+ *arg = x86_64_ty(ccx, arg.ty, |cls| {
let needed_int = cls.iter().filter(|&&c| c == Int).count() as isize;
let needed_sse = cls.iter().filter(|c| c.is_sse()).count() as isize;
let in_mem = cls.is_pass_byval() ||
}
in_mem
}, Attribute::ByVal);
- arg_tys.push(ty);
// An integer, pointer, double or float parameter
// thus the above closure passed to `x86_64_ty` won't
// get called.
- if t.kind() == Integer || t.kind() == Pointer {
- int_regs -= 1;
- } else if t.kind() == Double || t.kind() == Float {
- sse_regs -= 1;
+ match arg.ty.kind() {
+ Integer | Pointer => int_regs -= 1,
+ Double | Float => sse_regs -= 1,
+ _ => {}
}
}
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
- };
}
// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
-pub fn compute_abi_info(ccx: &CrateContext,
- atys: &[Type],
- rty: Type,
- ret_def: bool) -> FnType {
- let mut arg_tys = Vec::new();
-
- let ret_ty;
- if !ret_def {
- ret_ty = ArgType::direct(Type::void(ccx), None, None, None);
- } else if rty.kind() == Struct {
- ret_ty = match llsize_of_alloc(ccx, rty) {
- 1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None),
- 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None),
- 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None),
- 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None),
- _ => ArgType::indirect(rty, Some(Attribute::StructRet))
- };
- } else {
- let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
- ret_ty = ArgType::direct(rty, None, None, attr);
- }
-
- for &t in atys {
- let ty = match t.kind() {
- Struct => {
- match llsize_of_alloc(ccx, t) {
- 1 => ArgType::direct(t, Some(Type::i8(ccx)), None, None),
- 2 => ArgType::direct(t, Some(Type::i16(ccx)), None, None),
- 4 => ArgType::direct(t, Some(Type::i32(ccx)), None, None),
- 8 => ArgType::direct(t, Some(Type::i64(ccx)), None, None),
- _ => ArgType::indirect(t, None)
- }
- }
- _ => {
- let attr = if t == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
- ArgType::direct(t, None, None, attr)
+pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+ let fixup = |a: &mut ArgType, indirect_attr| {
+ if a.ty.kind() == Struct {
+ match llsize_of_alloc(ccx, a.ty) {
+ 1 => a.cast = Some(Type::i8(ccx)),
+ 2 => a.cast = Some(Type::i16(ccx)),
+ 4 => a.cast = Some(Type::i32(ccx)),
+ 8 => a.cast = Some(Type::i64(ccx)),
+ _ => *a = ArgType::indirect(a.ty, indirect_attr)
}
- };
- arg_tys.push(ty);
- }
-
- return FnType {
- arg_tys: arg_tys,
- ret_ty: ret_ty,
+ } else if a.ty == Type::i1(ccx) {
+ a.attr = Some(Attribute::ZExt);
+ }
};
+
+ fixup(&mut fty.ret, Some(Attribute::StructRet));
+ for arg in &mut fty.args {
+ fixup(arg, None);
+ }
}
use trans::base::{llvm_linkage_by_name, push_ctxt};
use trans::base;
use trans::build::*;
-use trans::cabi;
+use trans::cabi::FnType;
use trans::common::*;
use trans::debuginfo::DebugLoc;
use trans::declare;
use rustc_front::print::pprust;
use rustc_front::hir;
-///////////////////////////////////////////////////////////////////////////
-// Type definitions
-
-struct ForeignTypes<'tcx> {
- /// Rust signature of the function
- fn_sig: ty::FnSig<'tcx>,
-
- /// Adapter object for handling native ABI rules (trust me, you
- /// don't want to know)
- fn_ty: cabi::FnType,
-
- /// LLVM types that will appear on the foreign function
- llsig: LlvmSignature,
-}
-
-struct LlvmSignature {
- // LLVM versions of the types of this function's arguments.
- llarg_tys: Vec<Type> ,
-
- // LLVM version of the type that this function returns. Note that
- // this *may not be* the declared return type of the foreign
- // function, because the foreign function may opt to return via an
- // out pointer.
- llret_ty: Type,
-
- /// True if there is a return value (not bottom, not unit)
- ret_def: bool,
-}
-
-
///////////////////////////////////////////////////////////////////////////
// Calls to external functions
-pub fn llvm_calling_convention(ccx: &CrateContext,
- abi: Abi) -> CallConv {
- use syntax::abi::Abi::*;
- match ccx.sess().target.target.adjust_abi(abi) {
- RustIntrinsic => {
- // Intrinsics are emitted at the call site
- ccx.sess().bug("asked to register intrinsic fn");
- }
- PlatformIntrinsic => {
- // Intrinsics are emitted at the call site
- ccx.sess().bug("asked to register platform intrinsic fn");
- }
-
- 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");
- }
-
- // It's the ABI's job to select this, not us.
- System => ccx.sess().bug("system abi should be selected elsewhere"),
-
- Stdcall => llvm::X86StdcallCallConv,
- Fastcall => llvm::X86FastcallCallConv,
- Vectorcall => llvm::X86_VectorCall,
- C => llvm::CCallConv,
- Win64 => llvm::X86_64_Win64,
-
- // These API constants ought to be more specific...
- Cdecl => llvm::CCallConv,
- Aapcs => llvm::CCallConv,
- }
-}
-
pub fn register_static(ccx: &CrateContext,
foreign_item: &hir::ForeignItem) -> ValueRef {
let ty = ccx.tcx().node_id_to_type(foreign_item.id);
let llty = type_of::type_of(ccx, ty);
- let ident = link_name(foreign_item);
+ let ident = link_name(foreign_item.name, &foreign_item.attrs);
let c = match attr::first_attr_value_str_by_name(&foreign_item.attrs,
"linkage") {
// If this is a static with a linkage specified, then we need to handle
};
let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
- let llsig = foreign_signature(ccx, &fn_sig, &passed_arg_tys[..]);
- let fn_type = cabi::compute_abi_info(ccx,
- &llsig.llarg_tys,
- llsig.llret_ty,
- llsig.ret_def);
- let arg_tys: &[cabi::ArgType] = &fn_type.arg_tys;
+ 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();
// 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_ty.is_indirect() {
- match fn_type.ret_ty.cast {
+ if fn_type.ret.is_indirect() {
+ match fn_type.ret.cast {
Some(ty) => {
let llcastedretptr =
BitCast(bcx, llretptr, ty.ptr_to());
}
let mut offset = 0;
- for (i, arg_ty) in arg_tys.iter().enumerate() {
+ for (i, arg_ty) in fn_type.args.iter().enumerate() {
let mut llarg_rust = llargs_rust[i + offset];
if arg_ty.is_ignore() {
llargs_foreign.push(llarg_foreign);
}
- let cc = llvm_calling_convention(ccx, fn_abi);
-
// 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 mut attrs = llvm::AttrBuilder::new();
// Add attributes that are always applicable, independent of the concrete foreign ABI
- if fn_type.ret_ty.is_indirect() {
- let llret_sz = machine::llsize_of_real(ccx, fn_type.ret_ty.ty);
+ if fn_type.ret.is_indirect() {
+ let llret_sz = machine::llsize_of_real(ccx, fn_type.ret.ty);
// The outptr can be noalias and nocapture because it's entirely
// invisible to the program. We also know it's nonnull as well
};
// Add attributes that depend on the concrete foreign ABI
- let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 };
- match fn_type.ret_ty.attr {
+ let mut arg_idx = if fn_type.ret.is_indirect() { 1 } else { 0 };
+ match fn_type.ret.attr {
Some(attr) => { attrs.arg(arg_idx, attr); },
_ => ()
}
arg_idx += 1;
- for arg_ty in &fn_type.arg_tys {
+ for arg_ty in &fn_type.args {
if arg_ty.is_ignore() {
continue;
}
let llforeign_retval = CallWithConv(bcx,
llfn,
&llargs_foreign[..],
- cc,
+ fn_type.cconv,
Some(attrs),
call_debug_loc);
// 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 llsig.ret_def && !fn_type.ret_ty.is_indirect() {
- let llrust_ret_ty = llsig.llret_ty;
- let llforeign_ret_ty = match fn_type.ret_ty.cast {
+ if fn_type.ret.ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
+ let llrust_ret_ty = fn_type.ret.ty;
+ let llforeign_ret_ty = match fn_type.ret.cast {
Some(ty) => ty,
- None => fn_type.ret_ty.ty
+ None => fn_type.ret.ty
};
debug!("llretptr={:?}", Value(llretptr));
let fnty = ccx.tcx().node_id_to_type(id);
let mty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fnty);
- let tys = foreign_types_for_fn_ty(ccx, mty);
+ let (fn_abi, fn_sig) = match mty.sty {
+ ty::TyFnDef(_, _, ref fn_ty) => (fn_ty.abi, &fn_ty.sig),
+ _ => ccx.sess().bug("trans_rust_fn_with_foreign_abi 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 fn_ty = FnType::new(ccx, fn_abi, &fn_sig, &[]);
unsafe { // unsafe because we call LLVM operations
// Build up the Rust function (`foo0` above).
let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
// Build up the foreign wrapper (`foo` above).
- return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty);
+ return build_wrap_fn(ccx, llrustfn, llwrapfn, &fn_sig, &fn_ty, mty);
}
fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let t = monomorphize::apply_param_substs(tcx, param_substs, &t);
let path =
- tcx.map.def_path_from_id(id)
+ tcx.map.def_path(tcx.map.local_def_id(id))
.into_iter()
.map(|e| e.data.as_interned_str())
.chain(once(special_idents::clownshoe_abi.name.as_str()));
// Compute the type that the function would have if it were just a
// normal Rust function. This will be the type of the wrappee fn.
- match t.sty {
- ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f)=> {
+ let rust_fn_ty = match t.sty {
+ ty::TyFnDef(_, _, ref f) => {
assert!(f.abi != Abi::Rust);
assert!(f.abi != Abi::RustIntrinsic);
assert!(f.abi != Abi::PlatformIntrinsic);
+ tcx.mk_fn_ptr(ty::BareFnTy {
+ unsafety: f.unsafety,
+ abi: Abi::Rust,
+ sig: f.sig.clone()
+ })
}
_ => {
- ccx.sess().bug(&format!("build_rust_fn: extern fn {} has ty {:?}, \
- expected a bare fn ty",
- ccx.tcx().map.path_to_string(id),
- t));
+ unreachable!("build_rust_fn: extern fn {} has ty {:?}, \
+ expected a fn item type",
+ tcx.map.path_to_string(id), t);
}
};
- debug!("build_rust_fn: path={} id={} t={:?}",
+ debug!("build_rust_fn: path={} id={} ty={:?}",
ccx.tcx().map.path_to_string(id),
- id, t);
+ id, rust_fn_ty);
let llfn = declare::define_internal_rust_fn(ccx, &ps, t);
attributes::from_fn_attrs(ccx, attrs, llfn);
unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llrustfn: ValueRef,
llwrapfn: ValueRef,
- tys: &ForeignTypes<'tcx>,
+ fn_sig: &ty::FnSig<'tcx>,
+ fn_ty: &FnType,
t: Ty<'tcx>) {
let _icx = push_ctxt(
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
// If there is an out pointer on the foreign function
let foreign_outptr = {
- if tys.fn_ty.ret_ty.is_indirect() {
+ if fn_ty.ret.is_indirect() {
Some(get_param(llwrapfn, next_foreign_arg(false)))
} else {
None
let rustfn_ty = Type::from_ref(llvm::LLVMTypeOf(llrustfn)).element_type();
let mut rust_param_tys = rustfn_ty.func_params().into_iter();
// Push Rust return pointer, using null if it will be unused.
- let rust_uses_outptr = match tys.fn_sig.output {
+ let rust_uses_outptr = match fn_sig.output {
ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty),
ty::FnDiverging => false
};
return_ty={:?}",
Value(slot),
llrust_ret_ty,
- tys.fn_sig.output);
+ fn_sig.output);
llrust_args.push(slot);
return_alloca = Some(slot);
}
// Build up the arguments to the call to the rust function.
// Careful to adapt for cases where the native convention uses
// a pointer and Rust does not or vice versa.
- for i in 0..tys.fn_sig.inputs.len() {
- let rust_ty = tys.fn_sig.inputs[i];
+ for i in 0..fn_sig.inputs.len() {
+ let rust_ty = fn_sig.inputs[i];
let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
let llty = rust_param_tys.next().expect("Not enough parameter types!");
let llrust_ty = if rust_indirect {
} else {
llty
};
- let llforeign_arg_ty = tys.fn_ty.arg_tys[i];
+ let llforeign_arg_ty = fn_ty.args[i];
let foreign_indirect = llforeign_arg_ty.is_indirect();
if llforeign_arg_ty.is_ignore() {
None, Some(attributes));
// Get the return value where the foreign fn expects it.
- let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast {
+ let llforeign_ret_ty = match fn_ty.ret.cast {
Some(ty) => ty,
- None => tys.fn_ty.ret_ty.ty
+ None => fn_ty.ret.ty
};
match foreign_outptr {
- None if !tys.llsig.ret_def => {
+ None if fn_ty.ret.ty == Type::void(ccx) => {
// Function returns `()` or `bot`, which in Rust is the LLVM
// type "{}" but in foreign ABIs is "Void".
builder.ret_void();
}
}
}
-
-/// The ForeignSignature is the LLVM types of the arguments/return type of a function. Note that
-/// these LLVM types are not quite the same as the LLVM types would be for a native Rust function
-/// because foreign functions just plain ignore modes. They also don't pass aggregate values by
-/// pointer like we do.
-fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- fn_sig: &ty::FnSig<'tcx>,
- arg_tys: &[Ty<'tcx>])
- -> LlvmSignature {
- let llarg_tys = arg_tys.iter().map(|&arg| foreign_arg_type_of(ccx, arg)).collect();
- let (llret_ty, ret_def) = match fn_sig.output {
- ty::FnConverging(ret_ty) =>
- (type_of::foreign_arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
- ty::FnDiverging =>
- (Type::nil(ccx), false)
- };
- LlvmSignature {
- llarg_tys: llarg_tys,
- llret_ty: llret_ty,
- ret_def: ret_def
- }
-}
-
-fn foreign_types_for_fn_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- ty: Ty<'tcx>) -> ForeignTypes<'tcx> {
- let fn_sig = match ty.sty {
- ty::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(ref fn_ty) => &fn_ty.sig,
- _ => ccx.sess().bug("foreign_types_for_fn_ty 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 llsig = foreign_signature(ccx, &fn_sig, &fn_sig.inputs);
- let fn_ty = cabi::compute_abi_info(ccx,
- &llsig.llarg_tys,
- llsig.llret_ty,
- llsig.ret_def);
- debug!("foreign_types_for_fn_ty(\
- ty={:?}, \
- llsig={:?} -> {:?}, \
- fn_ty={:?} -> {:?}, \
- ret_def={}",
- ty,
- llsig.llarg_tys,
- llsig.llret_ty,
- fn_ty.arg_tys,
- fn_ty.ret_ty,
- llsig.ret_def);
-
- ForeignTypes {
- fn_sig: fn_sig,
- llsig: llsig,
- fn_ty: fn_ty
- }
-}
-
-fn lltype_for_fn_from_foreign_types(ccx: &CrateContext, tys: &ForeignTypes) -> Type {
- let mut llargument_tys = Vec::new();
-
- let ret_ty = tys.fn_ty.ret_ty;
- let llreturn_ty = if ret_ty.is_indirect() {
- llargument_tys.push(ret_ty.ty.ptr_to());
- Type::void(ccx)
- } else {
- match ret_ty.cast {
- Some(ty) => ty,
- None => ret_ty.ty
- }
- };
-
- for &arg_ty in &tys.fn_ty.arg_tys {
- if arg_ty.is_ignore() {
- continue;
- }
- // add padding
- match arg_ty.pad {
- Some(ty) => llargument_tys.push(ty),
- None => ()
}
-
- let llarg_ty = if arg_ty.is_indirect() {
- arg_ty.ty.ptr_to()
- } else {
- match arg_ty.cast {
- Some(ty) => ty,
- None => arg_ty.ty
- }
- };
-
- llargument_tys.push(llarg_ty);
- }
-
- if tys.fn_sig.variadic {
- Type::variadic_func(&llargument_tys, &llreturn_ty)
- } else {
- Type::func(&llargument_tys[..], &llreturn_ty)
- }
-}
-
-pub fn lltype_for_foreign_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- ty: Ty<'tcx>) -> Type {
- lltype_for_fn_from_foreign_types(ccx, &foreign_types_for_fn_ty(ccx, ty))
-}
-
-fn add_argument_attributes(tys: &ForeignTypes,
- llfn: ValueRef) {
- let mut i = if tys.fn_ty.ret_ty.is_indirect() {
- 1
- } else {
- 0
- };
-
- match tys.fn_ty.ret_ty.attr {
- Some(attr) => unsafe {
- llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64);
- },
- None => {}
- }
-
- i += 1;
-
- for &arg_ty in &tys.fn_ty.arg_tys {
- if arg_ty.is_ignore() {
- continue;
- }
- // skip padding
- if arg_ty.pad.is_some() { i += 1; }
-
- match arg_ty.attr {
- Some(attr) => unsafe {
- llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64);
- },
- None => ()
- }
-
- i += 1;
}
}
use middle::infer;
use middle::subst;
use trans::adt;
+use trans::cabi::FnType;
use trans::common::*;
-use trans::foreign;
use trans::machine;
use middle::ty::{self, Ty, TypeFoldable};
llsizingty
}
-pub fn foreign_arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
- if t.is_bool() {
- Type::i1(cx)
- } else {
- type_of(cx, t)
- }
-}
-
pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
if t.is_bool() {
Type::i1(cx)
ty::TyFnDef(..) => Type::nil(cx),
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 {
- let sig = cx.tcx().erase_late_bound_regions(&f.sig);
- let sig = infer::normalize_associated_type(cx.tcx(), &sig);
type_of_rust_fn(cx, None, &sig, f.abi).ptr_to()
} else {
- foreign::lltype_for_foreign_fn(cx, t).ptr_to()
+ FnType::new(cx, f.abi, &sig, &[]).to_llvm(cx).ptr_to()
}
}
ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx),