trans: Revamp and empower cabi::FnType.
This commit is contained in:
parent
9221b9118b
commit
c6d214bdeb
@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
pub use self::ArgKind::*;
|
pub use self::ArgKind::*;
|
||||||
|
|
||||||
use llvm::Attribute;
|
use llvm::{self, AttrHelper, ValueRef};
|
||||||
use std::option;
|
use trans::attributes;
|
||||||
|
use trans::common::return_type_is_void;
|
||||||
use trans::context::CrateContext;
|
use trans::context::CrateContext;
|
||||||
use trans::cabi_x86;
|
use trans::cabi_x86;
|
||||||
use trans::cabi_x86_64;
|
use trans::cabi_x86_64;
|
||||||
@ -23,6 +24,11 @@ use trans::cabi_powerpc64;
|
|||||||
use trans::cabi_mips;
|
use trans::cabi_mips;
|
||||||
use trans::cabi_asmjs;
|
use trans::cabi_asmjs;
|
||||||
use trans::type_::Type;
|
use trans::type_::Type;
|
||||||
|
use trans::type_of;
|
||||||
|
|
||||||
|
use middle::ty::{self, Ty};
|
||||||
|
|
||||||
|
use syntax::abi::Abi;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum ArgKind {
|
pub enum ArgKind {
|
||||||
@ -45,17 +51,17 @@ pub struct ArgType {
|
|||||||
/// Original LLVM type
|
/// Original LLVM type
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
/// Coerced LLVM Type
|
/// Coerced LLVM Type
|
||||||
pub cast: option::Option<Type>,
|
pub cast: Option<Type>,
|
||||||
/// Dummy argument, which is emitted before the real argument
|
/// Dummy argument, which is emitted before the real argument
|
||||||
pub pad: option::Option<Type>,
|
pub pad: Option<Type>,
|
||||||
/// LLVM attribute of argument
|
/// LLVM attribute of argument
|
||||||
pub attr: option::Option<Attribute>
|
pub attr: Option<llvm::Attribute>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArgType {
|
impl ArgType {
|
||||||
pub fn direct(ty: Type, cast: option::Option<Type>,
|
pub fn direct(ty: Type, cast: Option<Type>,
|
||||||
pad: option::Option<Type>,
|
pad: Option<Type>,
|
||||||
attr: option::Option<Attribute>) -> ArgType {
|
attr: Option<llvm::Attribute>) -> ArgType {
|
||||||
ArgType {
|
ArgType {
|
||||||
kind: Direct,
|
kind: Direct,
|
||||||
ty: ty,
|
ty: ty,
|
||||||
@ -65,12 +71,12 @@ impl ArgType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indirect(ty: Type, attr: option::Option<Attribute>) -> ArgType {
|
pub fn indirect(ty: Type, attr: Option<llvm::Attribute>) -> ArgType {
|
||||||
ArgType {
|
ArgType {
|
||||||
kind: Indirect,
|
kind: Indirect,
|
||||||
ty: ty,
|
ty: ty,
|
||||||
cast: option::Option::None,
|
cast: Option::None,
|
||||||
pad: option::Option::None,
|
pad: Option::None,
|
||||||
attr: attr
|
attr: attr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,6 +100,14 @@ impl ArgType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/// Metadata describing how the arguments to a native function
|
||||||
/// should be passed in order to respect the native ABI.
|
/// should be passed in order to respect the native ABI.
|
||||||
///
|
///
|
||||||
@ -101,37 +115,160 @@ impl ArgType {
|
|||||||
/// comments are reverse-engineered and may be inaccurate. -NDM
|
/// comments are reverse-engineered and may be inaccurate. -NDM
|
||||||
pub struct FnType {
|
pub struct FnType {
|
||||||
/// The LLVM types of each argument.
|
/// The LLVM types of each argument.
|
||||||
pub arg_tys: Vec<ArgType> ,
|
pub args: Vec<ArgType>,
|
||||||
|
|
||||||
/// LLVM return type.
|
/// LLVM return type.
|
||||||
pub ret_ty: ArgType,
|
pub ret: ArgType,
|
||||||
|
|
||||||
|
pub variadic: bool,
|
||||||
|
|
||||||
|
pub cconv: llvm::CallConv
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
impl FnType {
|
||||||
atys: &[Type],
|
pub fn new<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
rty: Type,
|
abi: Abi,
|
||||||
ret_def: bool) -> FnType {
|
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[..] {
|
match &ccx.sess().target.target.arch[..] {
|
||||||
"x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def),
|
"x86" => cabi_x86::compute_abi_info(ccx, &mut fty),
|
||||||
"x86_64" => if ccx.sess().target.target.options.is_like_windows {
|
"x86_64" => if ccx.sess().target.target.options.is_like_windows {
|
||||||
cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def)
|
cabi_x86_win64::compute_abi_info(ccx, &mut fty);
|
||||||
} else {
|
} else {
|
||||||
cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def)
|
cabi_x86_64::compute_abi_info(ccx, &mut fty);
|
||||||
},
|
},
|
||||||
"aarch64" => cabi_aarch64::compute_abi_info(ccx, atys, rty, ret_def),
|
"aarch64" => cabi_aarch64::compute_abi_info(ccx, &mut fty),
|
||||||
"arm" => {
|
"arm" => {
|
||||||
let flavor = if ccx.sess().target.target.target_os == "ios" {
|
let flavor = if ccx.sess().target.target.target_os == "ios" {
|
||||||
cabi_arm::Flavor::Ios
|
cabi_arm::Flavor::Ios
|
||||||
} else {
|
} else {
|
||||||
cabi_arm::Flavor::General
|
cabi_arm::Flavor::General
|
||||||
};
|
};
|
||||||
cabi_arm::compute_abi_info(ccx, atys, rty, ret_def, flavor)
|
cabi_arm::compute_abi_info(ccx, &mut fty, flavor);
|
||||||
},
|
},
|
||||||
"mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
|
"mips" => cabi_mips::compute_abi_info(ccx, &mut fty),
|
||||||
"powerpc" => cabi_powerpc::compute_abi_info(ccx, atys, rty, ret_def),
|
"powerpc" => cabi_powerpc::compute_abi_info(ccx, &mut fty),
|
||||||
"powerpc64" => cabi_powerpc64::compute_abi_info(ccx, atys, rty, ret_def),
|
"powerpc64" => cabi_powerpc64::compute_abi_info(ccx, &mut fty),
|
||||||
"asmjs" => cabi_asmjs::compute_abi_info(ccx, atys, rty, ret_def),
|
"asmjs" => cabi_asmjs::compute_abi_info(ccx, &mut fty),
|
||||||
a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)
|
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 {
|
||||||
|
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 {
|
||||||
|
arg.cast.unwrap_or(arg.ty)
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,24 +228,12 @@ fn is_reg_ty(ty: Type) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
rty: Type,
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_ty = if ret_def {
|
for arg in &mut fty.args {
|
||||||
classify_ret_ty(ccx, rty)
|
*arg = classify_arg_ty(ccx, arg.ty);
|
||||||
} else {
|
}
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -174,30 +174,17 @@ fn is_reg_ty(ty: Type) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) {
|
||||||
atys: &[Type],
|
|
||||||
rty: Type,
|
|
||||||
ret_def: bool,
|
|
||||||
flavor: Flavor) -> FnType {
|
|
||||||
let align_fn = match flavor {
|
let align_fn = match flavor {
|
||||||
Flavor::General => general_ty_align as TyAlignFn,
|
Flavor::General => general_ty_align as TyAlignFn,
|
||||||
Flavor::Ios => ios_ty_align as TyAlignFn,
|
Flavor::Ios => ios_ty_align as TyAlignFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut arg_tys = Vec::new();
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
for &aty in atys {
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty, align_fn);
|
||||||
let ty = classify_arg_ty(ccx, aty, align_fn);
|
|
||||||
arg_tys.push(ty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_ty = if ret_def {
|
for arg in &mut fty.args {
|
||||||
classify_ret_ty(ccx, rty, align_fn)
|
*arg = classify_arg_ty(ccx, arg.ty, align_fn);
|
||||||
} else {
|
}
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -49,24 +49,12 @@ fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
rty: Type,
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_ty = if ret_def {
|
for arg in &mut fty.args {
|
||||||
classify_ret_ty(ccx, rty)
|
*arg = classify_arg_ty(ccx, arg.ty);
|
||||||
} else {
|
}
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -161,27 +161,13 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
|
|||||||
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
|
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
rty: Type,
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty);
|
||||||
ret_def: bool) -> FnType {
|
}
|
||||||
let ret_ty = if ret_def {
|
|
||||||
classify_ret_ty(ccx, rty)
|
let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
|
||||||
} else {
|
for arg in &mut fty.args {
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
*arg = classify_arg_ty(ccx, arg.ty, &mut offset);
|
||||||
};
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -156,27 +156,13 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
|
|||||||
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
|
Type::struct_(ccx, &coerce_to_int(ccx, size), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
rty: Type,
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty);
|
||||||
ret_def: bool) -> FnType {
|
}
|
||||||
let ret_ty = if ret_def {
|
|
||||||
classify_ret_ty(ccx, rty)
|
let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };
|
||||||
} else {
|
for arg in &mut fty.args {
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
*arg = classify_arg_ty(ccx, arg.ty, &mut offset);
|
||||||
};
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -236,24 +236,12 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
|
|||||||
Type::struct_(ccx, &coerce_to_long(ccx, size), false)
|
Type::struct_(ccx, &coerce_to_long(ccx, size), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
rty: Type,
|
fty.ret = classify_ret_ty(ccx, fty.ret.ty);
|
||||||
ret_def: bool) -> FnType {
|
}
|
||||||
let ret_ty = if ret_def {
|
|
||||||
classify_ret_ty(ccx, rty)
|
for arg in &mut fty.args {
|
||||||
} else {
|
*arg = classify_arg_ty(ccx, arg.ty);
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -8,24 +8,14 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use self::Strategy::*;
|
|
||||||
use llvm::*;
|
use llvm::*;
|
||||||
use trans::cabi::{ArgType, FnType};
|
use trans::cabi::{ArgType, FnType};
|
||||||
use trans::type_::Type;
|
use trans::type_::Type;
|
||||||
use super::common::*;
|
use super::common::*;
|
||||||
use super::machine::*;
|
use super::machine::*;
|
||||||
|
|
||||||
enum Strategy { RetValue(Type), RetPointer }
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
if fty.ret.ty.kind() == Struct {
|
||||||
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 {
|
|
||||||
// Returning a structure. Most often, this will use
|
// Returning a structure. Most often, this will use
|
||||||
// a hidden first argument. On some platforms, though,
|
// a hidden first argument. On some platforms, though,
|
||||||
// small structs are returned as integers.
|
// small structs are returned as integers.
|
||||||
@ -33,53 +23,33 @@ pub fn compute_abi_info(ccx: &CrateContext,
|
|||||||
// Some links:
|
// Some links:
|
||||||
// http://www.angelcode.com/dev/callconv/callconv.html
|
// http://www.angelcode.com/dev/callconv/callconv.html
|
||||||
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
|
// 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 t = &ccx.sess().target.target;
|
||||||
let strategy = if t.options.is_like_osx || t.options.is_like_windows {
|
if t.options.is_like_osx || t.options.is_like_windows {
|
||||||
match llsize_of_alloc(ccx, rty) {
|
match llsize_of_alloc(ccx, fty.ret.ty) {
|
||||||
1 => RetValue(Type::i8(ccx)),
|
1 => fty.ret.cast = Some(Type::i8(ccx)),
|
||||||
2 => RetValue(Type::i16(ccx)),
|
2 => fty.ret.cast = Some(Type::i16(ccx)),
|
||||||
4 => RetValue(Type::i32(ccx)),
|
4 => fty.ret.cast = Some(Type::i32(ccx)),
|
||||||
8 => RetValue(Type::i64(ccx)),
|
8 => fty.ret.cast = Some(Type::i64(ccx)),
|
||||||
_ => RetPointer
|
_ => fty.ret = indirect
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RetPointer
|
fty.ret = indirect;
|
||||||
};
|
}
|
||||||
|
} else if fty.ret.ty == Type::i1(ccx) {
|
||||||
|
fty.ret.attr = Some(Attribute::ZExt);
|
||||||
|
}
|
||||||
|
|
||||||
match strategy {
|
for arg in &mut fty.args {
|
||||||
RetValue(t) => {
|
if arg.ty.kind() == Struct {
|
||||||
ret_ty = ArgType::direct(rty, Some(t), None, None);
|
*arg = if llsize_of_alloc(ccx, arg.ty) == 0 {
|
||||||
}
|
ArgType::ignore(arg.ty)
|
||||||
RetPointer => {
|
|
||||||
ret_ty = ArgType::indirect(rty, Some(Attribute::StructRet));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
|
ArgType::indirect(arg.ty, Some(Attribute::ByVal))
|
||||||
ret_ty = ArgType::direct(rty, None, None, attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
} else if arg.ty == Type::i1(ccx) {
|
||||||
|
arg.attr = Some(Attribute::ZExt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -383,10 +383,7 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
|
||||||
rty: Type,
|
|
||||||
ret_def: bool) -> FnType {
|
|
||||||
fn x86_64_ty<F>(ccx: &CrateContext,
|
fn x86_64_ty<F>(ccx: &CrateContext,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
is_mem_cls: F,
|
is_mem_cls: F,
|
||||||
@ -413,8 +410,8 @@ pub fn compute_abi_info(ccx: &CrateContext,
|
|||||||
let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
|
let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
|
||||||
let mut sse_regs = 8; // XMM0-7
|
let mut sse_regs = 8; // XMM0-7
|
||||||
|
|
||||||
let ret_ty = if ret_def {
|
if fty.ret.ty != Type::void(ccx) {
|
||||||
x86_64_ty(ccx, rty, |cls| {
|
fty.ret = x86_64_ty(ccx, fty.ret.ty, |cls| {
|
||||||
if cls.is_ret_bysret() {
|
if cls.is_ret_bysret() {
|
||||||
// `sret` parameter thus one less register available
|
// `sret` parameter thus one less register available
|
||||||
int_regs -= 1;
|
int_regs -= 1;
|
||||||
@ -422,14 +419,11 @@ pub fn compute_abi_info(ccx: &CrateContext,
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}, Attribute::StructRet)
|
}, Attribute::StructRet);
|
||||||
} else {
|
}
|
||||||
ArgType::direct(Type::void(ccx), None, None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut arg_tys = Vec::new();
|
for arg in &mut fty.args {
|
||||||
for t in atys {
|
*arg = x86_64_ty(ccx, arg.ty, |cls| {
|
||||||
let ty = x86_64_ty(ccx, *t, |cls| {
|
|
||||||
let needed_int = cls.iter().filter(|&&c| c == Int).count() as isize;
|
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 needed_sse = cls.iter().filter(|c| c.is_sse()).count() as isize;
|
||||||
let in_mem = cls.is_pass_byval() ||
|
let in_mem = cls.is_pass_byval() ||
|
||||||
@ -445,20 +439,14 @@ pub fn compute_abi_info(ccx: &CrateContext,
|
|||||||
}
|
}
|
||||||
in_mem
|
in_mem
|
||||||
}, Attribute::ByVal);
|
}, Attribute::ByVal);
|
||||||
arg_tys.push(ty);
|
|
||||||
|
|
||||||
// An integer, pointer, double or float parameter
|
// An integer, pointer, double or float parameter
|
||||||
// thus the above closure passed to `x86_64_ty` won't
|
// thus the above closure passed to `x86_64_ty` won't
|
||||||
// get called.
|
// get called.
|
||||||
if t.kind() == Integer || t.kind() == Pointer {
|
match arg.ty.kind() {
|
||||||
int_regs -= 1;
|
Integer | Pointer => int_regs -= 1,
|
||||||
} else if t.kind() == Double || t.kind() == Float {
|
Double | Float => sse_regs -= 1,
|
||||||
sse_regs -= 1;
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FnType {
|
|
||||||
arg_tys: arg_tys,
|
|
||||||
ret_ty: ret_ty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -16,49 +16,23 @@ use trans::type_::Type;
|
|||||||
|
|
||||||
// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
|
// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
|
||||||
|
|
||||||
pub fn compute_abi_info(ccx: &CrateContext,
|
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
|
||||||
atys: &[Type],
|
let fixup = |a: &mut ArgType, indirect_attr| {
|
||||||
rty: Type,
|
if a.ty.kind() == Struct {
|
||||||
ret_def: bool) -> FnType {
|
match llsize_of_alloc(ccx, a.ty) {
|
||||||
let mut arg_tys = Vec::new();
|
1 => a.cast = Some(Type::i8(ccx)),
|
||||||
|
2 => a.cast = Some(Type::i16(ccx)),
|
||||||
let ret_ty;
|
4 => a.cast = Some(Type::i32(ccx)),
|
||||||
if !ret_def {
|
8 => a.cast = Some(Type::i64(ccx)),
|
||||||
ret_ty = ArgType::direct(Type::void(ccx), None, None, None);
|
_ => *a = ArgType::indirect(a.ty, indirect_attr)
|
||||||
} 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);
|
|
||||||
}
|
}
|
||||||
|
} else if a.ty == Type::i1(ccx) {
|
||||||
for &t in atys {
|
a.attr = Some(Attribute::ZExt);
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
arg_tys.push(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FnType {
|
fixup(&mut fty.ret, Some(Attribute::StructRet));
|
||||||
arg_tys: arg_tys,
|
for arg in &mut fty.args {
|
||||||
ret_ty: ret_ty,
|
fixup(arg, None);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ use trans::attributes;
|
|||||||
use trans::base::{llvm_linkage_by_name, push_ctxt};
|
use trans::base::{llvm_linkage_by_name, push_ctxt};
|
||||||
use trans::base;
|
use trans::base;
|
||||||
use trans::build::*;
|
use trans::build::*;
|
||||||
use trans::cabi;
|
use trans::cabi::FnType;
|
||||||
use trans::common::*;
|
use trans::common::*;
|
||||||
use trans::debuginfo::DebugLoc;
|
use trans::debuginfo::DebugLoc;
|
||||||
use trans::declare;
|
use trans::declare;
|
||||||
@ -45,83 +45,15 @@ use syntax::attr::AttrMetaMethods;
|
|||||||
use rustc_front::print::pprust;
|
use rustc_front::print::pprust;
|
||||||
use rustc_front::hir;
|
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
|
// 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,
|
pub fn register_static(ccx: &CrateContext,
|
||||||
foreign_item: &hir::ForeignItem) -> ValueRef {
|
foreign_item: &hir::ForeignItem) -> ValueRef {
|
||||||
let ty = ccx.tcx().node_id_to_type(foreign_item.id);
|
let ty = ccx.tcx().node_id_to_type(foreign_item.id);
|
||||||
let llty = type_of::type_of(ccx, ty);
|
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,
|
let c = match attr::first_attr_value_str_by_name(&foreign_item.attrs,
|
||||||
"linkage") {
|
"linkage") {
|
||||||
// If this is a static with a linkage specified, then we need to handle
|
// If this is a static with a linkage specified, then we need to handle
|
||||||
@ -265,13 +197,9 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
};
|
};
|
||||||
let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
|
let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
|
||||||
let fn_sig = infer::normalize_associated_type(ccx.tcx(), &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();
|
let mut llargs_foreign = Vec::new();
|
||||||
|
|
||||||
@ -279,8 +207,8 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
// pointer that Rust gave us. Sometimes we have to bitcast
|
// pointer that Rust gave us. Sometimes we have to bitcast
|
||||||
// because foreign fns return slightly different (but equivalent)
|
// because foreign fns return slightly different (but equivalent)
|
||||||
// views on the same type (e.g., i64 in place of {i32,i32}).
|
// views on the same type (e.g., i64 in place of {i32,i32}).
|
||||||
if fn_type.ret_ty.is_indirect() {
|
if fn_type.ret.is_indirect() {
|
||||||
match fn_type.ret_ty.cast {
|
match fn_type.ret.cast {
|
||||||
Some(ty) => {
|
Some(ty) => {
|
||||||
let llcastedretptr =
|
let llcastedretptr =
|
||||||
BitCast(bcx, llretptr, ty.ptr_to());
|
BitCast(bcx, llretptr, ty.ptr_to());
|
||||||
@ -293,7 +221,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut offset = 0;
|
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];
|
let mut llarg_rust = llargs_rust[i + offset];
|
||||||
|
|
||||||
if arg_ty.is_ignore() {
|
if arg_ty.is_ignore() {
|
||||||
@ -359,15 +287,13 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
llargs_foreign.push(llarg_foreign);
|
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
|
// A function pointer is called without the declaration available, so we have to apply
|
||||||
// any attributes with ABI implications directly to the call instruction.
|
// any attributes with ABI implications directly to the call instruction.
|
||||||
let mut attrs = llvm::AttrBuilder::new();
|
let mut attrs = llvm::AttrBuilder::new();
|
||||||
|
|
||||||
// Add attributes that are always applicable, independent of the concrete foreign ABI
|
// Add attributes that are always applicable, independent of the concrete foreign ABI
|
||||||
if fn_type.ret_ty.is_indirect() {
|
if fn_type.ret.is_indirect() {
|
||||||
let llret_sz = machine::llsize_of_real(ccx, fn_type.ret_ty.ty);
|
let llret_sz = machine::llsize_of_real(ccx, fn_type.ret.ty);
|
||||||
|
|
||||||
// The outptr can be noalias and nocapture because it's entirely
|
// The outptr can be noalias and nocapture because it's entirely
|
||||||
// invisible to the program. We also know it's nonnull as well
|
// invisible to the program. We also know it's nonnull as well
|
||||||
@ -378,14 +304,14 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add attributes that depend on the concrete foreign ABI
|
// Add attributes that depend on the concrete foreign ABI
|
||||||
let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 };
|
let mut arg_idx = if fn_type.ret.is_indirect() { 1 } else { 0 };
|
||||||
match fn_type.ret_ty.attr {
|
match fn_type.ret.attr {
|
||||||
Some(attr) => { attrs.arg(arg_idx, attr); },
|
Some(attr) => { attrs.arg(arg_idx, attr); },
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
arg_idx += 1;
|
arg_idx += 1;
|
||||||
for arg_ty in &fn_type.arg_tys {
|
for arg_ty in &fn_type.args {
|
||||||
if arg_ty.is_ignore() {
|
if arg_ty.is_ignore() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -402,7 +328,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
let llforeign_retval = CallWithConv(bcx,
|
let llforeign_retval = CallWithConv(bcx,
|
||||||
llfn,
|
llfn,
|
||||||
&llargs_foreign[..],
|
&llargs_foreign[..],
|
||||||
cc,
|
fn_type.cconv,
|
||||||
Some(attrs),
|
Some(attrs),
|
||||||
call_debug_loc);
|
call_debug_loc);
|
||||||
|
|
||||||
@ -411,11 +337,11 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
// type to match because some ABIs will use a different type than
|
// 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
|
// the Rust type. e.g., a {u32,u32} struct could be returned as
|
||||||
// u64.
|
// u64.
|
||||||
if llsig.ret_def && !fn_type.ret_ty.is_indirect() {
|
if fn_type.ret.ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
|
||||||
let llrust_ret_ty = llsig.llret_ty;
|
let llrust_ret_ty = fn_type.ret.ty;
|
||||||
let llforeign_ret_ty = match fn_type.ret_ty.cast {
|
let llforeign_ret_ty = match fn_type.ret.cast {
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
None => fn_type.ret_ty.ty
|
None => fn_type.ret.ty
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("llretptr={:?}", Value(llretptr));
|
debug!("llretptr={:?}", Value(llretptr));
|
||||||
@ -543,14 +469,20 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
|
|
||||||
let fnty = ccx.tcx().node_id_to_type(id);
|
let fnty = ccx.tcx().node_id_to_type(id);
|
||||||
let mty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fnty);
|
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
|
unsafe { // unsafe because we call LLVM operations
|
||||||
// Build up the Rust function (`foo0` above).
|
// Build up the Rust function (`foo0` above).
|
||||||
let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
|
let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
|
||||||
|
|
||||||
// Build up the foreign wrapper (`foo` above).
|
// 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>,
|
fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
@ -568,7 +500,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
let t = monomorphize::apply_param_substs(tcx, param_substs, &t);
|
let t = monomorphize::apply_param_substs(tcx, param_substs, &t);
|
||||||
|
|
||||||
let path =
|
let path =
|
||||||
tcx.map.def_path_from_id(id)
|
tcx.map.def_path(tcx.map.local_def_id(id))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| e.data.as_interned_str())
|
.map(|e| e.data.as_interned_str())
|
||||||
.chain(once(special_idents::clownshoe_abi.name.as_str()));
|
.chain(once(special_idents::clownshoe_abi.name.as_str()));
|
||||||
@ -576,23 +508,27 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
|
|
||||||
// Compute the type that the function would have if it were just a
|
// 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.
|
// normal Rust function. This will be the type of the wrappee fn.
|
||||||
match t.sty {
|
let rust_fn_ty = match t.sty {
|
||||||
ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f)=> {
|
ty::TyFnDef(_, _, ref f) => {
|
||||||
assert!(f.abi != Abi::Rust);
|
assert!(f.abi != Abi::Rust);
|
||||||
assert!(f.abi != Abi::RustIntrinsic);
|
assert!(f.abi != Abi::RustIntrinsic);
|
||||||
assert!(f.abi != Abi::PlatformIntrinsic);
|
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 {:?}, \
|
unreachable!("build_rust_fn: extern fn {} has ty {:?}, \
|
||||||
expected a bare fn ty",
|
expected a fn item type",
|
||||||
ccx.tcx().map.path_to_string(id),
|
tcx.map.path_to_string(id), t);
|
||||||
t));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("build_rust_fn: path={} id={} t={:?}",
|
debug!("build_rust_fn: path={} id={} ty={:?}",
|
||||||
ccx.tcx().map.path_to_string(id),
|
ccx.tcx().map.path_to_string(id),
|
||||||
id, t);
|
id, rust_fn_ty);
|
||||||
|
|
||||||
let llfn = declare::define_internal_rust_fn(ccx, &ps, t);
|
let llfn = declare::define_internal_rust_fn(ccx, &ps, t);
|
||||||
attributes::from_fn_attrs(ccx, attrs, llfn);
|
attributes::from_fn_attrs(ccx, attrs, llfn);
|
||||||
@ -603,7 +539,8 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
llrustfn: ValueRef,
|
llrustfn: ValueRef,
|
||||||
llwrapfn: ValueRef,
|
llwrapfn: ValueRef,
|
||||||
tys: &ForeignTypes<'tcx>,
|
fn_sig: &ty::FnSig<'tcx>,
|
||||||
|
fn_ty: &FnType,
|
||||||
t: Ty<'tcx>) {
|
t: Ty<'tcx>) {
|
||||||
let _icx = push_ctxt(
|
let _icx = push_ctxt(
|
||||||
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
|
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
|
||||||
@ -650,7 +587,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
|
|
||||||
// If there is an out pointer on the foreign function
|
// If there is an out pointer on the foreign function
|
||||||
let foreign_outptr = {
|
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)))
|
Some(get_param(llwrapfn, next_foreign_arg(false)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -660,7 +597,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
let rustfn_ty = Type::from_ref(llvm::LLVMTypeOf(llrustfn)).element_type();
|
let rustfn_ty = Type::from_ref(llvm::LLVMTypeOf(llrustfn)).element_type();
|
||||||
let mut rust_param_tys = rustfn_ty.func_params().into_iter();
|
let mut rust_param_tys = rustfn_ty.func_params().into_iter();
|
||||||
// Push Rust return pointer, using null if it will be unused.
|
// 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::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty),
|
||||||
ty::FnDiverging => false
|
ty::FnDiverging => false
|
||||||
};
|
};
|
||||||
@ -696,7 +633,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
return_ty={:?}",
|
return_ty={:?}",
|
||||||
Value(slot),
|
Value(slot),
|
||||||
llrust_ret_ty,
|
llrust_ret_ty,
|
||||||
tys.fn_sig.output);
|
fn_sig.output);
|
||||||
llrust_args.push(slot);
|
llrust_args.push(slot);
|
||||||
return_alloca = Some(slot);
|
return_alloca = Some(slot);
|
||||||
}
|
}
|
||||||
@ -711,8 +648,8 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
// Build up the arguments to the call to the rust function.
|
// Build up the arguments to the call to the rust function.
|
||||||
// Careful to adapt for cases where the native convention uses
|
// Careful to adapt for cases where the native convention uses
|
||||||
// a pointer and Rust does not or vice versa.
|
// a pointer and Rust does not or vice versa.
|
||||||
for i in 0..tys.fn_sig.inputs.len() {
|
for i in 0..fn_sig.inputs.len() {
|
||||||
let rust_ty = tys.fn_sig.inputs[i];
|
let rust_ty = fn_sig.inputs[i];
|
||||||
let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
|
let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
|
||||||
let llty = rust_param_tys.next().expect("Not enough parameter types!");
|
let llty = rust_param_tys.next().expect("Not enough parameter types!");
|
||||||
let llrust_ty = if rust_indirect {
|
let llrust_ty = if rust_indirect {
|
||||||
@ -720,7 +657,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
} else {
|
} else {
|
||||||
llty
|
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();
|
let foreign_indirect = llforeign_arg_ty.is_indirect();
|
||||||
|
|
||||||
if llforeign_arg_ty.is_ignore() {
|
if llforeign_arg_ty.is_ignore() {
|
||||||
@ -802,12 +739,12 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
None, Some(attributes));
|
None, Some(attributes));
|
||||||
|
|
||||||
// Get the return value where the foreign fn expects it.
|
// 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,
|
Some(ty) => ty,
|
||||||
None => tys.fn_ty.ret_ty.ty
|
None => fn_ty.ret.ty
|
||||||
};
|
};
|
||||||
match foreign_outptr {
|
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
|
// Function returns `()` or `bot`, which in Rust is the LLVM
|
||||||
// type "{}" but in foreign ABIs is "Void".
|
// type "{}" but in foreign ABIs is "Void".
|
||||||
builder.ret_void();
|
builder.ret_void();
|
||||||
@ -874,140 +811,6 @@ pub fn link_name(name: ast::Name, attrs: &[ast::Attribute]) -> InternedString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ use middle::def_id::DefId;
|
|||||||
use middle::infer;
|
use middle::infer;
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
use trans::adt;
|
use trans::adt;
|
||||||
|
use trans::cabi::FnType;
|
||||||
use trans::common::*;
|
use trans::common::*;
|
||||||
use trans::foreign;
|
|
||||||
use trans::machine;
|
use trans::machine;
|
||||||
use middle::ty::{self, Ty, TypeFoldable};
|
use middle::ty::{self, Ty, TypeFoldable};
|
||||||
|
|
||||||
@ -239,14 +239,6 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
|
|||||||
llsizingty
|
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 {
|
pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
|
||||||
if t.is_bool() {
|
if t.is_bool() {
|
||||||
Type::i1(cx)
|
Type::i1(cx)
|
||||||
@ -390,12 +382,12 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
|
|||||||
|
|
||||||
ty::TyFnDef(..) => Type::nil(cx),
|
ty::TyFnDef(..) => Type::nil(cx),
|
||||||
ty::TyFnPtr(f) => {
|
ty::TyFnPtr(f) => {
|
||||||
if f.abi == Abi::Rust || f.abi == Abi::RustCall {
|
|
||||||
let sig = cx.tcx().erase_late_bound_regions(&f.sig);
|
let sig = cx.tcx().erase_late_bound_regions(&f.sig);
|
||||||
let sig = infer::normalize_associated_type(cx.tcx(), &sig);
|
let sig = infer::normalize_associated_type(cx.tcx(), &sig);
|
||||||
|
if f.abi == Abi::Rust || f.abi == Abi::RustCall {
|
||||||
type_of_rust_fn(cx, None, &sig, f.abi).ptr_to()
|
type_of_rust_fn(cx, None, &sig, f.abi).ptr_to()
|
||||||
} else {
|
} 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),
|
ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx),
|
||||||
|
Loading…
Reference in New Issue
Block a user