| // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| |
| use back::{abi, link}; |
| use llvm::{ValueRef, CallConv, get_param}; |
| use llvm; |
| use middle::weak_lang_items; |
| use trans::attributes; |
| use trans::base::{llvm_linkage_by_name, push_ctxt}; |
| use trans::base; |
| use trans::build::*; |
| use trans::cabi; |
| use trans::common::*; |
| use trans::debuginfo::DebugLoc; |
| use trans::declare; |
| use trans::expr; |
| use trans::machine; |
| use trans::monomorphize; |
| use trans::type_::Type; |
| use trans::type_of::*; |
| use trans::type_of; |
| use middle::infer; |
| use middle::ty::{self, Ty}; |
| use middle::subst::Substs; |
| |
| use std::cmp; |
| use std::iter::once; |
| use libc::c_uint; |
| use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi}; |
| use syntax::abi::{PlatformIntrinsic, RustIntrinsic, Rust, RustCall, Stdcall}; |
| use syntax::abi::{Fastcall, Vectorcall, System}; |
| use syntax::attr; |
| use syntax::codemap::Span; |
| use syntax::parse::token::{InternedString, special_idents}; |
| use syntax::ast; |
| |
| 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 { |
| 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); |
| 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 |
| // it a little specially. The typesystem prevents things like &T and |
| // extern "C" fn() from being non-null, so we can't just declare a |
| // static and call it a day. Some linkages (like weak) will make it such |
| // that the static actually has a null value. |
| Some(name) => { |
| let linkage = match llvm_linkage_by_name(&name) { |
| Some(linkage) => linkage, |
| None => { |
| ccx.sess().span_fatal(foreign_item.span, |
| "invalid linkage specified"); |
| } |
| }; |
| let llty2 = match ty.sty { |
| ty::TyRawPtr(ref mt) => type_of::type_of(ccx, mt.ty), |
| _ => { |
| ccx.sess().span_fatal(foreign_item.span, |
| "must have type `*T` or `*mut T`"); |
| } |
| }; |
| unsafe { |
| // Declare a symbol `foo` with the desired linkage. |
| let g1 = declare::declare_global(ccx, &ident[..], llty2); |
| llvm::SetLinkage(g1, linkage); |
| |
| // Declare an internal global `extern_with_linkage_foo` which |
| // is initialized with the address of `foo`. If `foo` is |
| // discarded during linking (for example, if `foo` has weak |
| // linkage and there are no definitions), then |
| // `extern_with_linkage_foo` will instead be initialized to |
| // zero. |
| let mut real_name = "_rust_extern_with_linkage_".to_string(); |
| real_name.push_str(&ident); |
| let g2 = declare::define_global(ccx, &real_name[..], llty).unwrap_or_else(||{ |
| ccx.sess().span_fatal(foreign_item.span, |
| &format!("symbol `{}` is already defined", ident)) |
| }); |
| llvm::SetLinkage(g2, llvm::InternalLinkage); |
| llvm::LLVMSetInitializer(g2, g1); |
| g2 |
| } |
| } |
| None => // Generate an external declaration. |
| declare::declare_global(ccx, &ident[..], llty), |
| } |
| } |
| |
| // only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions |
| pub fn get_extern_fn(ccx: &CrateContext, |
| externs: &mut ExternMap, |
| name: &str, |
| cc: llvm::CallConv, |
| ty: Type, |
| output: Ty) |
| -> ValueRef { |
| match externs.get(name) { |
| Some(n) => return *n, |
| None => {} |
| } |
| let f = declare::declare_fn(ccx, name, cc, ty, ty::FnConverging(output)); |
| externs.insert(name.to_string(), f); |
| f |
| } |
| |
| /// Registers a foreign function found in a library. Just adds a LLVM global. |
| pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| abi: Abi, fty: Ty<'tcx>, |
| name: &str, |
| attrs: &[ast::Attribute])-> ValueRef { |
| debug!("register_foreign_item_fn(abi={:?}, \ |
| ty={:?}, \ |
| name={})", |
| abi, |
| fty, |
| name); |
| |
| let cc = llvm_calling_convention(ccx, abi); |
| |
| // Register the function as a C extern fn |
| let tys = foreign_types_for_fn_ty(ccx, fty); |
| |
| // Make sure the calling convention is right for variadic functions |
| // (should've been caught if not in typeck) |
| if tys.fn_sig.variadic { |
| assert!(cc == llvm::CCallConv); |
| } |
| |
| // Create the LLVM value for the C extern fn |
| let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); |
| |
| let llfn = get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, fty); |
| attributes::unwind(llfn, false); |
| add_argument_attributes(&tys, llfn); |
| attributes::from_fn_attrs(ccx, attrs, llfn); |
| llfn |
| } |
| |
| /// Prepares a call to a native function. This requires adapting |
| /// from the Rust argument passing rules to the native rules. |
| /// |
| /// # Parameters |
| /// |
| /// - `callee_ty`: Rust type for the function we are calling |
| /// - `llfn`: the function pointer we are calling |
| /// - `llretptr`: where to store the return value of the function |
| /// - `llargs_rust`: a list of the argument values, prepared |
| /// as they would be if calling a Rust function |
| /// - `passed_arg_tys`: Rust type for the arguments. Normally we |
| /// can derive these from callee_ty but in the case of variadic |
| /// functions passed_arg_tys will include the Rust type of all |
| /// the arguments including the ones not specified in the fn's signature. |
| pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, |
| callee_ty: Ty<'tcx>, |
| llfn: ValueRef, |
| llretptr: ValueRef, |
| llargs_rust: &[ValueRef], |
| passed_arg_tys: Vec<Ty<'tcx>>, |
| call_debug_loc: DebugLoc) |
| -> Block<'blk, 'tcx> |
| { |
| let ccx = bcx.ccx(); |
| |
| debug!("trans_native_call(callee_ty={:?}, \ |
| llfn={}, \ |
| llretptr={})", |
| callee_ty, |
| ccx.tn().val_to_string(llfn), |
| ccx.tn().val_to_string(llretptr)); |
| |
| let (fn_abi, fn_sig) = match callee_ty.sty { |
| ty::TyBareFn(_, ref fn_ty) => (fn_ty.abi, &fn_ty.sig), |
| _ => ccx.sess().bug("trans_native_call 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, &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 mut llargs_foreign = Vec::new(); |
| |
| // If the foreign ABI expects return value by pointer, supply the |
| // 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 { |
| Some(ty) => { |
| let llcastedretptr = |
| BitCast(bcx, llretptr, ty.ptr_to()); |
| llargs_foreign.push(llcastedretptr); |
| } |
| None => { |
| llargs_foreign.push(llretptr); |
| } |
| } |
| } |
| |
| let mut offset = 0; |
| for (i, arg_ty) in arg_tys.iter().enumerate() { |
| let mut llarg_rust = llargs_rust[i + offset]; |
| |
| if arg_ty.is_ignore() { |
| continue; |
| } |
| |
| // Does Rust pass this argument by pointer? |
| let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]); |
| |
| debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}", |
| i, |
| ccx.tn().val_to_string(llarg_rust), |
| rust_indirect, |
| ccx.tn().type_to_string(arg_ty.ty)); |
| |
| // Ensure that we always have the Rust value indirectly, |
| // because it makes bitcasting easier. |
| if !rust_indirect { |
| let scratch = base::alloc_ty(bcx, passed_arg_tys[i], "__arg"); |
| if type_is_fat_ptr(ccx.tcx(), passed_arg_tys[i]) { |
| Store(bcx, llargs_rust[i + offset], expr::get_dataptr(bcx, scratch)); |
| Store(bcx, llargs_rust[i + offset + 1], expr::get_meta(bcx, scratch)); |
| offset += 1; |
| } else { |
| base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]); |
| } |
| llarg_rust = scratch; |
| } |
| |
| debug!("llarg_rust={} (after indirection)", |
| ccx.tn().val_to_string(llarg_rust)); |
| |
| // Check whether we need to do any casting |
| match arg_ty.cast { |
| Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()), |
| None => () |
| } |
| |
| debug!("llarg_rust={} (after casting)", |
| ccx.tn().val_to_string(llarg_rust)); |
| |
| // Finally, load the value if needed for the foreign ABI |
| let foreign_indirect = arg_ty.is_indirect(); |
| let llarg_foreign = if foreign_indirect { |
| llarg_rust |
| } else { |
| if passed_arg_tys[i].is_bool() { |
| let val = LoadRangeAssert(bcx, llarg_rust, 0, 2, llvm::False); |
| Trunc(bcx, val, Type::i1(bcx.ccx())) |
| } else { |
| Load(bcx, llarg_rust) |
| } |
| }; |
| |
| debug!("argument {}, llarg_foreign={}", |
| i, ccx.tn().val_to_string(llarg_foreign)); |
| |
| // fill padding with undef value |
| match arg_ty.pad { |
| Some(ty) => llargs_foreign.push(C_undef(ty)), |
| None => () |
| } |
| 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); |
| |
| // The outptr can be noalias and nocapture because it's entirely |
| // invisible to the program. We also know it's nonnull as well |
| // as how many bytes we can dereference |
| attrs.arg(1, llvm::Attribute::NoAlias) |
| .arg(1, llvm::Attribute::NoCapture) |
| .arg(1, llvm::DereferenceableAttribute(llret_sz)); |
| }; |
| |
| // 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 { |
| Some(attr) => { attrs.arg(arg_idx, attr); }, |
| _ => () |
| } |
| |
| arg_idx += 1; |
| for arg_ty in &fn_type.arg_tys { |
| if arg_ty.is_ignore() { |
| continue; |
| } |
| // skip padding |
| if arg_ty.pad.is_some() { arg_idx += 1; } |
| |
| if let Some(attr) = arg_ty.attr { |
| attrs.arg(arg_idx, attr); |
| } |
| |
| arg_idx += 1; |
| } |
| |
| let llforeign_retval = CallWithConv(bcx, |
| llfn, |
| &llargs_foreign[..], |
| cc, |
| Some(attrs), |
| call_debug_loc); |
| |
| // If the function we just called does not use an outpointer, |
| // store the result into the rust outpointer. Cast the outpointer |
| // 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 { |
| Some(ty) => ty, |
| None => fn_type.ret_ty.ty |
| }; |
| |
| debug!("llretptr={}", ccx.tn().val_to_string(llretptr)); |
| debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval)); |
| debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty)); |
| debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty)); |
| |
| if llrust_ret_ty == llforeign_ret_ty { |
| match fn_sig.output { |
| ty::FnConverging(result_ty) => { |
| base::store_ty(bcx, llforeign_retval, llretptr, result_ty) |
| } |
| ty::FnDiverging => {} |
| } |
| } else { |
| // The actual return type is a struct, but the ABI |
| // adaptation code has cast it into some scalar type. The |
| // code that follows is the only reliable way I have |
| // found to do a transform like i64 -> {i32,i32}. |
| // Basically we dump the data onto the stack then memcpy it. |
| // |
| // Other approaches I tried: |
| // - Casting rust ret pointer to the foreign type and using Store |
| // is (a) unsafe if size of foreign type > size of rust type and |
| // (b) runs afoul of strict aliasing rules, yielding invalid |
| // assembly under -O (specifically, the store gets removed). |
| // - Truncating foreign type to correct integral type and then |
| // bitcasting to the struct type yields invalid cast errors. |
| let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); |
| base::call_lifetime_start(bcx, llscratch); |
| Store(bcx, llforeign_retval, llscratch); |
| let llscratch_i8 = BitCast(bcx, llscratch, Type::i8(ccx).ptr_to()); |
| let llretptr_i8 = BitCast(bcx, llretptr, Type::i8(ccx).ptr_to()); |
| let llrust_size = machine::llsize_of_store(ccx, llrust_ret_ty); |
| let llforeign_align = machine::llalign_of_min(ccx, llforeign_ret_ty); |
| let llrust_align = machine::llalign_of_min(ccx, llrust_ret_ty); |
| let llalign = cmp::min(llforeign_align, llrust_align); |
| debug!("llrust_size={}", llrust_size); |
| base::call_memcpy(bcx, llretptr_i8, llscratch_i8, |
| C_uint(ccx, llrust_size), llalign as u32); |
| base::call_lifetime_end(bcx, llscratch); |
| } |
| } |
| |
| return bcx; |
| } |
| |
| // feature gate SIMD types in FFI, since I (huonw) am not sure the |
| // ABIs are handled at all correctly. |
| fn gate_simd_ffi(tcx: &ty::ctxt, decl: &hir::FnDecl, ty: &ty::BareFnTy) { |
| if !tcx.sess.features.borrow().simd_ffi { |
| let check = |ast_ty: &hir::Ty, ty: ty::Ty| { |
| if ty.is_simd() { |
| tcx.sess.struct_span_err(ast_ty.span, |
| &format!("use of SIMD type `{}` in FFI is highly experimental and \ |
| may result in invalid code", |
| pprust::ty_to_string(ast_ty))) |
| .fileline_help(ast_ty.span, |
| "add #![feature(simd_ffi)] to the crate attributes to enable") |
| .emit(); |
| } |
| }; |
| let sig = &ty.sig.0; |
| for (input, ty) in decl.inputs.iter().zip(&sig.inputs) { |
| check(&*input.ty, *ty) |
| } |
| if let hir::Return(ref ty) = decl.output { |
| check(&**ty, sig.output.unwrap()) |
| } |
| } |
| } |
| |
| pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &hir::ForeignMod) { |
| let _icx = push_ctxt("foreign::trans_foreign_mod"); |
| for foreign_item in &foreign_mod.items { |
| let lname = link_name(foreign_item); |
| |
| if let hir::ForeignItemFn(ref decl, _) = foreign_item.node { |
| match foreign_mod.abi { |
| Rust | RustIntrinsic | PlatformIntrinsic => {} |
| abi => { |
| let ty = ccx.tcx().node_id_to_type(foreign_item.id); |
| match ty.sty { |
| ty::TyBareFn(_, bft) => gate_simd_ffi(ccx.tcx(), &**decl, bft), |
| _ => ccx.tcx().sess.span_bug(foreign_item.span, |
| "foreign fn's sty isn't a bare_fn_ty?") |
| } |
| |
| register_foreign_item_fn(ccx, abi, ty, &lname, &foreign_item.attrs); |
| // Unlike for other items, we shouldn't call |
| // `base::update_linkage` here. Foreign items have |
| // special linkage requirements, which are handled |
| // inside `foreign::register_*`. |
| } |
| } |
| } |
| |
| ccx.item_symbols().borrow_mut().insert(foreign_item.id, |
| lname.to_string()); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Rust functions with foreign ABIs |
| // |
| // These are normal Rust functions defined with foreign ABIs. For |
| // now, and perhaps forever, we translate these using a "layer of |
| // indirection". That is, given a Rust declaration like: |
| // |
| // extern "C" fn foo(i: u32) -> u32 { ... } |
| // |
| // we will generate a function like: |
| // |
| // S foo(T i) { |
| // S r; |
| // foo0(&r, NULL, i); |
| // return r; |
| // } |
| // |
| // #[inline_always] |
| // void foo0(uint32_t *r, void *env, uint32_t i) { ... } |
| // |
| // Here the (internal) `foo0` function follows the Rust ABI as normal, |
| // where the `foo` function follows the C ABI. We rely on LLVM to |
| // inline the one into the other. Of course we could just generate the |
| // correct code in the first place, but this is much simpler. |
| |
| pub fn decl_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| t: Ty<'tcx>, |
| name: &str) |
| -> ValueRef { |
| let tys = foreign_types_for_fn_ty(ccx, t); |
| let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); |
| let cconv = match t.sty { |
| ty::TyBareFn(_, ref fn_ty) => { |
| llvm_calling_convention(ccx, fn_ty.abi) |
| } |
| _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi") |
| }; |
| let llfn = declare::declare_fn(ccx, name, cconv, llfn_ty, |
| ty::FnConverging(ccx.tcx().mk_nil())); |
| add_argument_attributes(&tys, llfn); |
| debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", |
| ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); |
| llfn |
| } |
| |
| pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext, |
| sp: Span, |
| sym: String, |
| node_id: ast::NodeId) |
| -> ValueRef { |
| let _icx = push_ctxt("foreign::register_foreign_fn"); |
| |
| let t = ccx.tcx().node_id_to_type(node_id); |
| let cconv = match t.sty { |
| ty::TyBareFn(_, ref fn_ty) => { |
| llvm_calling_convention(ccx, fn_ty.abi) |
| } |
| _ => panic!("expected bare fn in register_rust_fn_with_foreign_abi") |
| }; |
| let tys = foreign_types_for_fn_ty(ccx, t); |
| let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); |
| let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty); |
| add_argument_attributes(&tys, llfn); |
| debug!("register_rust_fn_with_foreign_abi(node_id={}, llfn_ty={}, llfn={})", |
| node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); |
| llfn |
| } |
| |
| pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| decl: &hir::FnDecl, |
| body: &hir::Block, |
| attrs: &[ast::Attribute], |
| llwrapfn: ValueRef, |
| param_substs: &'tcx Substs<'tcx>, |
| id: ast::NodeId, |
| hash: Option<&str>) { |
| let _icx = push_ctxt("foreign::build_foreign_fn"); |
| |
| 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); |
| |
| 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); |
| } |
| |
| fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| decl: &hir::FnDecl, |
| body: &hir::Block, |
| param_substs: &'tcx Substs<'tcx>, |
| attrs: &[ast::Attribute], |
| id: ast::NodeId, |
| hash: Option<&str>) |
| -> ValueRef |
| { |
| let _icx = push_ctxt("foreign::foreign::build_rust_fn"); |
| let tcx = ccx.tcx(); |
| let t = tcx.node_id_to_type(id); |
| let t = monomorphize::apply_param_substs(tcx, param_substs, &t); |
| |
| let path = |
| tcx.map.def_path_from_id(id) |
| .into_iter() |
| .map(|e| e.data.as_interned_str()) |
| .chain(once(special_idents::clownshoe_abi.name.as_str())); |
| let ps = link::mangle(path, hash); |
| |
| // 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::TyBareFn(_, ref f) => { |
| assert!(f.abi != Rust && f.abi != RustIntrinsic && f.abi != PlatformIntrinsic); |
| } |
| _ => { |
| ccx.sess().bug(&format!("build_rust_fn: extern fn {} has ty {:?}, \ |
| expected a bare fn ty", |
| ccx.tcx().map.path_to_string(id), |
| t)); |
| } |
| }; |
| |
| debug!("build_rust_fn: path={} id={} t={:?}", |
| ccx.tcx().map.path_to_string(id), |
| id, t); |
| |
| let llfn = declare::define_internal_rust_fn(ccx, &ps, t); |
| attributes::from_fn_attrs(ccx, attrs, llfn); |
| base::trans_fn(ccx, decl, body, llfn, param_substs, id, attrs); |
| llfn |
| } |
| |
| unsafe fn build_wrap_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| llrustfn: ValueRef, |
| llwrapfn: ValueRef, |
| tys: &ForeignTypes<'tcx>, |
| t: Ty<'tcx>) { |
| let _icx = push_ctxt( |
| "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn"); |
| |
| debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={:?})", |
| ccx.tn().val_to_string(llrustfn), |
| ccx.tn().val_to_string(llwrapfn), |
| t); |
| |
| // Avoid all the Rust generation stuff and just generate raw |
| // LLVM here. |
| // |
| // We want to generate code like this: |
| // |
| // S foo(T i) { |
| // S r; |
| // foo0(&r, NULL, i); |
| // return r; |
| // } |
| |
| if llvm::LLVMCountBasicBlocks(llwrapfn) != 0 { |
| ccx.sess().bug("wrapping a function inside non-empty wrapper, most likely cause is \ |
| multiple functions being wrapped"); |
| } |
| |
| let ptr = "the block\0".as_ptr(); |
| let the_block = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, |
| ptr as *const _); |
| |
| let builder = ccx.builder(); |
| builder.position_at_end(the_block); |
| |
| // Array for the arguments we will pass to the rust function. |
| let mut llrust_args = Vec::new(); |
| let mut next_foreign_arg_counter: c_uint = 0; |
| let mut next_foreign_arg = |pad: bool| -> c_uint { |
| next_foreign_arg_counter += if pad { |
| 2 |
| } else { |
| 1 |
| }; |
| next_foreign_arg_counter - 1 |
| }; |
| |
| // If there is an out pointer on the foreign function |
| let foreign_outptr = { |
| if tys.fn_ty.ret_ty.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 { |
| ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty), |
| ty::FnDiverging => false |
| }; |
| let return_alloca: Option<ValueRef>; |
| let llrust_ret_ty = if rust_uses_outptr { |
| rust_param_tys.next().expect("Missing return type!").element_type() |
| } else { |
| rustfn_ty.return_type() |
| }; |
| if rust_uses_outptr { |
| // Rust expects to use an outpointer. If the foreign fn |
| // also uses an outpointer, we can reuse it, but the types |
| // may vary, so cast first to the Rust type. If the |
| // foreign fn does NOT use an outpointer, we will have to |
| // alloca some scratch space on the stack. |
| match foreign_outptr { |
| Some(llforeign_outptr) => { |
| debug!("out pointer, foreign={}", |
| ccx.tn().val_to_string(llforeign_outptr)); |
| let llrust_retptr = |
| builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); |
| debug!("out pointer, foreign={} (casted)", |
| ccx.tn().val_to_string(llrust_retptr)); |
| llrust_args.push(llrust_retptr); |
| return_alloca = None; |
| } |
| |
| None => { |
| let slot = builder.alloca(llrust_ret_ty, "return_alloca"); |
| debug!("out pointer, \ |
| allocad={}, \ |
| llrust_ret_ty={}, \ |
| return_ty={:?}", |
| ccx.tn().val_to_string(slot), |
| ccx.tn().type_to_string(llrust_ret_ty), |
| tys.fn_sig.output); |
| llrust_args.push(slot); |
| return_alloca = Some(slot); |
| } |
| } |
| } else { |
| // Rust does not expect an outpointer. If the foreign fn |
| // does use an outpointer, then we will do a store of the |
| // value that the Rust fn returns. |
| return_alloca = None; |
| }; |
| |
| // 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]; |
| 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 { |
| llty.element_type() |
| } else { |
| llty |
| }; |
| let llforeign_arg_ty = tys.fn_ty.arg_tys[i]; |
| let foreign_indirect = llforeign_arg_ty.is_indirect(); |
| |
| if llforeign_arg_ty.is_ignore() { |
| debug!("skipping ignored arg #{}", i); |
| llrust_args.push(C_undef(llrust_ty)); |
| continue; |
| } |
| |
| // skip padding |
| let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some()); |
| let mut llforeign_arg = get_param(llwrapfn, foreign_index); |
| |
| debug!("llforeign_arg {}{}: {}", "#", |
| i, ccx.tn().val_to_string(llforeign_arg)); |
| debug!("rust_indirect = {}, foreign_indirect = {}", |
| rust_indirect, foreign_indirect); |
| |
| // Ensure that the foreign argument is indirect (by |
| // pointer). It makes adapting types easier, since we can |
| // always just bitcast pointers. |
| if !foreign_indirect { |
| llforeign_arg = if rust_ty.is_bool() { |
| let lltemp = builder.alloca(Type::bool(ccx), ""); |
| builder.store(builder.zext(llforeign_arg, Type::bool(ccx)), lltemp); |
| lltemp |
| } else { |
| let lltemp = builder.alloca(val_ty(llforeign_arg), ""); |
| builder.store(llforeign_arg, lltemp); |
| lltemp |
| } |
| } |
| |
| // If the types in the ABI and the Rust types don't match, |
| // bitcast the llforeign_arg pointer so it matches the types |
| // Rust expects. |
| if llforeign_arg_ty.cast.is_some() && !type_is_fat_ptr(ccx.tcx(), rust_ty){ |
| assert!(!foreign_indirect); |
| llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to()); |
| } |
| |
| let llrust_arg = if rust_indirect || type_is_fat_ptr(ccx.tcx(), rust_ty) { |
| llforeign_arg |
| } else { |
| if rust_ty.is_bool() { |
| let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False); |
| builder.trunc(tmp, Type::i1(ccx)) |
| } else if type_of::type_of(ccx, rust_ty).is_aggregate() { |
| // We want to pass small aggregates as immediate values, but using an aggregate |
| // LLVM type for this leads to bad optimizations, so its arg type is an |
| // appropriately sized integer and we have to convert it |
| let tmp = builder.bitcast(llforeign_arg, |
| type_of::arg_type_of(ccx, rust_ty).ptr_to()); |
| let load = builder.load(tmp); |
| llvm::LLVMSetAlignment(load, type_of::align_of(ccx, rust_ty)); |
| load |
| } else { |
| builder.load(llforeign_arg) |
| } |
| }; |
| |
| debug!("llrust_arg {}{}: {}", "#", |
| i, ccx.tn().val_to_string(llrust_arg)); |
| if type_is_fat_ptr(ccx.tcx(), rust_ty) { |
| let next_llrust_ty = rust_param_tys.next().expect("Not enough parameter types!"); |
| llrust_args.push(builder.load(builder.bitcast(builder.struct_gep( |
| llrust_arg, abi::FAT_PTR_ADDR), llrust_ty.ptr_to()))); |
| llrust_args.push(builder.load(builder.bitcast(builder.struct_gep( |
| llrust_arg, abi::FAT_PTR_EXTRA), next_llrust_ty.ptr_to()))); |
| } else { |
| llrust_args.push(llrust_arg); |
| } |
| } |
| |
| // Perform the call itself |
| debug!("calling llrustfn = {}, t = {:?}", |
| ccx.tn().val_to_string(llrustfn), t); |
| let attributes = attributes::from_fn_type(ccx, t); |
| let llrust_ret_val = builder.call(llrustfn, &llrust_args, Some(attributes)); |
| |
| // Get the return value where the foreign fn expects it. |
| let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast { |
| Some(ty) => ty, |
| None => tys.fn_ty.ret_ty.ty |
| }; |
| match foreign_outptr { |
| None if !tys.llsig.ret_def => { |
| // Function returns `()` or `bot`, which in Rust is the LLVM |
| // type "{}" but in foreign ABIs is "Void". |
| builder.ret_void(); |
| } |
| |
| None if rust_uses_outptr => { |
| // Rust uses an outpointer, but the foreign ABI does not. Load. |
| let llrust_outptr = return_alloca.unwrap(); |
| let llforeign_outptr_casted = |
| builder.bitcast(llrust_outptr, llforeign_ret_ty.ptr_to()); |
| let llforeign_retval = builder.load(llforeign_outptr_casted); |
| builder.ret(llforeign_retval); |
| } |
| |
| None if llforeign_ret_ty != llrust_ret_ty => { |
| // Neither ABI uses an outpointer, but the types don't |
| // quite match. Must cast. Probably we should try and |
| // examine the types and use a concrete llvm cast, but |
| // right now we just use a temp memory location and |
| // bitcast the pointer, which is the same thing the |
| // old wrappers used to do. |
| let lltemp = builder.alloca(llforeign_ret_ty, ""); |
| let lltemp_casted = builder.bitcast(lltemp, llrust_ret_ty.ptr_to()); |
| builder.store(llrust_ret_val, lltemp_casted); |
| let llforeign_retval = builder.load(lltemp); |
| builder.ret(llforeign_retval); |
| } |
| |
| None => { |
| // Neither ABI uses an outpointer, and the types |
| // match. Easy peasy. |
| builder.ret(llrust_ret_val); |
| } |
| |
| Some(llforeign_outptr) if !rust_uses_outptr => { |
| // Foreign ABI requires an out pointer, but Rust doesn't. |
| // Store Rust return value. |
| let llforeign_outptr_casted = |
| builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); |
| builder.store(llrust_ret_val, llforeign_outptr_casted); |
| builder.ret_void(); |
| } |
| |
| Some(_) => { |
| // Both ABIs use outpointers. Easy peasy. |
| builder.ret_void(); |
| } |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // General ABI Support |
| // |
| // This code is kind of a confused mess and needs to be reworked given |
| // the massive simplifications that have occurred. |
| |
| pub fn link_name(i: &hir::ForeignItem) -> InternedString { |
| match attr::first_attr_value_str_by_name(&i.attrs, "link_name") { |
| Some(ln) => ln.clone(), |
| None => match weak_lang_items::link_name(&i.attrs) { |
| Some(name) => name, |
| None => i.name.as_str(), |
| } |
| } |
| } |
| |
| /// 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::TyBareFn(_, 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, |
| ccx.tn().types_to_str(&llsig.llarg_tys), |
| ccx.tn().type_to_string(llsig.llret_ty), |
| ccx.tn().types_to_str(&fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>()), |
| ccx.tn().type_to_string(fn_ty.ret_ty.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; |
| } |
| } |