diff options
Diffstat (limited to 'src/librustc_trans/trans/foreign.rs')
-rw-r--r-- | src/librustc_trans/trans/foreign.rs | 1072 |
1 files changed, 0 insertions, 1072 deletions
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs deleted file mode 100644 index cace98a230f..00000000000 --- a/src/librustc_trans/trans/foreign.rs +++ /dev/null @@ -1,1072 +0,0 @@ -// 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, TyCtxt}; -use middle::subst::Substs; - -use std::cmp; -use std::iter::once; -use libc::c_uint; -use syntax::abi::Abi; -use syntax::attr; -use syntax::codemap::Span; -use syntax::parse::token::{InternedString, special_idents}; -use syntax::ast; -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<Type> , - - // LLVM version of the type that this function returns. Note that - // this *may not be* the declared return type of the foreign - // function, because the foreign function may opt to return via an - // out pointer. - llret_ty: Type, - - /// True if there is a return value (not bottom, not unit) - ret_def: bool, -} - - -/////////////////////////////////////////////////////////////////////////// -// Calls to external functions - -pub fn llvm_calling_convention(ccx: &CrateContext, - abi: Abi) -> CallConv { - use syntax::abi::Abi::*; - match ccx.sess().target.target.adjust_abi(abi) { - RustIntrinsic => { - // Intrinsics are emitted at the call site - ccx.sess().bug("asked to register intrinsic fn"); - } - PlatformIntrinsic => { - // Intrinsics are emitted at the call site - ccx.sess().bug("asked to register platform intrinsic fn"); - } - - Rust => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with Rust ABI"); - } - - RustCall => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with RustCall ABI"); - } - - // It's the ABI's job to select this, not us. - System => ccx.sess().bug("system abi should be selected elsewhere"), - - Stdcall => llvm::X86StdcallCallConv, - Fastcall => llvm::X86FastcallCallConv, - Vectorcall => llvm::X86_VectorCall, - C => llvm::CCallConv, - Win64 => llvm::X86_64_Win64, - - // These API constants ought to be more specific... - Cdecl => llvm::CCallConv, - Aapcs => llvm::CCallConv, - } -} - -pub fn register_static(ccx: &CrateContext, - foreign_item: &hir::ForeignItem) -> ValueRef { - let ty = ccx.tcx().node_id_to_type(foreign_item.id); - let llty = type_of::type_of(ccx, ty); - - let ident = link_name(foreign_item); - let 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 - // 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), - }; - - // Handle thread-local external statics. - for attr in foreign_item.attrs.iter() { - if attr.check_name("thread_local") { - llvm::set_thread_local(c, true); - } - } - - return c; -} - -// 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::TyFnDef(_, _, ref fn_ty) | - ty::TyFnPtr(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: &TyCtxt, 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 { - Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic => {} - abi => { - let ty = ccx.tcx().node_id_to_type(foreign_item.id); - match ty.sty { - ty::TyFnDef(_, _, bft) | - ty::TyFnPtr(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::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(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::TyFnDef(_, _, ref fn_ty) | ty::TyFnPtr(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::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f)=> { - assert!(f.abi != Abi::Rust); - assert!(f.abi != Abi::RustIntrinsic); - assert!(f.abi != 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, - None, 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::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, - 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; - } -} |