From c6d214bdeb1bfc9664ad14085c94bf2cbc5af6ee Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Tue, 23 Feb 2016 21:55:19 +0200 Subject: [PATCH] trans: Revamp and empower cabi::FnType. --- src/librustc_trans/trans/cabi.rs | 211 ++++++++++++--- src/librustc_trans/trans/cabi_aarch64.rs | 24 +- src/librustc_trans/trans/cabi_arm.rs | 25 +- src/librustc_trans/trans/cabi_asmjs.rs | 24 +- src/librustc_trans/trans/cabi_mips.rs | 30 +-- src/librustc_trans/trans/cabi_powerpc.rs | 30 +-- src/librustc_trans/trans/cabi_powerpc64.rs | 26 +- src/librustc_trans/trans/cabi_x86.rs | 76 ++---- src/librustc_trans/trans/cabi_x86_64.rs | 34 +-- src/librustc_trans/trans/cabi_x86_win64.rs | 60 ++--- src/librustc_trans/trans/foreign.rs | 297 ++++----------------- src/librustc_trans/trans/type_of.rs | 16 +- 12 files changed, 320 insertions(+), 533 deletions(-) diff --git a/src/librustc_trans/trans/cabi.rs b/src/librustc_trans/trans/cabi.rs index b2275171687..5325c667eb6 100644 --- a/src/librustc_trans/trans/cabi.rs +++ b/src/librustc_trans/trans/cabi.rs @@ -10,8 +10,9 @@ 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; @@ -23,6 +24,11 @@ use trans::cabi_powerpc64; 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 { @@ -45,17 +51,17 @@ pub struct ArgType { /// Original LLVM type pub ty: Type, /// Coerced LLVM Type - pub cast: option::Option, + pub cast: Option, /// Dummy argument, which is emitted before the real argument - pub pad: option::Option, + pub pad: Option, /// LLVM attribute of argument - pub attr: option::Option + pub attr: Option } impl ArgType { - pub fn direct(ty: Type, cast: option::Option, - pad: option::Option, - attr: option::Option) -> ArgType { + pub fn direct(ty: Type, cast: Option, + pad: Option, + attr: Option) -> ArgType { ArgType { kind: Direct, ty: ty, @@ -65,12 +71,12 @@ impl ArgType { } } - pub fn indirect(ty: Type, attr: option::Option) -> ArgType { + pub fn indirect(ty: Type, attr: Option) -> ArgType { ArgType { kind: Indirect, ty: ty, - cast: option::Option::None, - pad: option::Option::None, + cast: Option::None, + pad: Option::None, 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 /// 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 pub struct FnType { /// The LLVM types of each argument. - pub arg_tys: Vec , + pub args: Vec, /// 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) - } 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 +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_arm::Flavor::General + 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 { + 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) }; - 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); } } diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index f2434ceee2b..745cc3ba6d6 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -228,24 +228,12 @@ fn is_reg_ty(ty: Type) -> bool { } } -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); + } } diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index c5116e73804..c65fb588e72 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -174,30 +174,17 @@ fn is_reg_ty(ty: Type) -> bool { } } -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); + } } diff --git a/src/librustc_trans/trans/cabi_asmjs.rs b/src/librustc_trans/trans/cabi_asmjs.rs index 3a4a6b9960e..dd62194a892 100644 --- a/src/librustc_trans/trans/cabi_asmjs.rs +++ b/src/librustc_trans/trans/cabi_asmjs.rs @@ -49,24 +49,12 @@ fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType { } } -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); + } } diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index bcffb238f59..74fdc0828f2 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -161,27 +161,13 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { 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) - }; +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 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, - }; + 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); + } } diff --git a/src/librustc_trans/trans/cabi_powerpc.rs b/src/librustc_trans/trans/cabi_powerpc.rs index 1bcc8fd6bbb..57d49926fae 100644 --- a/src/librustc_trans/trans/cabi_powerpc.rs +++ b/src/librustc_trans/trans/cabi_powerpc.rs @@ -156,27 +156,13 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { 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) - }; +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 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, - }; + 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); + } } diff --git a/src/librustc_trans/trans/cabi_powerpc64.rs b/src/librustc_trans/trans/cabi_powerpc64.rs index f76bb4f9eeb..dd50b116451 100644 --- a/src/librustc_trans/trans/cabi_powerpc64.rs +++ b/src/librustc_trans/trans/cabi_powerpc64.rs @@ -236,24 +236,12 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { 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) - }; +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 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, - }; + for arg in &mut fty.args { + *arg = classify_arg_ty(ccx, arg.ty); + } } diff --git a/src/librustc_trans/trans/cabi_x86.rs b/src/librustc_trans/trans/cabi_x86.rs index 50a3095dea1..81fb9d71210 100644 --- a/src/librustc_trans/trans/cabi_x86.rs +++ b/src/librustc_trans/trans/cabi_x86.rs @@ -8,24 +8,14 @@ // 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. @@ -33,53 +23,33 @@ pub fn compute_abi_info(ccx: &CrateContext, // 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, - }; } diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 00d8fdad32d..5946aa4a8c3 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -383,10 +383,7 @@ fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { } } -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(ccx: &CrateContext, ty: Type, 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 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; @@ -422,14 +419,11 @@ pub fn compute_abi_info(ccx: &CrateContext, } 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() || @@ -445,20 +439,14 @@ pub fn compute_abi_info(ccx: &CrateContext, } 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, - }; } diff --git a/src/librustc_trans/trans/cabi_x86_win64.rs b/src/librustc_trans/trans/cabi_x86_win64.rs index 120c8dc0384..21c746a1e88 100644 --- a/src/librustc_trans/trans/cabi_x86_win64.rs +++ b/src/librustc_trans/trans/cabi_x86_win64.rs @@ -16,49 +16,23 @@ use trans::type_::Type; // 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) - } +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) } - _ => { - let attr = if t == Type::i1(ccx) { Some(Attribute::ZExt) } else { None }; - ArgType::direct(t, None, None, 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); + } } diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 592cb50930a..99df9c18185 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -17,7 +17,7 @@ use trans::attributes; 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; @@ -45,83 +45,15 @@ use syntax::attr::AttrMetaMethods; 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 , - - // 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 @@ -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 = 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(); @@ -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 // 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()); @@ -293,7 +221,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } 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() { @@ -359,15 +287,13 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, 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 @@ -378,14 +304,14 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }; // 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; } @@ -402,7 +328,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let llforeign_retval = CallWithConv(bcx, llfn, &llargs_foreign[..], - cc, + fn_type.cconv, Some(attrs), 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 // 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)); @@ -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 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>, @@ -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 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())); @@ -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 // 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); @@ -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>, 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"); @@ -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 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 @@ -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 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 }; @@ -696,7 +633,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, return_ty={:?}", Value(slot), llrust_ret_ty, - tys.fn_sig.output); + fn_sig.output); llrust_args.push(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. // 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 { @@ -720,7 +657,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } 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() { @@ -802,12 +739,12 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, 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(); @@ -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; + } } } diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index e16f25cfec5..ee2d84a7be7 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -14,8 +14,8 @@ use middle::def_id::DefId; 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}; @@ -239,14 +239,6 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ 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) @@ -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::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),