]> git.lizzy.rs Git - rust.git/commitdiff
Issue #3678: Remove wrappers and call foreign functions directly
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 21 May 2013 19:25:44 +0000 (15:25 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 19 Aug 2013 11:13:15 +0000 (07:13 -0400)
28 files changed:
src/librustc/back/upcall.rs
src/librustc/driver/driver.rs
src/librustc/lib/llvm.rs
src/librustc/middle/lint.rs
src/librustc/middle/stack_check.rs [new file with mode: 0644]
src/librustc/middle/trans/base.rs
src/librustc/middle/trans/cabi.rs
src/librustc/middle/trans/cabi_arm.rs
src/librustc/middle/trans/cabi_mips.rs
src/librustc/middle/trans/cabi_x86.rs
src/librustc/middle/trans/cabi_x86_64.rs
src/librustc/middle/trans/callee.rs
src/librustc/middle/trans/common.rs
src/librustc/middle/trans/datum.rs
src/librustc/middle/trans/expr.rs
src/librustc/middle/trans/foreign.rs
src/librustc/middle/trans/intrinsic.rs [new file with mode: 0644]
src/librustc/middle/trans/mod.rs
src/librustc/middle/trans/monomorphize.rs
src/librustc/middle/trans/reflect.rs
src/librustc/middle/trans/type_of.rs
src/librustc/middle/ty.rs
src/librustc/rustc.rs
src/librustc/util/ppaux.rs
src/test/compile-fail/warn-foreign-int-types.rs
src/test/run-pass/extern-return-TwoU16s.rs
src/test/run-pass/extern-return-TwoU8s.rs
src/test/run-pass/smallest-hello-world.rs

index 76bba481619859677b262aeb1626d6d537c8b3c3..0ad53c4d49c1a14ed20222222d42d296bf89163b 100644 (file)
@@ -16,8 +16,6 @@
 
 pub struct Upcalls {
     trace: ValueRef,
-    call_shim_on_c_stack: ValueRef,
-    call_shim_on_rust_stack: ValueRef,
     rust_personality: ValueRef,
     reset_stack_limit: ValueRef
 }
@@ -47,9 +45,6 @@ pub fn declare_upcalls(targ_cfg: @session::config, llmod: ModuleRef) -> @Upcalls
 
     @Upcalls {
         trace: upcall!(fn trace(opaque_ptr, opaque_ptr, int_ty) -> Type::void()),
-        call_shim_on_c_stack: upcall!(fn call_shim_on_c_stack(opaque_ptr, opaque_ptr) -> int_ty),
-        call_shim_on_rust_stack:
-            upcall!(fn call_shim_on_rust_stack(opaque_ptr, opaque_ptr) -> int_ty),
         rust_personality: upcall!(nothrow fn rust_personality -> Type::i32()),
         reset_stack_limit: upcall!(nothrow fn reset_stack_limit -> Type::void())
     }
index 5227d68774b5ffad235e4aad1df0b233bd8a69ff..828c01157682a3c37b66268537c30c5a5c3b4d32 100644 (file)
@@ -265,6 +265,9 @@ pub fn phase_3_run_analysis_passes(sess: Session,
     time(time_passes, ~"loop checking", ||
          middle::check_loop::check_crate(ty_cx, crate));
 
+    time(time_passes, ~"stack checking", ||
+         middle::stack_check::stack_check_crate(ty_cx, crate));
+
     let middle::moves::MoveMaps {moves_map, moved_variables_set,
                                  capture_map} =
         time(time_passes, ~"compute moves", ||
index 4c204b908bc6bc2a0188eff9ceae5a4e28e39cbb..b9e4e3da9200e8ba347a6efda36b328977d93b88 100644 (file)
@@ -2246,6 +2246,11 @@ pub fn type_to_str(&self, ty: Type) -> ~str {
         self.type_to_str_depth(ty, 30)
     }
 
+    pub fn types_to_str(&self, tys: &[Type]) -> ~str {
+        let strs = tys.map(|t| self.type_to_str(*t));
+        fmt!("[%s]", strs.connect(","))
+    }
+
     pub fn val_to_str(&self, val: ValueRef) -> ~str {
         unsafe {
             let ty = Type::from_ref(llvm::LLVMTypeOf(val));
index 435e7943d662ff548c2aa545b8271033f7c275b8..880095db2ee1de151d90f41d4bde0efa81aa24e2 100644 (file)
@@ -73,6 +73,7 @@
 #[deriving(Clone, Eq)]
 pub enum lint {
     ctypes,
+    cstack,
     unused_imports,
     unnecessary_qualification,
     while_true,
@@ -146,6 +147,13 @@ enum LintSource {
         default: warn
      }),
 
+    ("cstack",
+     LintSpec {
+        lint: cstack,
+        desc: "only invoke foreign functions from fixedstacksegment fns",
+        default: deny
+     }),
+
     ("unused_imports",
      LintSpec {
         lint: unused_imports,
diff --git a/src/librustc/middle/stack_check.rs b/src/librustc/middle/stack_check.rs
new file mode 100644 (file)
index 0000000..eb5e479
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright 2012-2013 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.
+
+/*!
+
+Lint mode to detect cases where we call non-Rust fns, which do not
+have a stack growth check, from locations not annotated to request
+large stacks.
+
+*/
+
+use middle::lint;
+use middle::ty;
+use syntax::ast;
+use syntax::attr;
+use syntax::codemap::span;
+use visit = syntax::oldvisit;
+use util::ppaux::Repr;
+
+#[deriving(Clone)]
+struct Context {
+    tcx: ty::ctxt,
+    safe_stack: bool
+}
+
+pub fn stack_check_crate(tcx: ty::ctxt,
+                         crate: &ast::Crate) {
+    let new_cx = Context {
+        tcx: tcx,
+        safe_stack: false
+    };
+    let visitor = visit::mk_vt(@visit::Visitor {
+        visit_item: stack_check_item,
+        visit_fn: stack_check_fn,
+        visit_expr: stack_check_expr,
+        ..*visit::default_visitor()
+    });
+    visit::visit_crate(crate, (new_cx, visitor));
+}
+
+fn stack_check_item(item: @ast::item,
+                    (in_cx, v): (Context, visit::vt<Context>)) {
+    let safe_stack = match item.node {
+        ast::item_fn(*) => {
+            attr::contains_name(item.attrs, "fixed_stack_segment")
+        }
+        _ => {
+            false
+        }
+    };
+    let new_cx = Context {
+        tcx: in_cx.tcx,
+        safe_stack: safe_stack
+    };
+    visit::visit_item(item, (new_cx, v));
+}
+
+fn stack_check_fn<'a>(fk: &visit::fn_kind,
+                      decl: &ast::fn_decl,
+                      body: &ast::Block,
+                      sp: span,
+                      id: ast::NodeId,
+                      (in_cx, v): (Context, visit::vt<Context>)) {
+    let safe_stack = match *fk {
+        visit::fk_item_fn(*) => in_cx.safe_stack, // see stack_check_item above
+        visit::fk_anon(*) | visit::fk_fn_block | visit::fk_method(*) => false,
+    };
+    let new_cx = Context {
+        tcx: in_cx.tcx,
+        safe_stack: safe_stack
+    };
+    debug!("stack_check_fn(safe_stack=%b, id=%?)", safe_stack, id);
+    visit::visit_fn(fk, decl, body, sp, id, (new_cx, v));
+}
+
+fn stack_check_expr<'a>(expr: @ast::expr,
+                        (cx, v): (Context, visit::vt<Context>)) {
+    debug!("stack_check_expr(safe_stack=%b, expr=%s)",
+           cx.safe_stack, expr.repr(cx.tcx));
+    if !cx.safe_stack {
+        match expr.node {
+            ast::expr_call(callee, _, _) => {
+                let callee_ty = ty::expr_ty(cx.tcx, callee);
+                debug!("callee_ty=%s", callee_ty.repr(cx.tcx));
+                match ty::get(callee_ty).sty {
+                    ty::ty_bare_fn(ref fty) => {
+                        if !fty.abis.is_rust() && !fty.abis.is_intrinsic() {
+                            cx.tcx.sess.add_lint(
+                                lint::cstack,
+                                callee.id,
+                                callee.span,
+                                fmt!("invoking non-Rust fn in fn without \
+                                      #[fixed_stack_segment]"));
+                        }
+                    }
+                    _ => {}
+                }
+            }
+            _ => {}
+        }
+    }
+    visit::visit_expr(expr, (cx, v));
+}
index 6640ed0f38d92d11f4940ed735df5d37e327fee7..6189d28b0ed088a7c49f04449ce15e2fea732101 100644 (file)
@@ -203,28 +203,28 @@ pub fn decl_internal_cdecl_fn(llmod: ModuleRef, name: &str, ty: Type) -> ValueRe
     return llfn;
 }
 
-pub fn get_extern_fn(externs: &mut ExternMap, llmod: ModuleRef, name: @str,
+pub fn get_extern_fn(externs: &mut ExternMap, llmod: ModuleRef, name: &str,
                      cc: lib::llvm::CallConv, ty: Type) -> ValueRef {
-    match externs.find_copy(&name) {
-        Some(n) => return n,
+    match externs.find_equiv(&name) {
+        Some(n) => return *n,
         None => ()
     }
     let f = decl_fn(llmod, name, cc, ty);
-    externs.insert(name, f);
+    externs.insert(name.to_owned(), f);
     return f;
 }
 
 pub fn get_extern_const(externs: &mut ExternMap, llmod: ModuleRef,
-                        name: @str, ty: Type) -> ValueRef {
-    match externs.find_copy(&name) {
-        Some(n) => return n,
+                        name: &str, ty: Type) -> ValueRef {
+    match externs.find_equiv(&name) {
+        Some(n) => return *n,
         None => ()
     }
     unsafe {
         let c = do name.with_c_str |buf| {
             llvm::LLVMAddGlobal(llmod, ty.to_ref(), buf)
         };
-        externs.insert(name, c);
+        externs.insert(name.to_owned(), c);
         return c;
     }
 }
@@ -511,7 +511,6 @@ pub fn get_res_dtor(ccx: @mut CrateContext,
                                      None,
                                      ty::lookup_item_type(tcx, parent_id).ty);
         let llty = type_of_dtor(ccx, class_ty);
-        let name = name.to_managed(); // :-(
         get_extern_fn(&mut ccx.externs,
                       ccx.llmod,
                       name,
@@ -798,13 +797,13 @@ pub fn fail_if_zero(cx: @mut Block, span: span, divrem: ast::binop,
     }
 }
 
-pub fn null_env_ptr(bcx: @mut Block) -> ValueRef {
-    C_null(Type::opaque_box(bcx.ccx()).ptr_to())
+pub fn null_env_ptr(ccx: &CrateContext) -> ValueRef {
+    C_null(Type::opaque_box(ccx).ptr_to())
 }
 
 pub fn trans_external_path(ccx: &mut CrateContext, did: ast::def_id, t: ty::t)
     -> ValueRef {
-    let name = csearch::get_symbol(ccx.sess.cstore, did).to_managed(); // Sad
+    let name = csearch::get_symbol(ccx.sess.cstore, did);
     match ty::get(t).sty {
       ty::ty_bare_fn(_) | ty::ty_closure(_) => {
         let llty = type_of_fn_from_ty(ccx, t);
@@ -1572,7 +1571,7 @@ pub fn mk_return_basic_block(llfn: ValueRef) -> BasicBlockRef {
 // slot where the return value of the function must go.
 pub fn make_return_pointer(fcx: @mut FunctionContext, output_type: ty::t) -> ValueRef {
     unsafe {
-        if !ty::type_is_immediate(fcx.ccx.tcx, output_type) {
+        if type_of::return_uses_outptr(fcx.ccx.tcx, output_type) {
             llvm::LLVMGetParam(fcx.llfn, 0)
         } else {
             let lloutputtype = type_of::type_of(fcx.ccx, output_type);
@@ -1612,7 +1611,7 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
             ty::subst_tps(ccx.tcx, substs.tys, substs.self_ty, output_type)
         }
     };
-    let is_immediate = ty::type_is_immediate(ccx.tcx, substd_output_type);
+    let uses_outptr = type_of::return_uses_outptr(ccx.tcx, substd_output_type);
     let fcx = @mut FunctionContext {
           llfn: llfndecl,
           llenv: unsafe {
@@ -1624,7 +1623,7 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
           llreturn: None,
           llself: None,
           personality: None,
-          has_immediate_return_value: is_immediate,
+          caller_expects_out_pointer: uses_outptr,
           llargs: @mut HashMap::new(),
           lllocals: @mut HashMap::new(),
           llupvars: @mut HashMap::new(),
@@ -1647,8 +1646,15 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
         fcx.alloca_insert_pt = Some(llvm::LLVMGetFirstInstruction(entry_bcx.llbb));
     }
 
-    if !ty::type_is_nil(substd_output_type) && !(is_immediate && skip_retptr) {
-        fcx.llretptr = Some(make_return_pointer(fcx, substd_output_type));
+    if !ty::type_is_voidish(substd_output_type) {
+        // If the function returns nil/bot, there is no real return
+        // value, so do not set `llretptr`.
+        if !skip_retptr || uses_outptr {
+            // Otherwise, we normally allocate the llretptr, unless we
+            // have been instructed to skip it for immediate return
+            // values.
+            fcx.llretptr = Some(make_return_pointer(fcx, substd_output_type));
+        }
     }
     fcx
 }
@@ -1796,7 +1802,7 @@ pub fn finish_fn(fcx: @mut FunctionContext, last_bcx: @mut Block) {
 // Builds the return block for a function.
 pub fn build_return_block(fcx: &FunctionContext, ret_cx: @mut Block) {
     // Return the value if this function immediate; otherwise, return void.
-    if fcx.llretptr.is_none() || !fcx.has_immediate_return_value {
+    if fcx.llretptr.is_none() || fcx.caller_expects_out_pointer {
         return RetVoid(ret_cx);
     }
 
@@ -1882,9 +1888,7 @@ pub fn trans_closure(ccx: @mut CrateContext,
     // translation calls that don't have a return value (trans_crate,
     // trans_mod, trans_item, et cetera) and those that do
     // (trans_block, trans_expr, et cetera).
-    if body.expr.is_none() || ty::type_is_bot(block_ty) ||
-        ty::type_is_nil(block_ty)
-    {
+    if body.expr.is_none() || ty::type_is_voidish(block_ty) {
         bcx = controlflow::trans_block(bcx, body, expr::Ignore);
     } else {
         let dest = expr::SaveIn(fcx.llretptr.unwrap());
@@ -2129,13 +2133,14 @@ pub fn trans_item(ccx: @mut CrateContext, item: &ast::item) {
       ast::item_fn(ref decl, purity, _abis, ref generics, ref body) => {
         if purity == ast::extern_fn  {
             let llfndecl = get_item_val(ccx, item.id);
-            foreign::trans_foreign_fn(ccx,
-                                      vec::append((*path).clone(),
-                                                  [path_name(item.ident)]),
-                                      decl,
-                                      body,
-                                      llfndecl,
-                                      item.id);
+            foreign::trans_rust_fn_with_foreign_abi(
+                ccx,
+                &vec::append((*path).clone(),
+                             [path_name(item.ident)]),
+                decl,
+                body,
+                llfndecl,
+                item.id);
         } else if !generics.is_type_parameterized() {
             let llfndecl = get_item_val(ccx, item.id);
             trans_fn(ccx,
@@ -2196,7 +2201,7 @@ pub fn trans_item(ccx: @mut CrateContext, item: &ast::item) {
           }
       },
       ast::item_foreign_mod(ref foreign_mod) => {
-        foreign::trans_foreign_mod(ccx, path, foreign_mod);
+        foreign::trans_foreign_mod(ccx, foreign_mod);
       }
       ast::item_struct(struct_def, ref generics) => {
         if !generics.is_type_parameterized() {
@@ -2291,8 +2296,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext,
 
     fn create_main(ccx: @mut CrateContext, main_llfn: ValueRef) -> ValueRef {
         let nt = ty::mk_nil();
-
-        let llfty = type_of_fn(ccx, [], nt);
+        let llfty = type_of_rust_fn(ccx, [], nt);
         let llfdecl = decl_fn(ccx.llmod, "_rust_main",
                               lib::llvm::CCallConv, llfty);
 
@@ -2300,7 +2304,7 @@ fn create_main(ccx: @mut CrateContext, main_llfn: ValueRef) -> ValueRef {
 
         // the args vector built in create_entry_fn will need
         // be updated if this assertion starts to fail.
-        assert!(fcx.has_immediate_return_value);
+        assert!(!fcx.caller_expects_out_pointer);
 
         let bcx = fcx.entry_bcx.unwrap();
         // Call main.
@@ -2463,7 +2467,10 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
                             let llfn = if purity != ast::extern_fn {
                                 register_fn(ccx, i.span, sym, i.id, ty)
                             } else {
-                                foreign::register_foreign_fn(ccx, i.span, sym, i.id)
+                                foreign::register_rust_fn_with_foreign_abi(ccx,
+                                                                           i.span,
+                                                                           sym,
+                                                                           i.id)
                             };
                             set_inline_hint_if_appr(i.attrs, llfn);
                             llfn
@@ -2509,9 +2516,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
                     match ni.node {
                         ast::foreign_item_fn(*) => {
                             let path = vec::append((*pth).clone(), [path_name(ni.ident)]);
-                            let sym = exported_name(ccx, path, ty, ni.attrs);
-
-                            register_fn(ccx, ni.span, sym, ni.id, ty)
+                            foreign::register_foreign_item_fn(ccx, abis, &path, ni);
                         }
                         ast::foreign_item_static(*) => {
                             let ident = token::ident_to_str(&ni.ident);
index 6a1905c451f97e4d99f397cc3498e841d754244e..005483a075f8df31145a6fc14e80ba2794fb7e4a 100644 (file)
@@ -8,19 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use lib::llvm::{llvm, ValueRef, Attribute, Void};
-use middle::trans::base::*;
-use middle::trans::build::*;
-use middle::trans::common::*;
-
-use middle::trans::type_::Type;
-
-use std::libc::c_uint;
+use lib::llvm::Attribute;
 use std::option;
-
-pub trait ABIInfo {
-    fn compute_info(&self, atys: &[Type], rty: Type, ret_def: bool) -> FnType;
-}
+use middle::trans::context::CrateContext;
+use middle::trans::cabi_x86;
+use middle::trans::cabi_x86_64;
+use middle::trans::cabi_arm;
+use middle::trans::cabi_mips;
+use middle::trans::type_::Type;
+use syntax::abi::{X86, X86_64, Arm, Mips};
 
 #[deriving(Clone)]
 pub struct LLVMType {
@@ -28,149 +24,38 @@ pub struct LLVMType {
     ty: Type
 }
 
+/// Metadata describing how the arguments to a native function
+/// should be passed in order to respect the native ABI.
+///
+/// I will do my best to describe this structure, but these
+/// comments are reverse-engineered and may be inaccurate. -NDM
 pub struct FnType {
+    /// The LLVM types of each argument. If the cast flag is true,
+    /// then the argument should be cast, typically because the
+    /// official argument type will be an int and the rust type is i8
+    /// or something like that.
     arg_tys: ~[LLVMType],
-    ret_ty: LLVMType,
-    attrs: ~[option::Option<Attribute>],
-    sret: bool
-}
-
-impl FnType {
-    pub fn decl_fn(&self, decl: &fn(fnty: Type) -> ValueRef) -> ValueRef {
-        let atys = self.arg_tys.iter().map(|t| t.ty).collect::<~[Type]>();
-        let rty = self.ret_ty.ty;
-        let fnty = Type::func(atys, &rty);
-        let llfn = decl(fnty);
-
-        for (i, a) in self.attrs.iter().enumerate() {
-            match *a {
-                option::Some(attr) => {
-                    unsafe {
-                        let llarg = get_param(llfn, i);
-                        llvm::LLVMAddAttribute(llarg, attr as c_uint);
-                    }
-                }
-                _ => ()
-            }
-        }
-        return llfn;
-    }
 
-    pub fn build_shim_args(&self, bcx: @mut Block, arg_tys: &[Type], llargbundle: ValueRef)
-                           -> ~[ValueRef] {
-        let mut atys: &[LLVMType] = self.arg_tys;
-        let mut attrs: &[option::Option<Attribute>] = self.attrs;
-
-        let mut llargvals = ~[];
-        let mut i = 0u;
-        let n = arg_tys.len();
-
-        if self.sret {
-            let llretptr = GEPi(bcx, llargbundle, [0u, n]);
-            let llretloc = Load(bcx, llretptr);
-                llargvals = ~[llretloc];
-                atys = atys.tail();
-                attrs = attrs.tail();
-        }
-
-        while i < n {
-            let llargval = if atys[i].cast {
-                let arg_ptr = GEPi(bcx, llargbundle, [0u, i]);
-                let arg_ptr = BitCast(bcx, arg_ptr, atys[i].ty.ptr_to());
-                Load(bcx, arg_ptr)
-            } else if attrs[i].is_some() {
-                GEPi(bcx, llargbundle, [0u, i])
-            } else {
-                load_inbounds(bcx, llargbundle, [0u, i])
-            };
-            llargvals.push(llargval);
-            i += 1u;
-        }
-
-        return llargvals;
-    }
-
-    pub fn build_shim_ret(&self, bcx: @mut Block, arg_tys: &[Type], ret_def: bool,
-                          llargbundle: ValueRef, llretval: ValueRef) {
-        for (i, a) in self.attrs.iter().enumerate() {
-            match *a {
-                option::Some(attr) => {
-                    unsafe {
-                        llvm::LLVMAddInstrAttribute(llretval, (i + 1u) as c_uint, attr as c_uint);
-                    }
-                }
-                _ => ()
-            }
-        }
-        if self.sret || !ret_def {
-            return;
-        }
-        let n = arg_tys.len();
-        // R** llretptr = &args->r;
-        let llretptr = GEPi(bcx, llargbundle, [0u, n]);
-        // R* llretloc = *llretptr; /* (args->r) */
-        let llretloc = Load(bcx, llretptr);
-        if self.ret_ty.cast {
-            let tmp_ptr = BitCast(bcx, llretloc, self.ret_ty.ty.ptr_to());
-            // *args->r = r;
-            Store(bcx, llretval, tmp_ptr);
-        } else {
-            // *args->r = r;
-            Store(bcx, llretval, llretloc);
-        };
-    }
-
-    pub fn build_wrap_args(&self, bcx: @mut Block, ret_ty: Type,
-                           llwrapfn: ValueRef, llargbundle: ValueRef) {
-        let mut atys: &[LLVMType] = self.arg_tys;
-        let mut attrs: &[option::Option<Attribute>] = self.attrs;
-        let mut j = 0u;
-        let llretptr = if self.sret {
-            atys = atys.tail();
-            attrs = attrs.tail();
-            j = 1u;
-            get_param(llwrapfn, 0u)
-        } else if self.ret_ty.cast {
-            let retptr = alloca(bcx, self.ret_ty.ty, "");
-            BitCast(bcx, retptr, ret_ty.ptr_to())
-        } else {
-            alloca(bcx, ret_ty, "")
-        };
+    /// A list of attributes to be attached to each argument (parallel
+    /// the `arg_tys` array). If the attribute for a given is Some,
+    /// then the argument should be passed by reference.
+    attrs: ~[option::Option<Attribute>],
 
-        let mut i = 0u;
-        let n = atys.len();
-        while i < n {
-            let mut argval = get_param(llwrapfn, i + j);
-            if attrs[i].is_some() {
-                argval = Load(bcx, argval);
-                store_inbounds(bcx, argval, llargbundle, [0u, i]);
-            } else if atys[i].cast {
-                let argptr = GEPi(bcx, llargbundle, [0u, i]);
-                let argptr = BitCast(bcx, argptr, atys[i].ty.ptr_to());
-                Store(bcx, argval, argptr);
-            } else {
-                store_inbounds(bcx, argval, llargbundle, [0u, i]);
-            }
-            i += 1u;
-        }
-        store_inbounds(bcx, llretptr, llargbundle, [0u, n]);
-    }
+    /// LLVM return type.
+    ret_ty: LLVMType,
 
-    pub fn build_wrap_ret(&self, bcx: @mut Block, arg_tys: &[Type], llargbundle: ValueRef) {
-        if self.ret_ty.ty.kind() == Void {
-            return;
-        }
+    /// If true, then an implicit pointer should be added for the result.
+    sret: bool
+}
 
-        if bcx.fcx.llretptr.is_some() {
-            let llretval = load_inbounds(bcx, llargbundle, [ 0, arg_tys.len() ]);
-            let llretval = if self.ret_ty.cast {
-                let retptr = BitCast(bcx, llretval, self.ret_ty.ty.ptr_to());
-                Load(bcx, retptr)
-            } else {
-                Load(bcx, llretval)
-            };
-            let llretptr = BitCast(bcx, bcx.fcx.llretptr.unwrap(), self.ret_ty.ty.ptr_to());
-            Store(bcx, llretval, llretptr);
-        }
+pub fn compute_abi_info(ccx: &mut CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
+    match ccx.sess.targ_cfg.arch {
+        X86 => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def),
+        X86_64 => cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def),
+        Arm => cabi_arm::compute_abi_info(ccx, atys, rty, ret_def),
+        Mips => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
     }
 }
index 7f1fc5978c4396d68517f5377a8aeac321a37d67..19f0b9b78eb353baa4c4bd3d12ed7bf47ab1017c 100644 (file)
@@ -10,7 +10,8 @@
 
 use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
 use lib::llvm::{Attribute, StructRetAttribute};
-use middle::trans::cabi::{ABIInfo, FnType, LLVMType};
+use middle::trans::cabi::{FnType, LLVMType};
+use middle::trans::context::CrateContext;
 
 use middle::trans::type_::Type;
 
@@ -124,45 +125,37 @@ fn is_reg_ty(ty: Type) -> bool {
     }
 }
 
-enum ARM_ABIInfo { ARM_ABIInfo }
-
-impl ABIInfo for ARM_ABIInfo {
-    fn compute_info(&self,
-                    atys: &[Type],
-                    rty: Type,
-                    ret_def: bool) -> FnType {
-        let mut arg_tys = ~[];
-        let mut attrs = ~[];
-        for &aty in atys.iter() {
-            let (ty, attr) = classify_arg_ty(aty);
-            arg_tys.push(ty);
-            attrs.push(attr);
-        }
-
-        let (ret_ty, ret_attr) = if ret_def {
-            classify_ret_ty(rty)
-        } else {
-            (LLVMType { cast: false, ty: Type::void() }, None)
-        };
+pub fn compute_abi_info(_ccx: &mut CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
+    let mut arg_tys = ~[];
+    let mut attrs = ~[];
+    for &aty in atys.iter() {
+        let (ty, attr) = classify_arg_ty(aty);
+        arg_tys.push(ty);
+        attrs.push(attr);
+    }
 
-        let mut ret_ty = ret_ty;
+    let (ret_ty, ret_attr) = if ret_def {
+        classify_ret_ty(rty)
+    } else {
+        (LLVMType { cast: false, ty: Type::void() }, None)
+    };
 
-        let sret = ret_attr.is_some();
-        if sret {
-            arg_tys.unshift(ret_ty);
-            attrs.unshift(ret_attr);
-            ret_ty = LLVMType { cast: false, ty: Type::void() };
-        }
+    let mut ret_ty = ret_ty;
 
-        return FnType {
-            arg_tys: arg_tys,
-            ret_ty: ret_ty,
-            attrs: attrs,
-            sret: sret
-        };
+    let sret = ret_attr.is_some();
+    if sret {
+        arg_tys.unshift(ret_ty);
+        attrs.unshift(ret_attr);
+        ret_ty = LLVMType { cast: false, ty: Type::void() };
     }
-}
 
-pub fn abi_info() -> @ABIInfo {
-    return @ARM_ABIInfo as @ABIInfo;
+    return FnType {
+        arg_tys: arg_tys,
+        ret_ty: ret_ty,
+        attrs: attrs,
+        sret: sret
+    };
 }
index f5fb68a70578c2fcf60536019877e5f00e07d236..4577bf11b84de8b7a5c5b78efb22b3d5e7c9ca33 100644 (file)
@@ -14,6 +14,7 @@
 use std::vec;
 use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
 use lib::llvm::{Attribute, StructRetAttribute};
+use middle::trans::context::CrateContext;
 use middle::trans::context::task_llcx;
 use middle::trans::cabi::*;
 
@@ -170,47 +171,39 @@ fn struct_ty(ty: Type,
     return Type::struct_(fields, false);
 }
 
-enum MIPS_ABIInfo { MIPS_ABIInfo }
-
-impl ABIInfo for MIPS_ABIInfo {
-    fn compute_info(&self,
-                    atys: &[Type],
-                    rty: Type,
-                    ret_def: bool) -> FnType {
-        let (ret_ty, ret_attr) = if ret_def {
-            classify_ret_ty(rty)
-        } else {
-            (LLVMType { cast: false, ty: Type::void() }, None)
-        };
-
-        let mut ret_ty = ret_ty;
-
-        let sret = ret_attr.is_some();
-        let mut arg_tys = ~[];
-        let mut attrs = ~[];
-        let mut offset = if sret { 4 } else { 0 };
-
-        for aty in atys.iter() {
-            let (ty, attr) = classify_arg_ty(*aty, &mut offset);
-            arg_tys.push(ty);
-            attrs.push(attr);
-        };
-
-        if sret {
-            arg_tys = vec::append(~[ret_ty], arg_tys);
-            attrs = vec::append(~[ret_attr], attrs);
-            ret_ty = LLVMType { cast: false, ty: Type::void() };
-        }
+pub fn compute_abi_info(_ccx: &mut CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
+    let (ret_ty, ret_attr) = if ret_def {
+        classify_ret_ty(rty)
+    } else {
+        (LLVMType { cast: false, ty: Type::void() }, None)
+    };
+
+    let mut ret_ty = ret_ty;
+
+    let sret = ret_attr.is_some();
+    let mut arg_tys = ~[];
+    let mut attrs = ~[];
+    let mut offset = if sret { 4 } else { 0 };
 
-        return FnType {
-            arg_tys: arg_tys,
-            ret_ty: ret_ty,
-            attrs: attrs,
-            sret: sret
-        };
+    for aty in atys.iter() {
+        let (ty, attr) = classify_arg_ty(*aty, &mut offset);
+        arg_tys.push(ty);
+        attrs.push(attr);
+    };
+
+    if sret {
+        arg_tys = vec::append(~[ret_ty], arg_tys);
+        attrs = vec::append(~[ret_attr], attrs);
+        ret_ty = LLVMType { cast: false, ty: Type::void() };
     }
-}
 
-pub fn abi_info() -> @ABIInfo {
-    return @MIPS_ABIInfo as @ABIInfo;
+    return FnType {
+        arg_tys: arg_tys,
+        ret_ty: ret_ty,
+        attrs: attrs,
+        sret: sret
+    };
 }
index 8c5a2e70484bcd73ce8f5ce094427578de6989ac..f0af31e795af239c218bcc3c350bf09715939df6 100644 (file)
 use super::cabi::*;
 use super::common::*;
 use super::machine::*;
-
 use middle::trans::type_::Type;
 
-struct X86_ABIInfo {
-    ccx: @mut CrateContext
-}
+pub fn compute_abi_info(ccx: &mut CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
+    let mut arg_tys = ~[];
+    let mut attrs = ~[];
 
-impl ABIInfo for X86_ABIInfo {
-    fn compute_info(&self,
-                    atys: &[Type],
-                    rty: Type,
-                    ret_def: bool) -> FnType {
-        let mut arg_tys = do atys.map |a| {
-            LLVMType { cast: false, ty: *a }
-        };
-        let mut ret_ty = LLVMType {
+    let ret_ty;
+    let sret;
+    if !ret_def {
+        ret_ty = LLVMType {
             cast: false,
-            ty: rty
+            ty: Type::void(),
         };
-        let mut attrs = do atys.map |_| {
-            None
-        };
-
-        // Rules for returning structs taken from
+        sret = false;
+    } else if rty.kind() == Struct {
+        // Returning a structure. Most often, this will use
+        // a hidden first argument. On some platforms, though,
+        // small structs are returned as integers.
+        //
+        // Some links:
         // http://www.angelcode.com/dev/callconv/callconv.html
         // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
-        let sret = {
-            let returning_a_struct = rty.kind() == Struct && ret_def;
-            let big_struct = match self.ccx.sess.targ_cfg.os {
-                os_win32 | os_macos => llsize_of_alloc(self.ccx, rty) > 8,
-                _ => true
-            };
-            returning_a_struct && big_struct
+
+        enum Strategy { RetValue(Type), RetPointer }
+        let strategy = match ccx.sess.targ_cfg.os {
+            os_win32 | os_macos => {
+                match llsize_of_alloc(ccx, rty) {
+                    1 => RetValue(Type::i8()),
+                    2 => RetValue(Type::i16()),
+                    4 => RetValue(Type::i32()),
+                    8 => RetValue(Type::i64()),
+                    _ => RetPointer
+                }
+            }
+            _ => {
+                RetPointer
+            }
         };
 
-        if sret {
-            let ret_ptr_ty = LLVMType {
-                cast: false,
-                ty: ret_ty.ty.ptr_to()
-            };
-            arg_tys = ~[ret_ptr_ty] + arg_tys;
-            attrs = ~[Some(StructRetAttribute)] + attrs;
-            ret_ty = LLVMType {
-                cast: false,
-                ty: Type::void(),
-            };
-        } else if !ret_def {
-            ret_ty = LLVMType {
-                cast: false,
-                ty: Type::void()
-            };
-        }
+        match strategy {
+            RetValue(t) => {
+                ret_ty = LLVMType {
+                    cast: true,
+                    ty: t
+                };
+                sret = false;
+            }
+            RetPointer => {
+                arg_tys.push(LLVMType {
+                    cast: false,
+                    ty: rty.ptr_to()
+                });
+                attrs.push(Some(StructRetAttribute));
 
-        return FnType {
-            arg_tys: arg_tys,
-            ret_ty: ret_ty,
-            attrs: attrs,
-            sret: sret
+                ret_ty = LLVMType {
+                    cast: false,
+                    ty: Type::void(),
+                };
+                sret = true;
+            }
+        }
+    } else {
+        ret_ty = LLVMType {
+            cast: false,
+            ty: rty
         };
+        sret = false;
+    }
+
+    for &a in atys.iter() {
+        arg_tys.push(LLVMType { cast: false, ty: a });
+        attrs.push(None);
     }
-}
 
-pub fn abi_info(ccx: @mut CrateContext) -> @ABIInfo {
-    return @X86_ABIInfo {
-        ccx: ccx
-    } as @ABIInfo;
+    return FnType {
+        arg_tys: arg_tys,
+        ret_ty: ret_ty,
+        attrs: attrs,
+        sret: sret
+    };
 }
index dd24ec3ff1ac198222cecf77cb54afb0b97cfa1d..179366878418f0a879d0481a7454925480c613c5 100644 (file)
@@ -15,6 +15,7 @@
 use lib::llvm::{Struct, Array, Attribute};
 use lib::llvm::{StructRetAttribute, ByValAttribute};
 use middle::trans::cabi::*;
+use middle::trans::context::CrateContext;
 
 use middle::trans::type_::Type;
 
@@ -331,10 +332,10 @@ fn llvec_len(cls: &[RegClass]) -> uint {
     return Type::struct_(tys, false);
 }
 
-fn x86_64_tys(atys: &[Type],
-              rty: Type,
-              ret_def: bool) -> FnType {
-
+pub fn compute_abi_info(_ccx: &mut CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
     fn x86_64_ty(ty: Type,
                  is_mem_cls: &fn(cls: &[RegClass]) -> bool,
                  attr: Attribute) -> (LLVMType, Option<Attribute>) {
@@ -384,18 +385,3 @@ fn x86_64_ty(ty: Type,
         sret: sret
     };
 }
-
-enum X86_64_ABIInfo { X86_64_ABIInfo }
-
-impl ABIInfo for X86_64_ABIInfo {
-    fn compute_info(&self,
-                    atys: &[Type],
-                    rty: Type,
-                    ret_def: bool) -> FnType {
-        return x86_64_tys(atys, rty, ret_def);
-    }
-}
-
-pub fn abi_info() -> @ABIInfo {
-    return @X86_64_ABIInfo as @ABIInfo;
-}
index 4caaf384873511c1dc8865f2bdcd895e7338781e..c4720f5cb35b144e156782518af4286301566783 100644 (file)
@@ -37,6 +37,7 @@
 use middle::trans::meth;
 use middle::trans::monomorphize;
 use middle::trans::type_of;
+use middle::trans::foreign;
 use middle::ty;
 use middle::subst::Subst;
 use middle::typeck;
@@ -46,6 +47,7 @@
 use middle::trans::type_::Type;
 
 use syntax::ast;
+use syntax::abi::AbiSet;
 use syntax::ast_map;
 use syntax::oldvisit;
 
@@ -240,20 +242,20 @@ pub fn trans_fn_ref_with_vtables(
         type_params: &[ty::t], // values for fn's ty params
         vtables: Option<typeck::vtable_res>) // vtables for the call
      -> FnData {
-    //!
-    //
-    // Translates a reference to a fn/method item, monomorphizing and
-    // inlining as it goes.
-    //
-    // # Parameters
-    //
-    // - `bcx`: the current block where the reference to the fn occurs
-    // - `def_id`: def id of the fn or method item being referenced
-    // - `ref_id`: node id of the reference to the fn/method, if applicable.
-    //   This parameter may be zero; but, if so, the resulting value may not
-    //   have the right type, so it must be cast before being used.
-    // - `type_params`: values for each of the fn/method's type parameters
-    // - `vtables`: values for each bound on each of the type parameters
+    /*!
+     * Translates a reference to a fn/method item, monomorphizing and
+     * inlining as it goes.
+     *
+     * # Parameters
+     *
+     * - `bcx`: the current block where the reference to the fn occurs
+     * - `def_id`: def id of the fn or method item being referenced
+     * - `ref_id`: node id of the reference to the fn/method, if applicable.
+     *   This parameter may be zero; but, if so, the resulting value may not
+     *   have the right type, so it must be cast before being used.
+     * - `type_params`: values for each of the fn/method's type parameters
+     * - `vtables`: values for each bound on each of the type parameters
+     */
 
     let _icx = push_ctxt("trans_fn_ref_with_vtables");
     let ccx = bcx.ccx();
@@ -386,7 +388,7 @@ pub fn trans_fn_ref_with_vtables(
     }
 
     // Find the actual function pointer.
-    let val = {
+    let mut val = {
         if def_id.crate == ast::LOCAL_CRATE {
             // Internal reference.
             get_item_val(ccx, def_id.node)
@@ -396,6 +398,35 @@ pub fn trans_fn_ref_with_vtables(
         }
     };
 
+    // This is subtle and surprising, but sometimes we have to bitcast
+    // the resulting fn pointer.  The reason has to do with external
+    // functions.  If you have two crates that both bind the same C
+    // library, they may not use precisely the same types: for
+    // example, they will probably each declare their own structs,
+    // which are distinct types from LLVM's point of view (nominal
+    // types).
+    //
+    // Now, if those two crates are linked into an application, and
+    // they contain inlined code, you can wind up with a situation
+    // where both of those functions wind up being loaded into this
+    // application simultaneously. In that case, the same function
+    // (from LLVM's point of view) requires two types. But of course
+    // LLVM won't allow one function to have two types.
+    //
+    // What we currently do, therefore, is declare the function with
+    // one of the two types (whichever happens to come first) and then
+    // bitcast as needed when the function is referenced to make sure
+    // it has the type we expect.
+    //
+    // This can occur on either a crate-local or crate-external
+    // reference. It also occurs when testing libcore and in some
+    // other weird situations. Annoying.
+    let llty = type_of::type_of_fn_from_ty(ccx, fn_tpt.ty);
+    let llptrty = llty.ptr_to();
+    if val_ty(val) != llptrty {
+        val = BitCast(bcx, val, llptrty);
+    }
+
     return FnData {llfn: val};
 }
 
@@ -543,16 +574,26 @@ pub fn body_contains_ret(body: &ast::Block) -> bool {
     *cx
 }
 
-// See [Note-arg-mode]
 pub fn trans_call_inner(in_cx: @mut Block,
                         call_info: Option<NodeInfo>,
-                        fn_expr_ty: ty::t,
+                        callee_ty: ty::t,
                         ret_ty: ty::t,
                         get_callee: &fn(@mut Block) -> Callee,
                         args: CallArgs,
                         dest: Option<expr::Dest>,
                         autoref_arg: AutorefArg)
                         -> Result {
+    /*!
+     * This behemoth of a function translates function calls.
+     * Unfortunately, in order to generate more efficient LLVM
+     * output at -O0, it has quite a complex signature (refactoring
+     * this into two functions seems like a good idea).
+     *
+     * In particular, for lang items, it is invoked with a dest of
+     * None, and
+     */
+
+
     do base::with_scope_result(in_cx, call_info, "call") |cx| {
         let callee = get_callee(cx);
         let mut bcx = callee.bcx;
@@ -580,98 +621,125 @@ pub fn trans_call_inner(in_cx: @mut Block,
             }
         };
 
-        let llretslot = trans_ret_slot(bcx, fn_expr_ty, dest);
+        let abi = match ty::get(callee_ty).sty {
+            ty::ty_bare_fn(ref f) => f.abis,
+            _ => AbiSet::Rust()
+        };
+        let is_rust_fn =
+            abi.is_rust() ||
+            abi.is_intrinsic();
+
+        // Generate a location to store the result. If the user does
+        // not care about the result, just make a stack slot.
+        let opt_llretslot = match dest {
+            None => {
+                assert!(!type_of::return_uses_outptr(in_cx.tcx(), ret_ty));
+                None
+            }
+            Some(expr::SaveIn(dst)) => Some(dst),
+            Some(expr::Ignore) => {
+                if !ty::type_is_voidish(ret_ty) {
+                    Some(alloc_ty(bcx, ret_ty, "__llret"))
+                } else {
+                    unsafe {
+                        Some(llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref()))
+                    }
+                }
+            }
+        };
 
-        let mut llargs = ~[];
+        let mut llresult = unsafe {
+            llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())
+        };
 
-        if !ty::type_is_immediate(bcx.tcx(), ret_ty) {
-            llargs.push(llretslot);
-        }
+        // The code below invokes the function, using either the Rust
+        // conventions (if it is a rust fn) or the native conventions
+        // (otherwise).  The important part is that, when all is sad
+        // and done, either the return value of the function will have been
+        // written in opt_llretslot (if it is Some) or `llresult` will be
+        // set appropriately (otherwise).
+        if is_rust_fn {
+            let mut llargs = ~[];
+
+            // Push the out-pointer if we use an out-pointer for this
+            // return type, otherwise push "undef".
+            if type_of::return_uses_outptr(in_cx.tcx(), ret_ty) {
+                llargs.push(opt_llretslot.unwrap());
+            }
+
+            // Push the environment.
+            llargs.push(llenv);
 
-        llargs.push(llenv);
-        bcx = trans_args(bcx, args, fn_expr_ty, autoref_arg, &mut llargs);
+            // Push the arguments.
+            bcx = trans_args(bcx, args, callee_ty,
+                             autoref_arg, &mut llargs);
 
-        // Now that the arguments have finished evaluating, we need to revoke
-        // the cleanup for the self argument
-        match callee.data {
-            Method(d) => {
-                for &v in d.temp_cleanup.iter() {
-                    revoke_clean(bcx, v);
+            // Now that the arguments have finished evaluating, we
+            // need to revoke the cleanup for the self argument
+            match callee.data {
+                Method(d) => {
+                    for &v in d.temp_cleanup.iter() {
+                        revoke_clean(bcx, v);
+                    }
                 }
+                _ => {}
             }
-            _ => {}
-        }
 
-        // Uncomment this to debug calls.
-        /*
-        printfln!("calling: %s", bcx.val_to_str(llfn));
-        for llarg in llargs.iter() {
-            printfln!("arg: %s", bcx.val_to_str(*llarg));
+            // Invoke the actual rust fn and update bcx/llresult.
+            let (llret, b) = base::invoke(bcx, llfn, llargs);
+            bcx = b;
+            llresult = llret;
+
+            // If the Rust convention for this type is return via
+            // the return value, copy it into llretslot.
+            match opt_llretslot {
+                Some(llretslot) => {
+                    if !type_of::return_uses_outptr(bcx.tcx(), ret_ty) &&
+                        !ty::type_is_voidish(ret_ty)
+                    {
+                        Store(bcx, llret, llretslot);
+                    }
+                }
+                None => {}
+            }
+        } else {
+            // Lang items are the only case where dest is None, and
+            // they are always Rust fns.
+            assert!(dest.is_some());
+
+            let mut llargs = ~[];
+            bcx = trans_args(bcx, args, callee_ty,
+                             autoref_arg, &mut llargs);
+            bcx = foreign::trans_native_call(bcx, callee_ty,
+                                             llfn, opt_llretslot.unwrap(), llargs);
         }
-        io::println("---");
-        */
-
-        // If the block is terminated, then one or more of the args
-        // has type _|_. Since that means it diverges, the code for
-        // the call itself is unreachable.
-        let (llresult, new_bcx) = base::invoke(bcx, llfn, llargs);
-        bcx = new_bcx;
 
+        // If the caller doesn't care about the result of this fn call,
+        // drop the temporary slot we made.
         match dest {
-            None => { assert!(ty::type_is_immediate(bcx.tcx(), ret_ty)) }
+            None => {
+                assert!(!type_of::return_uses_outptr(bcx.tcx(), ret_ty));
+            }
             Some(expr::Ignore) => {
                 // drop the value if it is not being saved.
-                if ty::type_needs_drop(bcx.tcx(), ret_ty) {
-                    if ty::type_is_immediate(bcx.tcx(), ret_ty) {
-                        let llscratchptr = alloc_ty(bcx, ret_ty, "__ret");
-                        Store(bcx, llresult, llscratchptr);
-                        bcx = glue::drop_ty(bcx, llscratchptr, ret_ty);
-                    } else {
-                        bcx = glue::drop_ty(bcx, llretslot, ret_ty);
-                    }
-                }
-            }
-            Some(expr::SaveIn(lldest)) => {
-                // If this is an immediate, store into the result location.
-                // (If this was not an immediate, the result will already be
-                // directly written into the output slot.)
-                if ty::type_is_immediate(bcx.tcx(), ret_ty) {
-                    Store(bcx, llresult, lldest);
-                }
+                bcx = glue::drop_ty(bcx, opt_llretslot.unwrap(), ret_ty);
             }
+            Some(expr::SaveIn(_)) => { }
         }
 
         if ty::type_is_bot(ret_ty) {
             Unreachable(bcx);
         }
+
         rslt(bcx, llresult)
     }
 }
 
-
 pub enum CallArgs<'self> {
     ArgExprs(&'self [@ast::expr]),
     ArgVals(&'self [ValueRef])
 }
 
-pub fn trans_ret_slot(bcx: @mut Block, fn_ty: ty::t, dest: Option<expr::Dest>)
-                      -> ValueRef {
-    let retty = ty::ty_fn_ret(fn_ty);
-
-    match dest {
-        Some(expr::SaveIn(dst)) => dst,
-        _ => {
-            if ty::type_is_immediate(bcx.tcx(), retty) {
-                unsafe {
-                    llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())
-                }
-            } else {
-                alloc_ty(bcx, retty, "__trans_ret_slot")
-            }
-        }
-    }
-}
-
 pub fn trans_args(cx: @mut Block,
                   args: CallArgs,
                   fn_ty: ty::t,
@@ -795,7 +863,7 @@ pub fn trans_arg_expr(bcx: @mut Block,
 
         if formal_arg_ty != arg_datum.ty {
             // this could happen due to e.g. subtyping
-            let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, &formal_arg_ty);
+            let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
             debug!("casting actual type (%s) to match formal (%s)",
                    bcx.val_to_str(val), bcx.llty_str(llformal_arg_ty));
             val = PointerCast(bcx, val, llformal_arg_ty);
index 4ca4a9a12fc4c96ba7355e2e90cb647139169c65..027696d37f128746950816a78c19672537731df1 100644 (file)
@@ -121,7 +121,7 @@ pub fn BuilderRef_res(B: BuilderRef) -> BuilderRef_res {
     }
 }
 
-pub type ExternMap = HashMap<@str, ValueRef>;
+pub type ExternMap = HashMap<~str, ValueRef>;
 
 // Types used for llself.
 pub struct ValSelfData {
@@ -197,10 +197,10 @@ pub struct FunctionContext {
     // outputting the resume instruction.
     personality: Option<ValueRef>,
 
-    // True if this function has an immediate return value, false otherwise.
-    // If this is false, the llretptr will alias the first argument of the
-    // function.
-    has_immediate_return_value: bool,
+    // True if the caller expects this fn to use the out pointer to
+    // return. Either way, your code should write into llretptr, but if
+    // this value is false, llretptr will be a local alloca.
+    caller_expects_out_pointer: bool,
 
     // Maps arguments to allocas created for them in llallocas.
     llargs: @mut HashMap<ast::NodeId, ValueRef>,
@@ -232,20 +232,20 @@ pub struct FunctionContext {
 
 impl FunctionContext {
     pub fn arg_pos(&self, arg: uint) -> uint {
-        if self.has_immediate_return_value {
-            arg + 1u
-        } else {
+        if self.caller_expects_out_pointer {
             arg + 2u
+        } else {
+            arg + 1u
         }
     }
 
     pub fn out_arg_pos(&self) -> uint {
-        assert!(!self.has_immediate_return_value);
+        assert!(self.caller_expects_out_pointer);
         0u
     }
 
     pub fn env_arg_pos(&self) -> uint {
-        if !self.has_immediate_return_value {
+        if self.caller_expects_out_pointer {
             1u
         } else {
             0u
index c8a09ce87c0a15beeb32eb8ad4735a3ea02619b6..1060a06a5cfa9d5a6b9a1511c7df0a65ab8ce014 100644 (file)
@@ -190,12 +190,12 @@ pub fn scratch_datum(bcx: @mut Block, ty: ty::t, name: &str, zero: bool) -> Datu
 
 pub fn appropriate_mode(tcx: ty::ctxt, ty: ty::t) -> DatumMode {
     /*!
-    *
-    * Indicates the "appropriate" mode for this value,
-    * which is either by ref or by value, depending
-    * on whether type is immediate or not. */
+     * Indicates the "appropriate" mode for this value,
+     * which is either by ref or by value, depending
+     * on whether type is immediate or not.
+     */
 
-    if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
+    if ty::type_is_voidish(ty) {
         ByValue
     } else if ty::type_is_immediate(tcx, ty) {
         ByValue
@@ -271,7 +271,7 @@ pub fn copy_to(&self, bcx: @mut Block, action: CopyAction, dst: ValueRef)
 
         let _icx = push_ctxt("copy_to");
 
-        if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+        if ty::type_is_voidish(self.ty) {
             return bcx;
         }
 
@@ -343,7 +343,7 @@ pub fn move_to(&self, bcx: @mut Block, action: CopyAction, dst: ValueRef)
         debug!("move_to(self=%s, action=%?, dst=%s)",
                self.to_str(bcx.ccx()), action, bcx.val_to_str(dst));
 
-        if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+        if ty::type_is_voidish(self.ty) {
             return bcx;
         }
 
@@ -432,7 +432,7 @@ pub fn to_value_llval(&self, bcx: @mut Block) -> ValueRef {
          *
          * Yields the value itself. */
 
-        if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+        if ty::type_is_voidish(self.ty) {
             C_nil()
         } else {
             match self.mode {
@@ -469,7 +469,7 @@ pub fn to_ref_llval(&self, bcx: @mut Block) -> ValueRef {
         match self.mode {
             ByRef(_) => self.val,
             ByValue => {
-                if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+                if ty::type_is_voidish(self.ty) {
                     C_null(type_of::type_of(bcx.ccx(), self.ty).ptr_to())
                 } else {
                     let slot = alloc_ty(bcx, self.ty, "");
index 04fd477a317380d2eee4a560eeb9c482e0f24bca..2ce8756848fa818b710307d5ece1e98a99d3cd3a 100644 (file)
@@ -290,7 +290,7 @@ fn add_env(bcx: @mut Block, expr: &ast::expr, datum: Datum) -> DatumBlock {
         assert_eq!(datum.appropriate_mode(tcx), ByValue);
         Store(bcx, datum.to_appropriate_llval(bcx), llfn);
         let llenv = GEPi(bcx, scratch.val, [0u, abi::fn_field_box]);
-        Store(bcx, base::null_env_ptr(bcx), llenv);
+        Store(bcx, base::null_env_ptr(bcx.ccx()), llenv);
         DatumBlock {bcx: bcx, datum: scratch}
     }
 
@@ -416,7 +416,7 @@ pub fn trans_into(bcx: @mut Block, expr: @ast::expr, dest: Dest) -> @mut Block {
     debuginfo::update_source_pos(bcx.fcx, expr.id, expr.span);
 
     let dest = {
-        if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
+        if ty::type_is_voidish(ty) {
             Ignore
         } else {
             dest
@@ -507,7 +507,7 @@ fn trans_to_datum_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock {
 
         ty::RvalueDpsExpr => {
             let ty = expr_ty(bcx, expr);
-            if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
+            if ty::type_is_voidish(ty) {
                 bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
                 return nil(bcx, ty);
             } else {
index 7694f690286b77c7c83edb15f3c56fbcc9144b0b..5586f1183ef87257539f97f63d1f9ec98f0489cc 100644 (file)
 // except according to those terms.
 
 
-use back::{link, abi};
-use lib::llvm::{Pointer, ValueRef};
+use back::{link};
+use std::libc::c_uint;
+use lib::llvm::{ValueRef, Attribute, CallConv};
+use lib::llvm::llvm;
 use lib;
-use middle::trans::base::*;
+use middle::trans::machine;
+use middle::trans::base;
+use middle::trans::base::push_ctxt;
 use middle::trans::cabi;
-use middle::trans::cabi_x86;
-use middle::trans::cabi_x86_64;
-use middle::trans::cabi_arm;
-use middle::trans::cabi_mips;
 use middle::trans::build::*;
-use middle::trans::callee::*;
+use middle::trans::builder::noname;
 use middle::trans::common::*;
-use middle::trans::datum::*;
-use middle::trans::expr::Ignore;
-use middle::trans::machine::llsize_of;
-use middle::trans::glue;
-use middle::trans::machine;
 use middle::trans::type_of::*;
 use middle::trans::type_of;
 use middle::ty;
 use middle::ty::FnSig;
-use util::ppaux::ty_to_str;
 
-use std::cell::Cell;
+use std::uint;
 use std::vec;
 use syntax::codemap::span;
-use syntax::{ast, ast_util};
+use syntax::{ast};
 use syntax::{attr, ast_map};
-use syntax::opt_vec;
 use syntax::parse::token::special_idents;
-use syntax::parse::token;
-use syntax::abi::{X86, X86_64, Arm, Mips};
 use syntax::abi::{RustIntrinsic, Rust, Stdcall, Fastcall,
-                  Cdecl, Aapcs, C};
+                  Cdecl, Aapcs, C, AbiSet};
+use util::ppaux::{Repr, UserString};
 use middle::trans::type_::Type;
 
-fn abi_info(ccx: @mut CrateContext) -> @cabi::ABIInfo {
-    return match ccx.sess.targ_cfg.arch {
-        X86 => cabi_x86::abi_info(ccx),
-        X86_64 => cabi_x86_64::abi_info(),
-        Arm => cabi_arm::abi_info(),
-        Mips => cabi_mips::abi_info(),
-    }
-}
-
-pub fn link_name(ccx: &CrateContext, i: &ast::foreign_item) -> @str {
-     match attr::first_attr_value_str_by_name(i.attrs, "link_name") {
-        None => ccx.sess.str_of(i.ident),
-        Some(ln) => ln,
-    }
-}
+///////////////////////////////////////////////////////////////////////////
+// Type definitions
 
-struct ShimTypes {
+struct ForeignTypes {
+    /// Rust signature of the function
     fn_sig: ty::FnSig,
 
+    /// 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,
 
     /// True if there is a return value (not bottom, not unit)
     ret_def: bool,
-
-    /// Type of the struct we will use to shuttle values back and forth.
-    /// This is always derived from the llsig.
-    bundle_ty: Type,
-
-    /// Type of the shim function itself.
-    shim_fn_ty: Type,
-
-    /// Adapter object for handling native ABI rules (trust me, you
-    /// don't want to know).
-    fn_ty: cabi::FnType
 }
 
 struct LlvmSignature {
+    // LLVM versions of the types of this function's arguments.
     llarg_tys: ~[Type],
-    llret_ty: Type,
-    sret: bool,
-}
 
-fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
-                     -> LlvmSignature {
-    /*!
-     * 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.
-     */
+    // 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,
 
-    let llarg_tys = fn_sig.inputs.map(|arg_ty| type_of(ccx, *arg_ty));
-    let llret_ty = type_of::type_of(ccx, fn_sig.output);
-    LlvmSignature {
-        llarg_tys: llarg_tys,
-        llret_ty: llret_ty,
-        sret: !ty::type_is_immediate(ccx.tcx, fn_sig.output),
-    }
+    // True if *Rust* would use an outpointer for this function.
+    sret: bool,
 }
 
-fn shim_types(ccx: @mut CrateContext, id: ast::NodeId) -> ShimTypes {
-    let fn_sig = match ty::get(ty::node_id_to_type(ccx.tcx, id)).sty {
-        ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(),
-        _ => ccx.sess.bug("c_arg_and_ret_lltys called on non-function type")
-    };
-    let llsig = foreign_signature(ccx, &fn_sig);
-    let bundle_ty = Type::struct_(llsig.llarg_tys + &[llsig.llret_ty.ptr_to()], false);
-    let ret_def = !ty::type_is_bot(fn_sig.output) &&
-                  !ty::type_is_nil(fn_sig.output);
-    let fn_ty = abi_info(ccx).compute_info(llsig.llarg_tys, llsig.llret_ty, ret_def);
-    ShimTypes {
-        fn_sig: fn_sig,
-        llsig: llsig,
-        ret_def: ret_def,
-        bundle_ty: bundle_ty,
-        shim_fn_ty: Type::func([bundle_ty.ptr_to()], &Type::void()),
-        fn_ty: fn_ty
-    }
-}
 
-type shim_arg_builder<'self> =
-    &'self fn(bcx: @mut Block, tys: &ShimTypes,
-              llargbundle: ValueRef) -> ~[ValueRef];
-
-type shim_ret_builder<'self> =
-    &'self fn(bcx: @mut Block, tys: &ShimTypes,
-              llargbundle: ValueRef,
-              llretval: ValueRef);
-
-fn build_shim_fn_(ccx: @mut CrateContext,
-                  shim_name: &str,
-                  llbasefn: ValueRef,
-                  tys: &ShimTypes,
-                  cc: lib::llvm::CallConv,
-                  arg_builder: shim_arg_builder,
-                  ret_builder: shim_ret_builder)
-               -> ValueRef {
-    let llshimfn = decl_internal_cdecl_fn(
-        ccx.llmod, shim_name, tys.shim_fn_ty);
-
-    // Declare the body of the shim function:
-    let fcx = new_fn_ctxt(ccx, ~[], llshimfn, tys.fn_sig.output, None);
-    let bcx = fcx.entry_bcx.unwrap();
-
-    let llargbundle = get_param(llshimfn, 0u);
-    let llargvals = arg_builder(bcx, tys, llargbundle);
-
-    // Create the call itself and store the return value:
-    let llretval = CallWithConv(bcx, llbasefn, llargvals, cc);
-
-    ret_builder(bcx, tys, llargbundle, llretval);
-
-    // Don't finish up the function in the usual way, because this doesn't
-    // follow the normal Rust calling conventions.
-    let ret_cx = match fcx.llreturn {
-        Some(llreturn) => raw_block(fcx, false, llreturn),
-        None => bcx
-    };
-    RetVoid(ret_cx);
-    fcx.cleanup();
+///////////////////////////////////////////////////////////////////////////
+// Calls to external functions
 
-    return llshimfn;
-}
+fn llvm_calling_convention(ccx: @mut CrateContext,
+                           abis: AbiSet)
+                           -> Option<CallConv> {
+    let arch = ccx.sess.targ_cfg.arch;
+    abis.for_arch(arch).map(|abi| {
+        match *abi {
+            RustIntrinsic => {
+                // Intrinsics are emitted by monomorphic fn
+                ccx.sess.bug(fmt!("Asked to register intrinsic fn"));
+            }
 
-type wrap_arg_builder<'self> = &'self fn(bcx: @mut Block,
-                                         tys: &ShimTypes,
-                                         llwrapfn: ValueRef,
-                                         llargbundle: ValueRef);
-
-type wrap_ret_builder<'self> = &'self fn(bcx: @mut Block,
-                                         tys: &ShimTypes,
-                                         llargbundle: ValueRef);
-
-fn build_wrap_fn_(ccx: @mut CrateContext,
-                  tys: &ShimTypes,
-                  llshimfn: ValueRef,
-                  llwrapfn: ValueRef,
-                  shim_upcall: ValueRef,
-                  needs_c_return: bool,
-                  arg_builder: wrap_arg_builder,
-                  ret_builder: wrap_ret_builder) {
-    let _icx = push_ctxt("foreign::build_wrap_fn_");
-    let fcx = new_fn_ctxt(ccx, ~[], llwrapfn, tys.fn_sig.output, None);
-    let bcx = fcx.entry_bcx.unwrap();
-
-    // Patch up the return type if it's not immediate and we're returning via
-    // the C ABI.
-    if needs_c_return && !ty::type_is_immediate(ccx.tcx, tys.fn_sig.output) {
-        let lloutputtype = type_of::type_of(fcx.ccx, tys.fn_sig.output);
-        fcx.llretptr = Some(alloca(bcx, lloutputtype, ""));
-    }
+            Rust => {
+                // FIXME(#3678) Implement linking to foreign fns with Rust ABI
+                ccx.sess.unimpl(
+                    fmt!("Foreign functions with Rust ABI"));
+            }
 
-    // Allocate the struct and write the arguments into it.
-    let llargbundle = alloca(bcx, tys.bundle_ty, "__llargbundle");
-    arg_builder(bcx, tys, llwrapfn, llargbundle);
+            Stdcall => lib::llvm::X86StdcallCallConv,
+            Fastcall => lib::llvm::X86FastcallCallConv,
+            C => lib::llvm::CCallConv,
 
-    // Create call itself.
-    let llshimfnptr = PointerCast(bcx, llshimfn, Type::i8p());
-    let llrawargbundle = PointerCast(bcx, llargbundle, Type::i8p());
-    Call(bcx, shim_upcall, [llrawargbundle, llshimfnptr]);
-    ret_builder(bcx, tys, llargbundle);
+            // NOTE These API constants ought to be more specific
+            Cdecl => lib::llvm::CCallConv,
+            Aapcs => lib::llvm::CCallConv,
+        }
+    })
+}
 
-    // Then return according to the C ABI.
-    let return_context = match fcx.llreturn {
-        Some(llreturn) => raw_block(fcx, false, llreturn),
-        None => bcx
-    };
 
-    let llfunctiontype = val_ty(llwrapfn);
-    let llfunctiontype = llfunctiontype.element_type();
-    let return_type = llfunctiontype.return_type();
-    if return_type.kind() == ::lib::llvm::Void {
-        // XXX: This might be wrong if there are any functions for which
-        // the C ABI specifies a void output pointer and the Rust ABI
-        // does not.
-        RetVoid(return_context);
-    } else {
-        // Cast if we have to...
-        // XXX: This is ugly.
-        let llretptr = BitCast(return_context, fcx.llretptr.unwrap(), return_type.ptr_to());
-        Ret(return_context, Load(return_context, llretptr));
-    }
-    fcx.cleanup();
-}
+pub fn register_foreign_item_fn(ccx: @mut CrateContext,
+                                abis: AbiSet,
+                                path: &ast_map::path,
+                                foreign_item: @ast::foreign_item) -> ValueRef {
+    /*!
+     * Registers a foreign function found in a library.
+     * Just adds a LLVM global.
+     */
 
-// For each foreign function F, we generate a wrapper function W and a shim
-// function S that all work together.  The wrapper function W is the function
-// that other rust code actually invokes.  Its job is to marshall the
-// arguments into a struct.  It then uses a small bit of assembly to switch
-// over to the C stack and invoke the shim function.  The shim function S then
-// unpacks the arguments from the struct and invokes the actual function F
-// according to its specified calling convention.
-//
-// Example: Given a foreign c-stack function F(x: X, y: Y) -> Z,
-// we generate a wrapper function W that looks like:
-//
-//    void W(Z* dest, void *env, X x, Y y) {
-//        struct { X x; Y y; Z *z; } args = { x, y, z };
-//        call_on_c_stack_shim(S, &args);
-//    }
-//
-// The shim function S then looks something like:
-//
-//     void S(struct { X x; Y y; Z *z; } *args) {
-//         *args->z = F(args->x, args->y);
-//     }
-//
-// However, if the return type of F is dynamically sized or of aggregate type,
-// the shim function looks like:
-//
-//     void S(struct { X x; Y y; Z *z; } *args) {
-//         F(args->z, args->x, args->y);
-//     }
-//
-// Note: on i386, the layout of the args struct is generally the same
-// as the desired layout of the arguments on the C stack.  Therefore,
-// we could use upcall_alloc_c_stack() to allocate the `args`
-// structure and switch the stack pointer appropriately to avoid a
-// round of copies.  (In fact, the shim function itself is
-// unnecessary). We used to do this, in fact, and will perhaps do so
-// in the future.
-pub fn trans_foreign_mod(ccx: @mut CrateContext,
-                         path: &ast_map::path,
-                         foreign_mod: &ast::foreign_mod) {
-    let _icx = push_ctxt("foreign::trans_foreign_mod");
+    debug!("register_foreign_item_fn(abis=%s, \
+            path=%s, \
+            foreign_item.id=%?)",
+           abis.repr(ccx.tcx),
+           path.repr(ccx.tcx),
+           foreign_item.id);
 
-    let arch = ccx.sess.targ_cfg.arch;
-    let abi = match foreign_mod.abis.for_arch(arch) {
+    let cc = match llvm_calling_convention(ccx, abis) {
+        Some(cc) => cc,
         None => {
+            // FIXME(#8357) We really ought to report a span here
             ccx.sess.fatal(
-                fmt!("No suitable ABI for target architecture \
+                fmt!("ABI `%s` has no suitable ABI \
+                      for target architecture \
                       in module %s",
+                     abis.user_string(ccx.tcx),
                      ast_map::path_to_str(*path,
                                           ccx.sess.intr())));
         }
-
-        Some(abi) => abi,
     };
 
-    for &foreign_item in foreign_mod.items.iter() {
-        match foreign_item.node {
-            ast::foreign_item_fn(*) => {
-                let id = foreign_item.id;
-                match abi {
-                    RustIntrinsic => {
-                        // Intrinsics are emitted by monomorphic fn
-                    }
-
-                    Rust => {
-                        // FIXME(#3678) Implement linking to foreign fns with Rust ABI
-                        ccx.sess.unimpl(
-                            fmt!("Foreign functions with Rust ABI"));
-                    }
-
-                    Stdcall => {
-                        build_foreign_fn(ccx, id, foreign_item,
-                                         lib::llvm::X86StdcallCallConv);
-                    }
-
-                    Fastcall => {
-                        build_foreign_fn(ccx, id, foreign_item,
-                                         lib::llvm::X86FastcallCallConv);
-                    }
-
-                    Cdecl => {
-                        // FIXME(#3678) should really be more specific
-                        build_foreign_fn(ccx, id, foreign_item,
-                                         lib::llvm::CCallConv);
-                    }
-
-                    Aapcs => {
-                        // FIXME(#3678) should really be more specific
-                        build_foreign_fn(ccx, id, foreign_item,
-                                         lib::llvm::CCallConv);
-                    }
-
-                    C => {
-                        build_foreign_fn(ccx, id, foreign_item,
-                                         lib::llvm::CCallConv);
-                    }
-                }
-            }
-            ast::foreign_item_static(*) => {
-                let ident = token::ident_to_str(&foreign_item.ident);
-                ccx.item_symbols.insert(foreign_item.id, /* bad */ident.to_owned());
-            }
-        }
-    }
+    // Register the function as a C extern fn
+    let lname = link_name(ccx, foreign_item);
+    let tys = foreign_types_for_id(ccx, foreign_item.id);
 
-    fn build_foreign_fn(ccx: @mut CrateContext,
-                        id: ast::NodeId,
-                        foreign_item: @ast::foreign_item,
-                        cc: lib::llvm::CallConv) {
-        let llwrapfn = get_item_val(ccx, id);
-        let tys = shim_types(ccx, id);
-        if attr::contains_name(foreign_item.attrs, "rust_stack") {
-            build_direct_fn(ccx, llwrapfn, foreign_item,
-                            &tys, cc);
-        } else if attr::contains_name(foreign_item.attrs, "fast_ffi") {
-            build_fast_ffi_fn(ccx, llwrapfn, foreign_item, &tys, cc);
-        } else {
-            let llshimfn = build_shim_fn(ccx, foreign_item, &tys, cc);
-            build_wrap_fn(ccx, &tys, llshimfn, llwrapfn);
-        }
-    }
+    // Create the LLVM value for the C extern fn
+    let llfn_ty = lltype_for_fn_from_foreign_types(&tys);
+    let llfn = base::get_extern_fn(&mut ccx.externs, ccx.llmod,
+                                   lname, cc, llfn_ty);
+    add_argument_attributes(&tys, llfn);
 
-    fn build_shim_fn(ccx: @mut CrateContext,
-                     foreign_item: &ast::foreign_item,
-                     tys: &ShimTypes,
-                     cc: lib::llvm::CallConv)
-                  -> ValueRef {
-        /*!
-         *
-         * Build S, from comment above:
-         *
-         *     void S(struct { X x; Y y; Z *z; } *args) {
-         *         F(args->z, args->x, args->y);
-         *     }
-         */
-
-        let _icx = push_ctxt("foreign::build_shim_fn");
-
-        fn build_args(bcx: @mut Block, tys: &ShimTypes, llargbundle: ValueRef)
-                   -> ~[ValueRef] {
-            let _icx = push_ctxt("foreign::shim::build_args");
-            tys.fn_ty.build_shim_args(bcx, tys.llsig.llarg_tys, llargbundle)
-        }
+    return llfn;
+}
 
-        fn build_ret(bcx: @mut Block,
-                     tys: &ShimTypes,
-                     llargbundle: ValueRef,
-                     llretval: ValueRef) {
-            let _icx = push_ctxt("foreign::shim::build_ret");
-            tys.fn_ty.build_shim_ret(bcx,
-                                     tys.llsig.llarg_tys,
-                                     tys.ret_def,
-                                     llargbundle,
-                                     llretval);
-        }
+pub fn trans_native_call(bcx: @mut Block,
+                         callee_ty: ty::t,
+                         llfn: ValueRef,
+                         llretptr: ValueRef,
+                         llargs_rust: &[ValueRef]) -> @mut Block {
+    /*!
+     * 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
+     */
 
-        let lname = link_name(ccx, foreign_item);
-        let llbasefn = base_fn(ccx, lname, tys, cc);
-        // Name the shim function
-        let shim_name = fmt!("%s__c_stack_shim", lname);
-        build_shim_fn_(ccx,
-                       shim_name,
-                       llbasefn,
-                       tys,
-                       cc,
-                       build_args,
-                       build_ret)
-    }
+    let ccx = bcx.ccx();
+    let tcx = bcx.tcx();
 
-    fn base_fn(ccx: &CrateContext,
-               lname: &str,
-               tys: &ShimTypes,
-               cc: lib::llvm::CallConv)
-               -> ValueRef {
-        // Declare the "prototype" for the base function F:
-        do tys.fn_ty.decl_fn |fnty| {
-            decl_fn(ccx.llmod, lname, cc, fnty)
-        }
-    }
+    debug!("trans_native_call(callee_ty=%s, \
+            llfn=%s, \
+            llretptr=%s)",
+           callee_ty.repr(tcx),
+           ccx.tn.val_to_str(llfn),
+           ccx.tn.val_to_str(llretptr));
 
-    // FIXME (#2535): this is very shaky and probably gets ABIs wrong all
-    // over the place
-    fn build_direct_fn(ccx: @mut CrateContext,
-                       decl: ValueRef,
-                       item: &ast::foreign_item,
-                       tys: &ShimTypes,
-                       cc: lib::llvm::CallConv) {
-        debug!("build_direct_fn(%s)", link_name(ccx, item));
-
-        let fcx = new_fn_ctxt(ccx, ~[], decl, tys.fn_sig.output, None);
-        let bcx = fcx.entry_bcx.unwrap();
-        let llbasefn = base_fn(ccx, link_name(ccx, item), tys, cc);
-        let ty = ty::lookup_item_type(ccx.tcx,
-                                      ast_util::local_def(item.id)).ty;
-        let ret_ty = ty::ty_fn_ret(ty);
-        let args = vec::from_fn(ty::ty_fn_args(ty).len(), |i| {
-            get_param(decl, fcx.arg_pos(i))
-        });
-        let retval = Call(bcx, llbasefn, args);
-        if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
-            Store(bcx, retval, fcx.llretptr.unwrap());
+    let (fn_abis, fn_sig) = match ty::get(callee_ty).sty {
+        ty::ty_bare_fn(ref fn_ty) => (fn_ty.abis, fn_ty.sig.clone()),
+        _ => ccx.sess.bug("trans_native_call called on non-function type")
+    };
+    let llsig = foreign_signature(ccx, &fn_sig);
+    let ret_def = !ty::type_is_voidish(fn_sig.output);
+    let fn_type = cabi::compute_abi_info(ccx,
+                                         llsig.llarg_tys,
+                                         llsig.llret_ty,
+                                         ret_def);
+
+    let all_arg_tys: &[cabi::LLVMType] = fn_type.arg_tys;
+    let all_attributes: &[Option<Attribute>] = fn_type.attrs;
+
+    let mut llargs_foreign = ~[];
+
+    // 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}).
+    let (arg_tys, attributes) = {
+        if fn_type.sret {
+            if all_arg_tys[0].cast {
+                let llcastedretptr =
+                    BitCast(bcx, llretptr, all_arg_tys[0].ty.ptr_to());
+                llargs_foreign.push(llcastedretptr);
+            } else {
+                llargs_foreign.push(llretptr);
+            }
+            (all_arg_tys.tail(), all_attributes.tail())
+        } else {
+            (all_arg_tys, all_attributes)
         }
-        finish_fn(fcx, bcx);
-    }
+    };
 
-    // FIXME (#2535): this is very shaky and probably gets ABIs wrong all
-    // over the place
-    fn build_fast_ffi_fn(ccx: @mut CrateContext,
-                         decl: ValueRef,
-                         item: &ast::foreign_item,
-                         tys: &ShimTypes,
-                         cc: lib::llvm::CallConv) {
-        debug!("build_fast_ffi_fn(%s)", link_name(ccx, item));
-
-        let fcx = new_fn_ctxt(ccx, ~[], decl, tys.fn_sig.output, None);
-        let bcx = fcx.entry_bcx.unwrap();
-        let llbasefn = base_fn(ccx, link_name(ccx, item), tys, cc);
-        set_no_inline(fcx.llfn);
-        set_fixed_stack_segment(fcx.llfn);
-        let ty = ty::lookup_item_type(ccx.tcx,
-                                      ast_util::local_def(item.id)).ty;
-        let ret_ty = ty::ty_fn_ret(ty);
-        let args = vec::from_fn(ty::ty_fn_args(ty).len(), |i| {
-            get_param(decl, fcx.arg_pos(i))
-        });
-        let retval = Call(bcx, llbasefn, args);
-        if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
-            Store(bcx, retval, fcx.llretptr.unwrap());
-        }
-        finish_fn(fcx, bcx);
-    }
+    for (i, &llarg_rust) in llargs_rust.iter().enumerate() {
+        let mut llarg_rust = llarg_rust;
 
-    fn build_wrap_fn(ccx: @mut CrateContext,
-                     tys: &ShimTypes,
-                     llshimfn: ValueRef,
-                     llwrapfn: ValueRef) {
-        /*!
-         *
-         * Build W, from comment above:
-         *
-         *     void W(Z* dest, void *env, X x, Y y) {
-         *         struct { X x; Y y; Z *z; } args = { x, y, z };
-         *         call_on_c_stack_shim(S, &args);
-         *     }
-         *
-         * One thing we have to be very careful of is to
-         * account for the Rust modes.
-         */
-
-        let _icx = push_ctxt("foreign::build_wrap_fn");
-
-        build_wrap_fn_(ccx,
-                       tys,
-                       llshimfn,
-                       llwrapfn,
-                       ccx.upcalls.call_shim_on_c_stack,
-                       false,
-                       build_args,
-                       build_ret);
-
-        fn build_args(bcx: @mut Block,
-                      tys: &ShimTypes,
-                      llwrapfn: ValueRef,
-                      llargbundle: ValueRef) {
-            let _icx = push_ctxt("foreign::wrap::build_args");
-            let ccx = bcx.ccx();
-            let n = tys.llsig.llarg_tys.len();
-            for i in range(0u, n) {
-                let arg_i = bcx.fcx.arg_pos(i);
-                let mut llargval = get_param(llwrapfn, arg_i);
-
-                // In some cases, Rust will pass a pointer which the
-                // native C type doesn't have.  In that case, just
-                // load the value from the pointer.
-                if type_of::arg_is_indirect(ccx, &tys.fn_sig.inputs[i]) {
-                    llargval = Load(bcx, llargval);
-                }
+        // Does Rust pass this argument by pointer?
+        let rust_indirect = type_of::arg_is_indirect(ccx, fn_sig.inputs[i]);
 
-                store_inbounds(bcx, llargval, llargbundle, [0u, i]);
-            }
+        debug!("argument %u, llarg_rust=%s, rust_indirect=%b, arg_ty=%s",
+               i,
+               ccx.tn.val_to_str(llarg_rust),
+               rust_indirect,
+               ccx.tn.type_to_str(arg_tys[i].ty));
 
-            for &retptr in bcx.fcx.llretptr.iter() {
-                store_inbounds(bcx, retptr, llargbundle, [0u, n]);
-            }
+        // Ensure that we always have the Rust value indirectly,
+        // because it makes bitcasting easier.
+        if !rust_indirect {
+            let scratch = base::alloca(bcx, arg_tys[i].ty, "__arg");
+            Store(bcx, llarg_rust, scratch);
+            llarg_rust = scratch;
         }
 
-        fn build_ret(bcx: @mut Block,
-                     shim_types: &ShimTypes,
-                     llargbundle: ValueRef) {
-            let _icx = push_ctxt("foreign::wrap::build_ret");
-            let arg_count = shim_types.fn_sig.inputs.len();
-            for &retptr in bcx.fcx.llretptr.iter() {
-                let llretptr = load_inbounds(bcx, llargbundle, [0, arg_count]);
-                Store(bcx, Load(bcx, llretptr), retptr);
-            }
-        }
-    }
-}
+        debug!("llarg_rust=%s (after indirection)",
+               ccx.tn.val_to_str(llarg_rust));
 
-pub fn trans_intrinsic(ccx: @mut CrateContext,
-                       decl: ValueRef,
-                       item: &ast::foreign_item,
-                       path: ast_map::path,
-                       substs: @param_substs,
-                       attributes: &[ast::Attribute],
-                       ref_id: Option<ast::NodeId>) {
-    debug!("trans_intrinsic(item.ident=%s)", ccx.sess.str_of(item.ident));
-
-    fn simple_llvm_intrinsic(bcx: @mut Block, name: &'static str, num_args: uint) {
-        assert!(num_args <= 4);
-        let mut args = [0 as ValueRef, ..4];
-        let first_real_arg = bcx.fcx.arg_pos(0u);
-        for i in range(0u, num_args) {
-            args[i] = get_param(bcx.fcx.llfn, first_real_arg + i);
+        // Check whether we need to do any casting
+        let foreignarg_ty = arg_tys[i].ty;
+        if arg_tys[i].cast {
+            llarg_rust = BitCast(bcx, llarg_rust, foreignarg_ty.ptr_to());
         }
-        let llfn = bcx.ccx().intrinsics.get_copy(&name);
-        Ret(bcx, Call(bcx, llfn, args.slice(0, num_args)));
-    }
-
-    fn with_overflow_instrinsic(bcx: @mut Block, name: &'static str) {
-        let first_real_arg = bcx.fcx.arg_pos(0u);
-        let a = get_param(bcx.fcx.llfn, first_real_arg);
-        let b = get_param(bcx.fcx.llfn, first_real_arg + 1);
-        let llfn = bcx.ccx().intrinsics.get_copy(&name);
-
-        // convert `i1` to a `bool`, and write to the out parameter
-        let val = Call(bcx, llfn, [a, b]);
-        let result = ExtractValue(bcx, val, 0);
-        let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool());
-        let retptr = get_param(bcx.fcx.llfn, bcx.fcx.out_arg_pos());
-        let ret = Load(bcx, retptr);
-        let ret = InsertValue(bcx, ret, result, 0);
-        let ret = InsertValue(bcx, ret, overflow, 1);
-        Store(bcx, ret, retptr);
-        RetVoid(bcx)
-    }
 
-    fn memcpy_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) {
-        let ccx = bcx.ccx();
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        let align = C_i32(machine::llalign_of_min(ccx, lltp_ty) as i32);
-        let size = match sizebits {
-            32 => C_i32(machine::llsize_of_real(ccx, lltp_ty) as i32),
-            64 => C_i64(machine::llsize_of_real(ccx, lltp_ty) as i64),
-            _ => ccx.sess.fatal("Invalid value for sizebits")
-        };
+        debug!("llarg_rust=%s (after casting)",
+               ccx.tn.val_to_str(llarg_rust));
 
-        let decl = bcx.fcx.llfn;
-        let first_real_arg = bcx.fcx.arg_pos(0u);
-        let dst_ptr = PointerCast(bcx, get_param(decl, first_real_arg), Type::i8p());
-        let src_ptr = PointerCast(bcx, get_param(decl, first_real_arg + 1), Type::i8p());
-        let count = get_param(decl, first_real_arg + 2);
-        let volatile = C_i1(false);
-        let llfn = bcx.ccx().intrinsics.get_copy(&name);
-        Call(bcx, llfn, [dst_ptr, src_ptr, Mul(bcx, size, count), align, volatile]);
-        RetVoid(bcx);
-    }
-
-    fn memset_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) {
-        let ccx = bcx.ccx();
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        let align = C_i32(machine::llalign_of_min(ccx, lltp_ty) as i32);
-        let size = match sizebits {
-            32 => C_i32(machine::llsize_of_real(ccx, lltp_ty) as i32),
-            64 => C_i64(machine::llsize_of_real(ccx, lltp_ty) as i64),
-            _ => ccx.sess.fatal("Invalid value for sizebits")
+        // Finally, load the value if needed for the foreign ABI
+        let foreign_indirect = attributes[i].is_some();
+        let llarg_foreign = if foreign_indirect {
+            llarg_rust
+        } else {
+            Load(bcx, llarg_rust)
         };
 
-        let decl = bcx.fcx.llfn;
-        let first_real_arg = bcx.fcx.arg_pos(0u);
-        let dst_ptr = PointerCast(bcx, get_param(decl, first_real_arg), Type::i8p());
-        let val = get_param(decl, first_real_arg + 1);
-        let count = get_param(decl, first_real_arg + 2);
-        let volatile = C_i1(false);
-        let llfn = bcx.ccx().intrinsics.get_copy(&name);
-        Call(bcx, llfn, [dst_ptr, val, Mul(bcx, size, count), align, volatile]);
-        RetVoid(bcx);
-    }
+        debug!("argument %u, llarg_foreign=%s",
+               i, ccx.tn.val_to_str(llarg_foreign));
 
-    fn count_zeros_intrinsic(bcx: @mut Block, name: &'static str) {
-        let x = get_param(bcx.fcx.llfn, bcx.fcx.arg_pos(0u));
-        let y = C_i1(false);
-        let llfn = bcx.ccx().intrinsics.get_copy(&name);
-        Ret(bcx, Call(bcx, llfn, [x, y]));
+        llargs_foreign.push(llarg_foreign);
     }
 
-    let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, item.id));
-
-    let fcx = new_fn_ctxt_w_id(ccx,
-                               path,
-                               decl,
-                               item.id,
-                               output_type,
-                               true,
-                               Some(substs),
-                               None,
-                               Some(item.span));
-
-    set_always_inline(fcx.llfn);
+    let cc = match llvm_calling_convention(ccx, fn_abis) {
+        Some(cc) => cc,
+        None => {
+            // FIXME(#8357) We really ought to report a span here
+            ccx.sess.fatal(
+                fmt!("ABI string `%s` has no suitable ABI \
+                      for target architecture",
+                     fn_abis.user_string(ccx.tcx)));
+        }
+    };
 
-    // Set the fixed stack segment flag if necessary.
-    if attr::contains_name(attributes, "fixed_stack_segment") {
-        set_fixed_stack_segment(fcx.llfn);
-    }
+    let llforeign_retval = CallWithConv(bcx, llfn, llargs_foreign, cc);
 
-    let mut bcx = fcx.entry_bcx.unwrap();
-    let first_real_arg = fcx.arg_pos(0u);
+    // 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 ret_def && !fn_type.sret {
+        let llrust_ret_ty = llsig.llret_ty;
+        let llforeign_ret_ty = fn_type.ret_ty.ty;
 
-    let nm = ccx.sess.str_of(item.ident);
-    let name = nm.as_slice();
+        debug!("llretptr=%s", ccx.tn.val_to_str(llretptr));
+        debug!("llforeign_retval=%s", ccx.tn.val_to_str(llforeign_retval));
+        debug!("llrust_ret_ty=%s", ccx.tn.type_to_str(llrust_ret_ty));
+        debug!("llforeign_ret_ty=%s", ccx.tn.type_to_str(llforeign_ret_ty));
 
-    // This requires that atomic intrinsics follow a specific naming pattern:
-    // "atomic_<operation>[_<ordering>], and no ordering means SeqCst
-    if name.starts_with("atomic_") {
-        let split : ~[&str] = name.split_iter('_').collect();
-        assert!(split.len() >= 2, "Atomic intrinsic not correct format");
-        let order = if split.len() == 2 {
-            lib::llvm::SequentiallyConsistent
+        if llrust_ret_ty == llforeign_ret_ty {
+            Store(bcx, llforeign_retval, llretptr);
         } else {
-            match split[2] {
-                "relaxed" => lib::llvm::Monotonic,
-                "acq"     => lib::llvm::Acquire,
-                "rel"     => lib::llvm::Release,
-                "acqrel"  => lib::llvm::AcquireRelease,
-                _ => ccx.sess.fatal("Unknown ordering in atomic intrinsic")
-            }
-        };
-
-        match split[1] {
-            "cxchg" => {
-                let old = AtomicCmpXchg(bcx, get_param(decl, first_real_arg),
-                                        get_param(decl, first_real_arg + 1u),
-                                        get_param(decl, first_real_arg + 2u),
-                                        order);
-                Ret(bcx, old);
-            }
-            "load" => {
-                let old = AtomicLoad(bcx, get_param(decl, first_real_arg),
-                                     order);
-                Ret(bcx, old);
-            }
-            "store" => {
-                AtomicStore(bcx, get_param(decl, first_real_arg + 1u),
-                            get_param(decl, first_real_arg),
-                            order);
-                RetVoid(bcx);
-            }
-            "fence" => {
-                AtomicFence(bcx, order);
-                RetVoid(bcx);
-            }
-            op => {
-                // These are all AtomicRMW ops
-                let atom_op = match op {
-                    "xchg"  => lib::llvm::Xchg,
-                    "xadd"  => lib::llvm::Add,
-                    "xsub"  => lib::llvm::Sub,
-                    "and"   => lib::llvm::And,
-                    "nand"  => lib::llvm::Nand,
-                    "or"    => lib::llvm::Or,
-                    "xor"   => lib::llvm::Xor,
-                    "max"   => lib::llvm::Max,
-                    "min"   => lib::llvm::Min,
-                    "umax"  => lib::llvm::UMax,
-                    "umin"  => lib::llvm::UMin,
-                    _ => ccx.sess.fatal("Unknown atomic operation")
-                };
-
-                let old = AtomicRMW(bcx, atom_op, get_param(decl, first_real_arg),
-                                    get_param(decl, first_real_arg + 1u),
-                                    order);
-                Ret(bcx, old);
-            }
+            // 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");
+            Store(bcx, llforeign_retval, llscratch);
+            let llscratch_i8 = BitCast(bcx, llscratch, Type::i8().ptr_to());
+            let llretptr_i8 = BitCast(bcx, llretptr, Type::i8().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 = uint::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);
         }
-
-        fcx.cleanup();
-        return;
     }
 
-    match name {
-        "size_of" => {
-            let tp_ty = substs.tys[0];
-            let lltp_ty = type_of::type_of(ccx, tp_ty);
-            Ret(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)));
-        }
-        "move_val" => {
-            // Create a datum reflecting the value being moved.
-            // Use `appropriate_mode` so that the datum is by ref
-            // if the value is non-immediate. Note that, with
-            // intrinsics, there are no argument cleanups to
-            // concern ourselves with.
-            let tp_ty = substs.tys[0];
-            let mode = appropriate_mode(ccx.tcx, tp_ty);
-            let src = Datum {val: get_param(decl, first_real_arg + 1u),
-                             ty: tp_ty, mode: mode};
-            bcx = src.move_to(bcx, DROP_EXISTING,
-                              get_param(decl, first_real_arg));
-            RetVoid(bcx);
-        }
-        "move_val_init" => {
-            // See comments for `"move_val"`.
-            let tp_ty = substs.tys[0];
-            let mode = appropriate_mode(ccx.tcx, tp_ty);
-            let src = Datum {val: get_param(decl, first_real_arg + 1u),
-                             ty: tp_ty, mode: mode};
-            bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
-            RetVoid(bcx);
-        }
-        "min_align_of" => {
-            let tp_ty = substs.tys[0];
-            let lltp_ty = type_of::type_of(ccx, tp_ty);
-            Ret(bcx, C_uint(ccx, machine::llalign_of_min(ccx, lltp_ty)));
-        }
-        "pref_align_of"=> {
-            let tp_ty = substs.tys[0];
-            let lltp_ty = type_of::type_of(ccx, tp_ty);
-            Ret(bcx, C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)));
-        }
-        "get_tydesc" => {
-            let tp_ty = substs.tys[0];
-            let static_ti = get_tydesc(ccx, tp_ty);
-            glue::lazily_emit_all_tydesc_glue(ccx, static_ti);
-
-            // FIXME (#3730): ideally this shouldn't need a cast,
-            // but there's a circularity between translating rust types to llvm
-            // types and having a tydesc type available. So I can't directly access
-            // the llvm type of intrinsic::TyDesc struct.
-            let userland_tydesc_ty = type_of::type_of(ccx, output_type);
-            let td = PointerCast(bcx, static_ti.tydesc, userland_tydesc_ty);
-            Ret(bcx, td);
-        }
-        "init" => {
-            let tp_ty = substs.tys[0];
-            let lltp_ty = type_of::type_of(ccx, tp_ty);
-            match bcx.fcx.llretptr {
-                Some(ptr) => { Store(bcx, C_null(lltp_ty), ptr); RetVoid(bcx); }
-                None if ty::type_is_nil(tp_ty) => RetVoid(bcx),
-                None => Ret(bcx, C_null(lltp_ty)),
-            }
-        }
-        "uninit" => {
-            // Do nothing, this is effectively a no-op
-            let retty = substs.tys[0];
-            if ty::type_is_immediate(ccx.tcx, retty) && !ty::type_is_nil(retty) {
-                unsafe {
-                    Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref()));
-                }
-            } else {
-                RetVoid(bcx)
-            }
-        }
-        "forget" => {
-            RetVoid(bcx);
-        }
-        "transmute" => {
-            let (in_type, out_type) = (substs.tys[0], substs.tys[1]);
-            let llintype = type_of::type_of(ccx, in_type);
-            let llouttype = type_of::type_of(ccx, out_type);
-
-            let in_type_size = machine::llbitsize_of_real(ccx, llintype);
-            let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
-            if in_type_size != out_type_size {
-                let sp = match ccx.tcx.items.get_copy(&ref_id.unwrap()) {
-                    ast_map::node_expr(e) => e.span,
-                    _ => fail!("transmute has non-expr arg"),
-                };
-                let pluralize = |n| if 1u == n { "" } else { "s" };
-                ccx.sess.span_fatal(sp,
-                                    fmt!("transmute called on types with \
-                                          different sizes: %s (%u bit%s) to \
-                                          %s (%u bit%s)",
-                                         ty_to_str(ccx.tcx, in_type),
-                                         in_type_size,
-                                         pluralize(in_type_size),
-                                         ty_to_str(ccx.tcx, out_type),
-                                         out_type_size,
-                                         pluralize(out_type_size)));
-            }
+    return bcx;
+}
 
-            if !ty::type_is_nil(out_type) {
-                let llsrcval = get_param(decl, first_real_arg);
-                if ty::type_is_immediate(ccx.tcx, in_type) {
-                    match fcx.llretptr {
-                        Some(llretptr) => {
-                            Store(bcx, llsrcval, PointerCast(bcx, llretptr, llintype.ptr_to()));
-                            RetVoid(bcx);
-                        }
-                        None => match (llintype.kind(), llouttype.kind()) {
-                            (Pointer, other) | (other, Pointer) if other != Pointer => {
-                                let tmp = Alloca(bcx, llouttype, "");
-                                Store(bcx, llsrcval, PointerCast(bcx, tmp, llintype.ptr_to()));
-                                Ret(bcx, Load(bcx, tmp));
-                            }
-                            _ => Ret(bcx, BitCast(bcx, llsrcval, llouttype))
-                        }
-                    }
-                } else if ty::type_is_immediate(ccx.tcx, out_type) {
-                    let llsrcptr = PointerCast(bcx, llsrcval, llouttype.ptr_to());
-                    Ret(bcx, Load(bcx, llsrcptr));
-                } else {
-                    // NB: Do not use a Load and Store here. This causes massive
-                    // code bloat when `transmute` is used on large structural
-                    // types.
-                    let lldestptr = fcx.llretptr.unwrap();
-                    let lldestptr = PointerCast(bcx, lldestptr, Type::i8p());
-                    let llsrcptr = PointerCast(bcx, llsrcval, Type::i8p());
-
-                    let llsize = llsize_of(ccx, llintype);
-                    call_memcpy(bcx, lldestptr, llsrcptr, llsize, 1);
-                    RetVoid(bcx);
-                };
-            } else {
-                RetVoid(bcx);
-            }
-        }
-        "needs_drop" => {
-            let tp_ty = substs.tys[0];
-            Ret(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)));
-        }
-        "contains_managed" => {
-            let tp_ty = substs.tys[0];
-            Ret(bcx, C_bool(ty::type_contents(ccx.tcx, tp_ty).contains_managed()));
-        }
-        "visit_tydesc" => {
-            let td = get_param(decl, first_real_arg);
-            let visitor = get_param(decl, first_real_arg + 1u);
-            let td = PointerCast(bcx, td, ccx.tydesc_type.ptr_to());
-            glue::call_tydesc_glue_full(bcx, visitor, td,
-                                        abi::tydesc_field_visit_glue, None);
-            RetVoid(bcx);
-        }
-        "frame_address" => {
-            let frameaddress = ccx.intrinsics.get_copy(& &"llvm.frameaddress");
-            let frameaddress_val = Call(bcx, frameaddress, [C_i32(0i32)]);
-            let star_u8 = ty::mk_imm_ptr(
-                bcx.tcx(),
-                ty::mk_mach_uint(ast::ty_u8));
-            let fty = ty::mk_closure(bcx.tcx(), ty::ClosureTy {
-                purity: ast::impure_fn,
-                sigil: ast::BorrowedSigil,
-                onceness: ast::Many,
-                region: ty::re_bound(ty::br_anon(0)),
-                bounds: ty::EmptyBuiltinBounds(),
-                sig: FnSig {
-                    bound_lifetime_names: opt_vec::Empty,
-                    inputs: ~[ star_u8 ],
-                    output: ty::mk_nil()
-                }
-            });
-            let datum = Datum {val: get_param(decl, first_real_arg),
-                               mode: ByRef(ZeroMem), ty: fty};
-            let arg_vals = ~[frameaddress_val];
-            bcx = trans_call_inner(
-                bcx, None, fty, ty::mk_nil(),
-                |bcx| Callee {bcx: bcx, data: Closure(datum)},
-                ArgVals(arg_vals), Some(Ignore), DontAutorefArg).bcx;
-            RetVoid(bcx);
-        }
-        "morestack_addr" => {
-            // XXX This is a hack to grab the address of this particular
-            // native function. There should be a general in-language
-            // way to do this
-            let llfty = type_of_fn(bcx.ccx(), [], ty::mk_nil());
-            let morestack_addr = decl_cdecl_fn(
-                bcx.ccx().llmod, "__morestack", llfty);
-            let morestack_addr = PointerCast(bcx, morestack_addr, Type::nil().ptr_to());
-            Ret(bcx, morestack_addr);
-        }
-        "offset" => {
-            let ptr = get_param(decl, first_real_arg);
-            let offset = get_param(decl, first_real_arg + 1);
-            Ret(bcx, GEP(bcx, ptr, [offset]));
-        }
-        "offset_inbounds" => {
-            let ptr = get_param(decl, first_real_arg);
-            let offset = get_param(decl, first_real_arg + 1);
-            Ret(bcx, InBoundsGEP(bcx, ptr, [offset]));
-        }
-        "memcpy32" => memcpy_intrinsic(bcx, "llvm.memcpy.p0i8.p0i8.i32", substs.tys[0], 32),
-        "memcpy64" => memcpy_intrinsic(bcx, "llvm.memcpy.p0i8.p0i8.i64", substs.tys[0], 64),
-        "memmove32" => memcpy_intrinsic(bcx, "llvm.memmove.p0i8.p0i8.i32", substs.tys[0], 32),
-        "memmove64" => memcpy_intrinsic(bcx, "llvm.memmove.p0i8.p0i8.i64", substs.tys[0], 64),
-        "memset32" => memset_intrinsic(bcx, "llvm.memset.p0i8.i32", substs.tys[0], 32),
-        "memset64" => memset_intrinsic(bcx, "llvm.memset.p0i8.i64", substs.tys[0], 64),
-        "sqrtf32" => simple_llvm_intrinsic(bcx, "llvm.sqrt.f32", 1),
-        "sqrtf64" => simple_llvm_intrinsic(bcx, "llvm.sqrt.f64", 1),
-        "powif32" => simple_llvm_intrinsic(bcx, "llvm.powi.f32", 2),
-        "powif64" => simple_llvm_intrinsic(bcx, "llvm.powi.f64", 2),
-        "sinf32" => simple_llvm_intrinsic(bcx, "llvm.sin.f32", 1),
-        "sinf64" => simple_llvm_intrinsic(bcx, "llvm.sin.f64", 1),
-        "cosf32" => simple_llvm_intrinsic(bcx, "llvm.cos.f32", 1),
-        "cosf64" => simple_llvm_intrinsic(bcx, "llvm.cos.f64", 1),
-        "powf32" => simple_llvm_intrinsic(bcx, "llvm.pow.f32", 2),
-        "powf64" => simple_llvm_intrinsic(bcx, "llvm.pow.f64", 2),
-        "expf32" => simple_llvm_intrinsic(bcx, "llvm.exp.f32", 1),
-        "expf64" => simple_llvm_intrinsic(bcx, "llvm.exp.f64", 1),
-        "exp2f32" => simple_llvm_intrinsic(bcx, "llvm.exp2.f32", 1),
-        "exp2f64" => simple_llvm_intrinsic(bcx, "llvm.exp2.f64", 1),
-        "logf32" => simple_llvm_intrinsic(bcx, "llvm.log.f32", 1),
-        "logf64" => simple_llvm_intrinsic(bcx, "llvm.log.f64", 1),
-        "log10f32" => simple_llvm_intrinsic(bcx, "llvm.log10.f32", 1),
-        "log10f64" => simple_llvm_intrinsic(bcx, "llvm.log10.f64", 1),
-        "log2f32" => simple_llvm_intrinsic(bcx, "llvm.log2.f32", 1),
-        "log2f64" => simple_llvm_intrinsic(bcx, "llvm.log2.f64", 1),
-        "fmaf32" => simple_llvm_intrinsic(bcx, "llvm.fma.f32", 3),
-        "fmaf64" => simple_llvm_intrinsic(bcx, "llvm.fma.f64", 3),
-        "fabsf32" => simple_llvm_intrinsic(bcx, "llvm.fabs.f32", 1),
-        "fabsf64" => simple_llvm_intrinsic(bcx, "llvm.fabs.f64", 1),
-        "floorf32" => simple_llvm_intrinsic(bcx, "llvm.floor.f32", 1),
-        "floorf64" => simple_llvm_intrinsic(bcx, "llvm.floor.f64", 1),
-        "ceilf32" => simple_llvm_intrinsic(bcx, "llvm.ceil.f32", 1),
-        "ceilf64" => simple_llvm_intrinsic(bcx, "llvm.ceil.f64", 1),
-        "truncf32" => simple_llvm_intrinsic(bcx, "llvm.trunc.f32", 1),
-        "truncf64" => simple_llvm_intrinsic(bcx, "llvm.trunc.f64", 1),
-        "ctpop8" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i8", 1),
-        "ctpop16" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i16", 1),
-        "ctpop32" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i32", 1),
-        "ctpop64" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i64", 1),
-        "ctlz8" => count_zeros_intrinsic(bcx, "llvm.ctlz.i8"),
-        "ctlz16" => count_zeros_intrinsic(bcx, "llvm.ctlz.i16"),
-        "ctlz32" => count_zeros_intrinsic(bcx, "llvm.ctlz.i32"),
-        "ctlz64" => count_zeros_intrinsic(bcx, "llvm.ctlz.i64"),
-        "cttz8" => count_zeros_intrinsic(bcx, "llvm.cttz.i8"),
-        "cttz16" => count_zeros_intrinsic(bcx, "llvm.cttz.i16"),
-        "cttz32" => count_zeros_intrinsic(bcx, "llvm.cttz.i32"),
-        "cttz64" => count_zeros_intrinsic(bcx, "llvm.cttz.i64"),
-        "bswap16" => simple_llvm_intrinsic(bcx, "llvm.bswap.i16", 1),
-        "bswap32" => simple_llvm_intrinsic(bcx, "llvm.bswap.i32", 1),
-        "bswap64" => simple_llvm_intrinsic(bcx, "llvm.bswap.i64", 1),
-
-        "i8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i8"),
-        "i16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i16"),
-        "i32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i32"),
-        "i64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i64"),
-
-        "u8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i8"),
-        "u16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i16"),
-        "u32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i32"),
-        "u64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i64"),
-
-        "i8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i8"),
-        "i16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i16"),
-        "i32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i32"),
-        "i64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i64"),
-
-        "u8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i8"),
-        "u16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i16"),
-        "u32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i32"),
-        "u64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i64"),
-
-        "i8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i8"),
-        "i16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i16"),
-        "i32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i32"),
-        "i64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i64"),
-
-        "u8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i8"),
-        "u16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i16"),
-        "u32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i32"),
-        "u64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i64"),
-
-        _ => {
-            // Could we make this an enum rather than a string? does it get
-            // checked earlier?
-            ccx.sess.span_bug(item.span, "unknown intrinsic");
-        }
+pub fn trans_foreign_mod(ccx: @mut CrateContext,
+                         foreign_mod: &ast::foreign_mod) {
+    let _icx = push_ctxt("foreign::trans_foreign_mod");
+    for &foreign_item in foreign_mod.items.iter() {
+        let lname = link_name(ccx, foreign_item);
+        ccx.item_symbols.insert(foreign_item.id, lname.to_owned());
     }
-    fcx.cleanup();
 }
 
-/**
- * Translates a "crust" fn, meaning a Rust fn that can be called
- * from C code.  In this case, we have to perform some adaptation
- * to (1) switch back to the Rust stack and (2) adapt the C calling
- * convention to our own.
- *
- * Example: Given a crust fn F(x: X, y: Y) -> Z, we generate a
- * Rust function R as normal:
- *
- *    void R(Z* dest, void *env, X x, Y y) {...}
- *
- * and then we generate a wrapper function W that looks like:
- *
- *    Z W(X x, Y y) {
- *        struct { X x; Y y; Z *z; } args = { x, y, z };
- *        call_on_c_stack_shim(S, &args);
- *    }
- *
- * Note that the wrapper follows the foreign (typically "C") ABI.
- * The wrapper is the actual "value" of the foreign fn.  Finally,
- * we generate a shim function S that looks like:
- *
- *     void S(struct { X x; Y y; Z *z; } *args) {
- *         R(args->z, NULL, args->x, args->y);
- *     }
- */
-pub fn trans_foreign_fn(ccx: @mut CrateContext,
-                        path: ast_map::path,
-                        decl: &ast::fn_decl,
-                        body: &ast::Block,
-                        llwrapfn: ValueRef,
-                        id: ast::NodeId) {
+///////////////////////////////////////////////////////////////////////////
+// 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 register_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
+                                         sp: span,
+                                         sym: ~str,
+                                         node_id: ast::NodeId)
+                                         -> ValueRef {
+    let _icx = push_ctxt("foreign::register_foreign_fn");
+
+    let tys = foreign_types_for_id(ccx, node_id);
+    let llfn_ty = lltype_for_fn_from_foreign_types(&tys);
+    let llfn = base::register_fn_llvmty(ccx,
+                                        sp,
+                                        sym,
+                                        node_id,
+                                        lib::llvm::CCallConv,
+                                        llfn_ty);
+    add_argument_attributes(&tys, llfn);
+    debug!("register_rust_fn_with_foreign_abi(node_id=%?, llfn_ty=%s, llfn=%s)",
+           node_id, ccx.tn.type_to_str(llfn_ty), ccx.tn.val_to_str(llfn));
+    llfn
+}
+
+pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
+                                      path: &ast_map::path,
+                                      decl: &ast::fn_decl,
+                                      body: &ast::Block,
+                                      llwrapfn: ValueRef,
+                                      id: ast::NodeId) {
     let _icx = push_ctxt("foreign::build_foreign_fn");
+    let tys = foreign_types_for_id(ccx, id);
+
+    unsafe { // unsafe because we call LLVM operations
+        // Build up the Rust function (`foo0` above).
+        let llrustfn = build_rust_fn(ccx, path, decl, body, id);
+
+        // Build up the foreign wrapper (`foo` above).
+        return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys);
+    }
 
     fn build_rust_fn(ccx: @mut CrateContext,
                      path: &ast_map::path,
                      decl: &ast::fn_decl,
                      body: &ast::Block,
                      id: ast::NodeId)
-                  -> ValueRef {
+                     -> ValueRef {
         let _icx = push_ctxt("foreign::foreign::build_rust_fn");
-        let t = ty::node_id_to_type(ccx.tcx, id);
-        // XXX: Bad copy.
+        let tcx = ccx.tcx;
+        let t = ty::node_id_to_type(tcx, id);
         let ps = link::mangle_internal_name_by_path(
-                            ccx,
-                            vec::append_one((*path).clone(),
-                                            ast_map::path_name(
-                                            special_idents::clownshoe_abi)));
+            ccx, vec::append_one((*path).clone(), ast_map::path_name(
+                special_idents::clownshoe_abi
+            )));
         let llty = type_of_fn_from_ty(ccx, t);
-        let llfndecl = decl_internal_cdecl_fn(ccx.llmod, ps, llty);
-        trans_fn(ccx,
-                 (*path).clone(),
-                 decl,
-                 body,
-                 llfndecl,
-                 no_self,
-                 None,
-                 id,
-                 []);
+        let llfndecl = base::decl_internal_cdecl_fn(ccx.llmod, ps, llty);
+        base::trans_fn(ccx,
+                       (*path).clone(),
+                       decl,
+                       body,
+                       llfndecl,
+                       base::no_self,
+                       None,
+                       id,
+                       []);
         return llfndecl;
     }
 
-    fn build_shim_fn(ccx: @mut CrateContext,
-                     path: ast_map::path,
-                     llrustfn: ValueRef,
-                     tys: &ShimTypes)
-                     -> ValueRef {
-        /*!
-         *
-         * Generate the shim S:
-         *
-         *     void S(struct { X x; Y y; Z *z; } *args) {
-         *         R(args->z, NULL, &args->x, args->y);
-         *     }
-         *
-         * One complication is that we must adapt to the Rust
-         * calling convention, which introduces indirection
-         * in some cases.  To demonstrate this, I wrote one of the
-         * entries above as `&args->x`, because presumably `X` is
-         * one of those types that is passed by pointer in Rust.
-         */
-
-        let _icx = push_ctxt("foreign::foreign::build_shim_fn");
-
-        fn build_args(bcx: @mut Block, tys: &ShimTypes, llargbundle: ValueRef)
-                      -> ~[ValueRef] {
-            let _icx = push_ctxt("foreign::extern::shim::build_args");
-            let ccx = bcx.ccx();
-            let mut llargvals = ~[];
-            let mut i = 0u;
-            let n = tys.fn_sig.inputs.len();
-
-            if !ty::type_is_immediate(bcx.tcx(), tys.fn_sig.output) {
-                let llretptr = load_inbounds(bcx, llargbundle, [0u, n]);
-                llargvals.push(llretptr);
+    unsafe fn build_wrap_fn(ccx: @mut CrateContext,
+                            llrustfn: ValueRef,
+                            llwrapfn: ValueRef,
+                            tys: &ForeignTypes) {
+        let _icx = push_ctxt(
+            "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
+        let tcx = ccx.tcx;
+
+        debug!("build_wrap_fn(llrustfn=%s, llwrapfn=%s)",
+               ccx.tn.val_to_str(llrustfn),
+               ccx.tn.val_to_str(llwrapfn));
+
+        // 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;
+        //     }
+
+        let the_block =
+            "the block".to_c_str().with_ref(
+                |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llwrapfn, s));
+
+        let builder = ccx.builder.B;
+        llvm::LLVMPositionBuilderAtEnd(builder, the_block);
+
+        // Array for the arguments we will pass to the rust function.
+        let mut llrust_args = ~[];
+        let mut next_foreign_arg_counter: c_uint = 0;
+        let next_foreign_arg: &fn() -> c_uint = {
+            || {
+                next_foreign_arg_counter += 1;
+                next_foreign_arg_counter - 1
             }
+        };
 
-            let llenvptr = C_null(Type::opaque_box(bcx.ccx()).ptr_to());
-            llargvals.push(llenvptr);
-            while i < n {
-                // Get a pointer to the argument:
-                let mut llargval = GEPi(bcx, llargbundle, [0u, i]);
+        // If there is an out pointer on the foreign function
+        let foreign_outptr = {
+            if tys.fn_ty.sret {
+                Some(llvm::LLVMGetParam(llwrapfn, next_foreign_arg()))
+            } else {
+                None
+            }
+        };
 
-                if !type_of::arg_is_indirect(ccx, &tys.fn_sig.inputs[i]) {
-                    // If Rust would pass this by value, load the value.
-                    llargval = Load(bcx, llargval);
+        // Push Rust return pointer, using null if it will be unused.
+        let rust_uses_outptr =
+            type_of::return_uses_outptr(tcx, tys.fn_sig.output);
+        let return_alloca: Option<ValueRef>;
+        let llrust_ret_ty = tys.llsig.llret_ty;
+        let llrust_retptr_ty = llrust_ret_ty.ptr_to();
+        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
+            // foriegn 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=%s",
+                           ccx.tn.val_to_str(llforeign_outptr));
+                    let llrust_retptr =
+                        llvm::LLVMBuildBitCast(builder,
+                                               llforeign_outptr,
+                                               llrust_ret_ty.ptr_to().to_ref(),
+                                               noname());
+                    debug!("out pointer, foreign=%s (casted)",
+                           ccx.tn.val_to_str(llrust_retptr));
+                    llrust_args.push(llrust_retptr);
+                    return_alloca = None;
                 }
 
-                llargvals.push(llargval);
-                i += 1u;
+                None => {
+                    let slot = {
+                        "return_alloca".to_c_str().with_ref(
+                            |s| llvm::LLVMBuildAlloca(builder,
+                                                      llrust_ret_ty.to_ref(),
+                                                      s))
+                    };
+                    debug!("out pointer, \
+                            allocad=%s, \
+                            llrust_ret_ty=%s, \
+                            return_ty=%s",
+                           ccx.tn.val_to_str(slot),
+                           ccx.tn.type_to_str(llrust_ret_ty),
+                           tys.fn_sig.output.repr(tcx));
+                    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;
+        };
+
+        // Push an (null) env pointer
+        let env_pointer = base::null_env_ptr(ccx);
+        debug!("env pointer=%s", ccx.tn.val_to_str(env_pointer));
+        llrust_args.push(env_pointer);
+
+        // 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 range(0, tys.fn_sig.inputs.len()) {
+            let rust_ty = tys.fn_sig.inputs[i];
+            let llrust_ty = tys.llsig.llarg_tys[i];
+            let foreign_index = next_foreign_arg();
+            let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
+            let foreign_indirect = tys.fn_ty.attrs[foreign_index].is_some();
+            let mut llforeign_arg = llvm::LLVMGetParam(llwrapfn, foreign_index);
+
+            debug!("llforeign_arg #%u: %s",
+                   i, ccx.tn.val_to_str(llforeign_arg));
+            debug!("rust_indirect = %b, foreign_indirect = %b",
+                   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 {
+                let lltemp =
+                    llvm::LLVMBuildAlloca(
+                        builder, val_ty(llforeign_arg).to_ref(), noname());
+                llvm::LLVMBuildStore(
+                    builder, llforeign_arg, lltemp);
+                llforeign_arg = 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 tys.fn_ty.arg_tys[foreign_index].cast {
+                assert!(!foreign_indirect);
+                llforeign_arg = llvm::LLVMBuildBitCast(
+                    builder, llforeign_arg,
+                    llrust_ty.ptr_to().to_ref(), noname());
             }
-            return llargvals;
-        }
 
-        fn build_ret(bcx: @mut Block,
-                     shim_types: &ShimTypes,
-                     llargbundle: ValueRef,
-                     llretval: ValueRef) {
-            if bcx.fcx.llretptr.is_some() &&
-                ty::type_is_immediate(bcx.tcx(), shim_types.fn_sig.output) {
-                // Write the value into the argument bundle.
-                let arg_count = shim_types.fn_sig.inputs.len();
-                let llretptr = load_inbounds(bcx,
-                                             llargbundle,
-                                             [0, arg_count]);
-                Store(bcx, llretval, llretptr);
+            let llrust_arg = if rust_indirect {
+                llforeign_arg
             } else {
-                // NB: The return pointer in the Rust ABI function is wired
-                // directly into the return slot in the shim struct.
+                llvm::LLVMBuildLoad(builder, llforeign_arg, noname())
+            };
+
+            debug!("llrust_arg #%u: %s",
+                   i, ccx.tn.val_to_str(llrust_arg));
+            llrust_args.push(llrust_arg);
+        }
+
+        // Perform the call itself
+        let llrust_ret_val = do llrust_args.as_imm_buf |ptr, len| {
+            debug!("calling llrustfn = %s", ccx.tn.val_to_str(llrustfn));
+            llvm::LLVMBuildCall(builder, llrustfn, ptr,
+                                len as c_uint, noname())
+        };
+
+        // Get the return value where the foreign fn expects it.
+        let llforeign_ret_ty = tys.fn_ty.ret_ty.ty;
+        match foreign_outptr {
+            None if !tys.ret_def => {
+                // Function returns `()` or `bot`, which in Rust is the LLVM
+                // type "{}" but in foreign ABIs is "Void".
+                llvm::LLVMBuildRetVoid(builder);
+            }
+
+            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 =
+                    llvm::LLVMBuildBitCast(builder,
+                                           llrust_outptr,
+                                           llforeign_ret_ty.ptr_to().to_ref(),
+                                           noname());
+                let llforeign_retval =
+                    llvm::LLVMBuildLoad(builder, llforeign_outptr_casted, noname());
+                llvm::LLVMBuildRet(builder, 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 =
+                    llvm::LLVMBuildAlloca(
+                        builder, llforeign_ret_ty.to_ref(), noname());
+                let lltemp_casted =
+                    llvm::LLVMBuildBitCast(builder,
+                                           lltemp,
+                                           llrust_ret_ty.ptr_to().to_ref(),
+                                           noname());
+                llvm::LLVMBuildStore(
+                    builder, llrust_ret_val, lltemp_casted);
+                let llforeign_retval =
+                    llvm::LLVMBuildLoad(builder, lltemp, noname());
+                llvm::LLVMBuildRet(builder, llforeign_retval);
+            }
+
+            None => {
+                // Neither ABI uses an outpointer, and the types
+                // match. Easy peasy.
+                llvm::LLVMBuildRet(builder, 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 =
+                    llvm::LLVMBuildBitCast(builder,
+                                           llforeign_outptr,
+                                           llrust_retptr_ty.to_ref(),
+                                           noname());
+                llvm::LLVMBuildStore(
+                    builder, llrust_ret_val, llforeign_outptr_casted);
+                llvm::LLVMBuildRetVoid(builder);
+            }
+
+            Some(_) => {
+                // Both ABIs use outpointers. Easy peasy.
+                llvm::LLVMBuildRetVoid(builder);
             }
         }
+    }
+}
 
-        let shim_name = link::mangle_internal_name_by_path(
-            ccx,
-            vec::append_one(path, ast_map::path_name(
-                special_idents::clownshoe_stack_shim
-            )));
-        build_shim_fn_(ccx,
-                       shim_name,
-                       llrustfn,
-                       tys,
-                       lib::llvm::CCallConv,
-                       build_args,
-                       build_ret)
+///////////////////////////////////////////////////////////////////////////
+// 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(ccx: &CrateContext, i: @ast::foreign_item) -> @str {
+     match attr::first_attr_value_str_by_name(i.attrs, "link_name") {
+        None => ccx.sess.str_of(i.ident),
+        Some(ln) => ln,
     }
+}
 
-    fn build_wrap_fn(ccx: @mut CrateContext,
-                     llshimfn: ValueRef,
-                     llwrapfn: ValueRef,
-                     tys: &ShimTypes) {
-        /*!
-         *
-         * Generate the wrapper W:
-         *
-         *    Z W(X x, Y y) {
-         *        struct { X x; Y y; Z *z; } args = { x, y, z };
-         *        call_on_c_stack_shim(S, &args);
-         *    }
-         */
-
-        let _icx = push_ctxt("foreign::foreign::build_wrap_fn");
-
-        build_wrap_fn_(ccx,
-                       tys,
-                       llshimfn,
-                       llwrapfn,
-                       ccx.upcalls.call_shim_on_rust_stack,
-                       true,
-                       build_args,
-                       build_ret);
-
-        fn build_args(bcx: @mut Block,
-                      tys: &ShimTypes,
-                      llwrapfn: ValueRef,
-                      llargbundle: ValueRef) {
-            let _icx = push_ctxt("foreign::foreign::wrap::build_args");
-            tys.fn_ty.build_wrap_args(bcx,
-                                      tys.llsig.llret_ty,
-                                      llwrapfn,
-                                      llargbundle);
-        }
+fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
+                     -> LlvmSignature {
+    /*!
+     * 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 build_ret(bcx: @mut Block, tys: &ShimTypes, llargbundle: ValueRef) {
-            let _icx = push_ctxt("foreign::foreign::wrap::build_ret");
-            tys.fn_ty.build_wrap_ret(bcx, tys.llsig.llarg_tys, llargbundle);
-        }
+    let llarg_tys = fn_sig.inputs.map(|&arg| type_of(ccx, arg));
+    let llret_ty = type_of::type_of(ccx, fn_sig.output);
+    LlvmSignature {
+        llarg_tys: llarg_tys,
+        llret_ty: llret_ty,
+        sret: type_of::return_uses_outptr(ccx.tcx, fn_sig.output),
     }
+}
 
-    let tys = shim_types(ccx, id);
-    // The internal Rust ABI function - runs on the Rust stack
-    // XXX: Bad copy.
-    let llrustfn = build_rust_fn(ccx, &path, decl, body, id);
-    // The internal shim function - runs on the Rust stack
-    let llshimfn = build_shim_fn(ccx, path, llrustfn, &tys);
-    // The foreign C function - runs on the C stack
-    build_wrap_fn(ccx, llshimfn, llwrapfn, &tys)
+fn foreign_types_for_id(ccx: &mut CrateContext,
+                        id: ast::NodeId) -> ForeignTypes {
+    foreign_types_for_fn_ty(ccx, ty::node_id_to_type(ccx.tcx, id))
 }
 
-pub fn register_foreign_fn(ccx: @mut CrateContext,
-                           sp: span,
-                           sym: ~str,
-                           node_id: ast::NodeId)
-                           -> ValueRef {
-    let _icx = push_ctxt("foreign::register_foreign_fn");
+fn foreign_types_for_fn_ty(ccx: &mut CrateContext,
+                           ty: ty::t) -> ForeignTypes {
+    let fn_sig = match ty::get(ty).sty {
+        ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(),
+        _ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type")
+    };
+    let llsig = foreign_signature(ccx, &fn_sig);
+    let ret_def = !ty::type_is_voidish(fn_sig.output);
+    let fn_ty = cabi::compute_abi_info(ccx,
+                                       llsig.llarg_tys,
+                                       llsig.llret_ty,
+                                       ret_def);
+    debug!("foreign_types_for_fn_ty(\
+           ty=%s, \
+           llsig=%s -> %s, \
+           fn_ty=%s -> %s, \
+           ret_def=%b",
+           ty.repr(ccx.tcx),
+           ccx.tn.types_to_str(llsig.llarg_tys),
+           ccx.tn.type_to_str(llsig.llret_ty),
+           ccx.tn.types_to_str(fn_ty.arg_tys.map(|t| t.ty)),
+           ccx.tn.type_to_str(fn_ty.ret_ty.ty),
+           ret_def);
+
+    ForeignTypes {
+        fn_sig: fn_sig,
+        llsig: llsig,
+        ret_def: ret_def,
+        fn_ty: fn_ty
+    }
+}
 
-    let sym = Cell::new(sym);
+fn lltype_for_fn_from_foreign_types(tys: &ForeignTypes) -> Type {
+    let llargument_tys: ~[Type] =
+        tys.fn_ty.arg_tys.iter().map(|t| t.ty).collect();
+    let llreturn_ty = tys.fn_ty.ret_ty.ty;
+    Type::func(llargument_tys, &llreturn_ty)
+}
+
+pub fn lltype_for_foreign_fn(ccx: &mut CrateContext, ty: ty::t) -> Type {
+    let fn_types = foreign_types_for_fn_ty(ccx, ty);
+    lltype_for_fn_from_foreign_types(&fn_types)
+}
 
-    let tys = shim_types(ccx, node_id);
-    do tys.fn_ty.decl_fn |fnty| {
-        register_fn_llvmty(ccx, sp, sym.take(), node_id, lib::llvm::CCallConv, fnty)
+fn add_argument_attributes(tys: &ForeignTypes,
+                           llfn: ValueRef) {
+    for (i, a) in tys.fn_ty.attrs.iter().enumerate() {
+        match *a {
+            Some(attr) => {
+                let llarg = get_param(llfn, i);
+                unsafe {
+                    llvm::LLVMAddAttribute(llarg, attr as c_uint);
+                }
+            }
+            None => ()
+        }
     }
 }
diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs
new file mode 100644 (file)
index 0000000..2232b89
--- /dev/null
@@ -0,0 +1,503 @@
+// Copyright 2012-2013 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};
+use lib::llvm::{SequentiallyConsistent, Acquire, Release, Xchg};
+use lib::llvm::{ValueRef, Pointer};
+use lib;
+use middle::trans::base::*;
+use middle::trans::build::*;
+use middle::trans::callee::*;
+use middle::trans::common::*;
+use middle::trans::datum::*;
+use middle::trans::type_of::*;
+use middle::trans::type_of;
+use middle::trans::expr::Ignore;
+use middle::trans::machine;
+use middle::trans::glue;
+use middle::ty::FnSig;
+use middle::ty;
+use syntax::ast;
+use syntax::ast_map;
+use syntax::attr;
+use syntax::opt_vec;
+use util::ppaux::{ty_to_str};
+use middle::trans::machine::llsize_of;
+use middle::trans::type_::Type;
+
+pub fn trans_intrinsic(ccx: @mut CrateContext,
+                       decl: ValueRef,
+                       item: &ast::foreign_item,
+                       path: ast_map::path,
+                       substs: @param_substs,
+                       attributes: &[ast::Attribute],
+                       ref_id: Option<ast::NodeId>) {
+    debug!("trans_intrinsic(item.ident=%s)", ccx.sess.str_of(item.ident));
+
+    fn simple_llvm_intrinsic(bcx: @mut Block, name: &'static str, num_args: uint) {
+        assert!(num_args <= 4);
+        let mut args = [0 as ValueRef, ..4];
+        let first_real_arg = bcx.fcx.arg_pos(0u);
+        for i in range(0u, num_args) {
+            args[i] = get_param(bcx.fcx.llfn, first_real_arg + i);
+        }
+        let llfn = bcx.ccx().intrinsics.get_copy(&name);
+        Ret(bcx, Call(bcx, llfn, args.slice(0, num_args)));
+    }
+
+    fn with_overflow_instrinsic(bcx: @mut Block, name: &'static str) {
+        let first_real_arg = bcx.fcx.arg_pos(0u);
+        let a = get_param(bcx.fcx.llfn, first_real_arg);
+        let b = get_param(bcx.fcx.llfn, first_real_arg + 1);
+        let llfn = bcx.ccx().intrinsics.get_copy(&name);
+
+        // convert `i1` to a `bool`, and write to the out parameter
+        let val = Call(bcx, llfn, [a, b]);
+        let result = ExtractValue(bcx, val, 0);
+        let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool());
+        let retptr = get_param(bcx.fcx.llfn, bcx.fcx.out_arg_pos());
+        let ret = Load(bcx, retptr);
+        let ret = InsertValue(bcx, ret, result, 0);
+        let ret = InsertValue(bcx, ret, overflow, 1);
+        Store(bcx, ret, retptr);
+        RetVoid(bcx)
+    }
+
+    fn memcpy_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) {
+        let ccx = bcx.ccx();
+        let lltp_ty = type_of::type_of(ccx, tp_ty);
+        let align = C_i32(machine::llalign_of_min(ccx, lltp_ty) as i32);
+        let size = match sizebits {
+            32 => C_i32(machine::llsize_of_real(ccx, lltp_ty) as i32),
+            64 => C_i64(machine::llsize_of_real(ccx, lltp_ty) as i64),
+            _ => ccx.sess.fatal("Invalid value for sizebits")
+        };
+
+        let decl = bcx.fcx.llfn;
+        let first_real_arg = bcx.fcx.arg_pos(0u);
+        let dst_ptr = PointerCast(bcx, get_param(decl, first_real_arg), Type::i8p());
+        let src_ptr = PointerCast(bcx, get_param(decl, first_real_arg + 1), Type::i8p());
+        let count = get_param(decl, first_real_arg + 2);
+        let volatile = C_i1(false);
+        let llfn = bcx.ccx().intrinsics.get_copy(&name);
+        Call(bcx, llfn, [dst_ptr, src_ptr, Mul(bcx, size, count), align, volatile]);
+        RetVoid(bcx);
+    }
+
+    fn memset_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) {
+        let ccx = bcx.ccx();
+        let lltp_ty = type_of::type_of(ccx, tp_ty);
+        let align = C_i32(machine::llalign_of_min(ccx, lltp_ty) as i32);
+        let size = match sizebits {
+            32 => C_i32(machine::llsize_of_real(ccx, lltp_ty) as i32),
+            64 => C_i64(machine::llsize_of_real(ccx, lltp_ty) as i64),
+            _ => ccx.sess.fatal("Invalid value for sizebits")
+        };
+
+        let decl = bcx.fcx.llfn;
+        let first_real_arg = bcx.fcx.arg_pos(0u);
+        let dst_ptr = PointerCast(bcx, get_param(decl, first_real_arg), Type::i8p());
+        let val = get_param(decl, first_real_arg + 1);
+        let count = get_param(decl, first_real_arg + 2);
+        let volatile = C_i1(false);
+        let llfn = bcx.ccx().intrinsics.get_copy(&name);
+        Call(bcx, llfn, [dst_ptr, val, Mul(bcx, size, count), align, volatile]);
+        RetVoid(bcx);
+    }
+
+    fn count_zeros_intrinsic(bcx: @mut Block, name: &'static str) {
+        let x = get_param(bcx.fcx.llfn, bcx.fcx.arg_pos(0u));
+        let y = C_i1(false);
+        let llfn = bcx.ccx().intrinsics.get_copy(&name);
+        Ret(bcx, Call(bcx, llfn, [x, y]));
+    }
+
+    let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, item.id));
+
+    let fcx = new_fn_ctxt_w_id(ccx,
+                               path,
+                               decl,
+                               item.id,
+                               output_type,
+                               true,
+                               Some(substs),
+                               None,
+                               Some(item.span));
+
+    set_always_inline(fcx.llfn);
+
+    // Set the fixed stack segment flag if necessary.
+    if attr::contains_name(attributes, "fixed_stack_segment") {
+        set_fixed_stack_segment(fcx.llfn);
+    }
+
+    let mut bcx = fcx.entry_bcx.unwrap();
+    let first_real_arg = fcx.arg_pos(0u);
+
+    let nm = ccx.sess.str_of(item.ident);
+    let name = nm.as_slice();
+
+    // This requires that atomic intrinsics follow a specific naming pattern:
+    // "atomic_<operation>[_<ordering>], and no ordering means SeqCst
+    if name.starts_with("atomic_") {
+        let split : ~[&str] = name.split_iter('_').collect();
+        assert!(split.len() >= 2, "Atomic intrinsic not correct format");
+        let order = if split.len() == 2 {
+            lib::llvm::SequentiallyConsistent
+        } else {
+            match split[2] {
+                "relaxed" => lib::llvm::Monotonic,
+                "acq"     => lib::llvm::Acquire,
+                "rel"     => lib::llvm::Release,
+                "acqrel"  => lib::llvm::AcquireRelease,
+                _ => ccx.sess.fatal("Unknown ordering in atomic intrinsic")
+            }
+        };
+
+        match split[1] {
+            "cxchg" => {
+                let old = AtomicCmpXchg(bcx, get_param(decl, first_real_arg),
+                                        get_param(decl, first_real_arg + 1u),
+                                        get_param(decl, first_real_arg + 2u),
+                                        order);
+                Ret(bcx, old);
+            }
+            "load" => {
+                let old = AtomicLoad(bcx, get_param(decl, first_real_arg),
+                                     order);
+                Ret(bcx, old);
+            }
+            "store" => {
+                AtomicStore(bcx, get_param(decl, first_real_arg + 1u),
+                            get_param(decl, first_real_arg),
+                            order);
+                RetVoid(bcx);
+            }
+            "fence" => {
+                AtomicFence(bcx, order);
+                RetVoid(bcx);
+            }
+            op => {
+                // These are all AtomicRMW ops
+                let atom_op = match op {
+                    "xchg"  => lib::llvm::Xchg,
+                    "xadd"  => lib::llvm::Add,
+                    "xsub"  => lib::llvm::Sub,
+                    "and"   => lib::llvm::And,
+                    "nand"  => lib::llvm::Nand,
+                    "or"    => lib::llvm::Or,
+                    "xor"   => lib::llvm::Xor,
+                    "max"   => lib::llvm::Max,
+                    "min"   => lib::llvm::Min,
+                    "umax"  => lib::llvm::UMax,
+                    "umin"  => lib::llvm::UMin,
+                    _ => ccx.sess.fatal("Unknown atomic operation")
+                };
+
+                let old = AtomicRMW(bcx, atom_op, get_param(decl, first_real_arg),
+                                    get_param(decl, first_real_arg + 1u),
+                                    order);
+                Ret(bcx, old);
+            }
+        }
+
+        fcx.cleanup();
+        return;
+    }
+
+    match name {
+        "size_of" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Ret(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)));
+        }
+        "move_val" => {
+            // Create a datum reflecting the value being moved.
+            // Use `appropriate_mode` so that the datum is by ref
+            // if the value is non-immediate. Note that, with
+            // intrinsics, there are no argument cleanups to
+            // concern ourselves with.
+            let tp_ty = substs.tys[0];
+            let mode = appropriate_mode(ccx.tcx, tp_ty);
+            let src = Datum {val: get_param(decl, first_real_arg + 1u),
+                             ty: tp_ty, mode: mode};
+            bcx = src.move_to(bcx, DROP_EXISTING,
+                              get_param(decl, first_real_arg));
+            RetVoid(bcx);
+        }
+        "move_val_init" => {
+            // See comments for `"move_val"`.
+            let tp_ty = substs.tys[0];
+            let mode = appropriate_mode(ccx.tcx, tp_ty);
+            let src = Datum {val: get_param(decl, first_real_arg + 1u),
+                             ty: tp_ty, mode: mode};
+            bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
+            RetVoid(bcx);
+        }
+        "min_align_of" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Ret(bcx, C_uint(ccx, machine::llalign_of_min(ccx, lltp_ty)));
+        }
+        "pref_align_of"=> {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Ret(bcx, C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)));
+        }
+        "get_tydesc" => {
+            let tp_ty = substs.tys[0];
+            let static_ti = get_tydesc(ccx, tp_ty);
+            glue::lazily_emit_all_tydesc_glue(ccx, static_ti);
+
+            // FIXME (#3730): ideally this shouldn't need a cast,
+            // but there's a circularity between translating rust types to llvm
+            // types and having a tydesc type available. So I can't directly access
+            // the llvm type of intrinsic::TyDesc struct.
+            let userland_tydesc_ty = type_of::type_of(ccx, output_type);
+            let td = PointerCast(bcx, static_ti.tydesc, userland_tydesc_ty);
+            Ret(bcx, td);
+        }
+        "init" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            match bcx.fcx.llretptr {
+                Some(ptr) => { Store(bcx, C_null(lltp_ty), ptr); RetVoid(bcx); }
+                None if ty::type_is_nil(tp_ty) => RetVoid(bcx),
+                None => Ret(bcx, C_null(lltp_ty)),
+            }
+        }
+        "uninit" => {
+            // Do nothing, this is effectively a no-op
+            let retty = substs.tys[0];
+            if ty::type_is_immediate(ccx.tcx, retty) && !ty::type_is_nil(retty) {
+                unsafe {
+                    Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref()));
+                }
+            } else {
+                RetVoid(bcx)
+            }
+        }
+        "forget" => {
+            RetVoid(bcx);
+        }
+        "transmute" => {
+            let (in_type, out_type) = (substs.tys[0], substs.tys[1]);
+            let llintype = type_of::type_of(ccx, in_type);
+            let llouttype = type_of::type_of(ccx, out_type);
+
+            let in_type_size = machine::llbitsize_of_real(ccx, llintype);
+            let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
+            if in_type_size != out_type_size {
+                let sp = match ccx.tcx.items.get_copy(&ref_id.unwrap()) {
+                    ast_map::node_expr(e) => e.span,
+                    _ => fail!("transmute has non-expr arg"),
+                };
+                let pluralize = |n| if 1u == n { "" } else { "s" };
+                ccx.sess.span_fatal(sp,
+                                    fmt!("transmute called on types with \
+                                          different sizes: %s (%u bit%s) to \
+                                          %s (%u bit%s)",
+                                         ty_to_str(ccx.tcx, in_type),
+                                         in_type_size,
+                                         pluralize(in_type_size),
+                                         ty_to_str(ccx.tcx, out_type),
+                                         out_type_size,
+                                         pluralize(out_type_size)));
+            }
+
+            if !ty::type_is_voidish(out_type) {
+                let llsrcval = get_param(decl, first_real_arg);
+                if ty::type_is_immediate(ccx.tcx, in_type) {
+                    match fcx.llretptr {
+                        Some(llretptr) => {
+                            Store(bcx, llsrcval, PointerCast(bcx, llretptr, llintype.ptr_to()));
+                            RetVoid(bcx);
+                        }
+                        None => match (llintype.kind(), llouttype.kind()) {
+                            (Pointer, other) | (other, Pointer) if other != Pointer => {
+                                let tmp = Alloca(bcx, llouttype, "");
+                                Store(bcx, llsrcval, PointerCast(bcx, tmp, llintype.ptr_to()));
+                                Ret(bcx, Load(bcx, tmp));
+                            }
+                            _ => Ret(bcx, BitCast(bcx, llsrcval, llouttype))
+                        }
+                    }
+                } else if ty::type_is_immediate(ccx.tcx, out_type) {
+                    let llsrcptr = PointerCast(bcx, llsrcval, llouttype.ptr_to());
+                    Ret(bcx, Load(bcx, llsrcptr));
+                } else {
+                    // NB: Do not use a Load and Store here. This causes massive
+                    // code bloat when `transmute` is used on large structural
+                    // types.
+                    let lldestptr = fcx.llretptr.unwrap();
+                    let lldestptr = PointerCast(bcx, lldestptr, Type::i8p());
+                    let llsrcptr = PointerCast(bcx, llsrcval, Type::i8p());
+
+                    let llsize = llsize_of(ccx, llintype);
+                    call_memcpy(bcx, lldestptr, llsrcptr, llsize, 1);
+                    RetVoid(bcx);
+                };
+            } else {
+                RetVoid(bcx);
+            }
+        }
+        "needs_drop" => {
+            let tp_ty = substs.tys[0];
+            Ret(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)));
+        }
+        "contains_managed" => {
+            let tp_ty = substs.tys[0];
+            Ret(bcx, C_bool(ty::type_contents(ccx.tcx, tp_ty).contains_managed()));
+        }
+        "visit_tydesc" => {
+            let td = get_param(decl, first_real_arg);
+            let visitor = get_param(decl, first_real_arg + 1u);
+            let td = PointerCast(bcx, td, ccx.tydesc_type.ptr_to());
+            glue::call_tydesc_glue_full(bcx, visitor, td,
+                                        abi::tydesc_field_visit_glue, None);
+            RetVoid(bcx);
+        }
+        "frame_address" => {
+            let frameaddress = ccx.intrinsics.get_copy(& &"llvm.frameaddress");
+            let frameaddress_val = Call(bcx, frameaddress, [C_i32(0i32)]);
+            let star_u8 = ty::mk_imm_ptr(
+                bcx.tcx(),
+                ty::mk_mach_uint(ast::ty_u8));
+            let fty = ty::mk_closure(bcx.tcx(), ty::ClosureTy {
+                purity: ast::impure_fn,
+                sigil: ast::BorrowedSigil,
+                onceness: ast::Many,
+                region: ty::re_bound(ty::br_anon(0)),
+                bounds: ty::EmptyBuiltinBounds(),
+                sig: FnSig {
+                    bound_lifetime_names: opt_vec::Empty,
+                    inputs: ~[ star_u8 ],
+                    output: ty::mk_nil()
+                }
+            });
+            let datum = Datum {val: get_param(decl, first_real_arg),
+                               mode: ByRef(ZeroMem), ty: fty};
+            let arg_vals = ~[frameaddress_val];
+            bcx = trans_call_inner(
+                bcx, None, fty, ty::mk_nil(),
+                |bcx| Callee {bcx: bcx, data: Closure(datum)},
+                ArgVals(arg_vals), Some(Ignore), DontAutorefArg).bcx;
+            RetVoid(bcx);
+        }
+        "morestack_addr" => {
+            // XXX This is a hack to grab the address of this particular
+            // native function. There should be a general in-language
+            // way to do this
+            let llfty = type_of_rust_fn(bcx.ccx(), [], ty::mk_nil());
+            let morestack_addr = decl_cdecl_fn(
+                bcx.ccx().llmod, "__morestack", llfty);
+            let morestack_addr = PointerCast(bcx, morestack_addr, Type::nil().ptr_to());
+            Ret(bcx, morestack_addr);
+        }
+        "offset" => {
+            let ptr = get_param(decl, first_real_arg);
+            let offset = get_param(decl, first_real_arg + 1);
+            Ret(bcx, GEP(bcx, ptr, [offset]));
+        }
+        "offset_inbounds" => {
+            let ptr = get_param(decl, first_real_arg);
+            let offset = get_param(decl, first_real_arg + 1);
+            Ret(bcx, InBoundsGEP(bcx, ptr, [offset]));
+        }
+        "memcpy32" => memcpy_intrinsic(bcx, "llvm.memcpy.p0i8.p0i8.i32", substs.tys[0], 32),
+        "memcpy64" => memcpy_intrinsic(bcx, "llvm.memcpy.p0i8.p0i8.i64", substs.tys[0], 64),
+        "memmove32" => memcpy_intrinsic(bcx, "llvm.memmove.p0i8.p0i8.i32", substs.tys[0], 32),
+        "memmove64" => memcpy_intrinsic(bcx, "llvm.memmove.p0i8.p0i8.i64", substs.tys[0], 64),
+        "memset32" => memset_intrinsic(bcx, "llvm.memset.p0i8.i32", substs.tys[0], 32),
+        "memset64" => memset_intrinsic(bcx, "llvm.memset.p0i8.i64", substs.tys[0], 64),
+        "sqrtf32" => simple_llvm_intrinsic(bcx, "llvm.sqrt.f32", 1),
+        "sqrtf64" => simple_llvm_intrinsic(bcx, "llvm.sqrt.f64", 1),
+        "powif32" => simple_llvm_intrinsic(bcx, "llvm.powi.f32", 2),
+        "powif64" => simple_llvm_intrinsic(bcx, "llvm.powi.f64", 2),
+        "sinf32" => simple_llvm_intrinsic(bcx, "llvm.sin.f32", 1),
+        "sinf64" => simple_llvm_intrinsic(bcx, "llvm.sin.f64", 1),
+        "cosf32" => simple_llvm_intrinsic(bcx, "llvm.cos.f32", 1),
+        "cosf64" => simple_llvm_intrinsic(bcx, "llvm.cos.f64", 1),
+        "powf32" => simple_llvm_intrinsic(bcx, "llvm.pow.f32", 2),
+        "powf64" => simple_llvm_intrinsic(bcx, "llvm.pow.f64", 2),
+        "expf32" => simple_llvm_intrinsic(bcx, "llvm.exp.f32", 1),
+        "expf64" => simple_llvm_intrinsic(bcx, "llvm.exp.f64", 1),
+        "exp2f32" => simple_llvm_intrinsic(bcx, "llvm.exp2.f32", 1),
+        "exp2f64" => simple_llvm_intrinsic(bcx, "llvm.exp2.f64", 1),
+        "logf32" => simple_llvm_intrinsic(bcx, "llvm.log.f32", 1),
+        "logf64" => simple_llvm_intrinsic(bcx, "llvm.log.f64", 1),
+        "log10f32" => simple_llvm_intrinsic(bcx, "llvm.log10.f32", 1),
+        "log10f64" => simple_llvm_intrinsic(bcx, "llvm.log10.f64", 1),
+        "log2f32" => simple_llvm_intrinsic(bcx, "llvm.log2.f32", 1),
+        "log2f64" => simple_llvm_intrinsic(bcx, "llvm.log2.f64", 1),
+        "fmaf32" => simple_llvm_intrinsic(bcx, "llvm.fma.f32", 3),
+        "fmaf64" => simple_llvm_intrinsic(bcx, "llvm.fma.f64", 3),
+        "fabsf32" => simple_llvm_intrinsic(bcx, "llvm.fabs.f32", 1),
+        "fabsf64" => simple_llvm_intrinsic(bcx, "llvm.fabs.f64", 1),
+        "floorf32" => simple_llvm_intrinsic(bcx, "llvm.floor.f32", 1),
+        "floorf64" => simple_llvm_intrinsic(bcx, "llvm.floor.f64", 1),
+        "ceilf32" => simple_llvm_intrinsic(bcx, "llvm.ceil.f32", 1),
+        "ceilf64" => simple_llvm_intrinsic(bcx, "llvm.ceil.f64", 1),
+        "truncf32" => simple_llvm_intrinsic(bcx, "llvm.trunc.f32", 1),
+        "truncf64" => simple_llvm_intrinsic(bcx, "llvm.trunc.f64", 1),
+        "ctpop8" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i8", 1),
+        "ctpop16" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i16", 1),
+        "ctpop32" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i32", 1),
+        "ctpop64" => simple_llvm_intrinsic(bcx, "llvm.ctpop.i64", 1),
+        "ctlz8" => count_zeros_intrinsic(bcx, "llvm.ctlz.i8"),
+        "ctlz16" => count_zeros_intrinsic(bcx, "llvm.ctlz.i16"),
+        "ctlz32" => count_zeros_intrinsic(bcx, "llvm.ctlz.i32"),
+        "ctlz64" => count_zeros_intrinsic(bcx, "llvm.ctlz.i64"),
+        "cttz8" => count_zeros_intrinsic(bcx, "llvm.cttz.i8"),
+        "cttz16" => count_zeros_intrinsic(bcx, "llvm.cttz.i16"),
+        "cttz32" => count_zeros_intrinsic(bcx, "llvm.cttz.i32"),
+        "cttz64" => count_zeros_intrinsic(bcx, "llvm.cttz.i64"),
+        "bswap16" => simple_llvm_intrinsic(bcx, "llvm.bswap.i16", 1),
+        "bswap32" => simple_llvm_intrinsic(bcx, "llvm.bswap.i32", 1),
+        "bswap64" => simple_llvm_intrinsic(bcx, "llvm.bswap.i64", 1),
+
+        "i8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i8"),
+        "i16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i16"),
+        "i32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i32"),
+        "i64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i64"),
+
+        "u8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i8"),
+        "u16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i16"),
+        "u32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i32"),
+        "u64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i64"),
+
+        "i8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i8"),
+        "i16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i16"),
+        "i32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i32"),
+        "i64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i64"),
+
+        "u8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i8"),
+        "u16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i16"),
+        "u32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i32"),
+        "u64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i64"),
+
+        "i8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i8"),
+        "i16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i16"),
+        "i32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i32"),
+        "i64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i64"),
+
+        "u8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i8"),
+        "u16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i16"),
+        "u32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i32"),
+        "u64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i64"),
+
+        _ => {
+            // Could we make this an enum rather than a string? does it get
+            // checked earlier?
+            ccx.sess.span_bug(item.span, "unknown intrinsic");
+        }
+    }
+    fcx.cleanup();
+}
index 387a8ecc5eec60a6d39392bc4784b5104618281d..cf6d465cb820c50f230e222cac3b3f3d7f73091f 100644 (file)
@@ -35,6 +35,7 @@
 pub mod cabi_arm;
 pub mod cabi_mips;
 pub mod foreign;
+pub mod intrinsic;
 pub mod reflect;
 pub mod debuginfo;
 pub mod type_use;
index 21ef9058069ab3154c5a3a0be766166b92b56fa2..5249a2b9b7bb642443ed9b7659d2c5afeb8ce967 100644 (file)
 use middle::trans::base;
 use middle::trans::common::*;
 use middle::trans::datum;
-use middle::trans::foreign;
 use middle::trans::machine;
 use middle::trans::meth;
 use middle::trans::type_of::type_of_fn_from_ty;
 use middle::trans::type_of;
 use middle::trans::type_use;
+use middle::trans::intrinsic;
 use middle::ty;
 use middle::ty::{FnSig};
 use middle::typeck;
@@ -239,8 +239,8 @@ pub fn monomorphic_fn(ccx: @mut CrateContext,
       }
       ast_map::node_foreign_item(i, _, _, _) => {
           let d = mk_lldecl();
-          foreign::trans_intrinsic(ccx, d, i, pt, psubsts, i.attrs,
-                                ref_id);
+          intrinsic::trans_intrinsic(ccx, d, i, pt, psubsts, i.attrs,
+                                     ref_id);
           d
       }
       ast_map::node_variant(ref v, enum_item, _) => {
index fe5f8cd70ef19ba1d7f366c9373067b183634024..dd1b041ef80f1aae3d49dd4012a6b087986b3464 100644 (file)
@@ -284,7 +284,7 @@ pub fn visit_ty(&mut self, t: ty::t) {
                                                                sub_path,
                                                                "get_disr");
 
-                let llfty = type_of_fn(ccx, [opaqueptrty], ty::mk_int());
+                let llfty = type_of_rust_fn(ccx, [opaqueptrty], ty::mk_int());
                 let llfdecl = decl_internal_cdecl_fn(ccx.llmod, sym, llfty);
                 let fcx = new_fn_ctxt(ccx,
                                       ~[],
index 6a382cc1a5d1cc82b6682d6fc34a32683c5509c2..6a57827e6d1c60ab3a6f2599a4c62d6ee1e17e55 100644 (file)
@@ -11,6 +11,7 @@
 
 use middle::trans::adt;
 use middle::trans::common::*;
+use middle::trans::foreign;
 use middle::ty;
 use util::ppaux;
 
 use syntax::ast;
 use syntax::opt_vec;
 
-pub fn arg_is_indirect(ccx: &CrateContext, arg_ty: &ty::t) -> bool {
-    !ty::type_is_immediate(ccx.tcx, *arg_ty)
+pub fn arg_is_indirect(ccx: &CrateContext, arg_ty: ty::t) -> bool {
+    !ty::type_is_immediate(ccx.tcx, arg_ty)
 }
 
-pub fn type_of_explicit_arg(ccx: &mut CrateContext, arg_ty: &ty::t) -> Type {
-    let llty = type_of(ccx, *arg_ty);
+pub fn return_uses_outptr(tcx: ty::ctxt, ty: ty::t) -> bool {
+    !ty::type_is_immediate(tcx, ty)
+}
+
+pub fn type_of_explicit_arg(ccx: &mut CrateContext, arg_ty: ty::t) -> Type {
+    let llty = type_of(ccx, arg_ty);
     if arg_is_indirect(ccx, arg_ty) {
         llty.ptr_to()
     } else {
@@ -34,17 +39,19 @@ pub fn type_of_explicit_arg(ccx: &mut CrateContext, arg_ty: &ty::t) -> Type {
 
 pub fn type_of_explicit_args(ccx: &mut CrateContext,
                              inputs: &[ty::t]) -> ~[Type] {
-    inputs.map(|arg_ty| type_of_explicit_arg(ccx, arg_ty))
+    inputs.map(|&arg_ty| type_of_explicit_arg(ccx, arg_ty))
 }
 
-pub fn type_of_fn(cx: &mut CrateContext, inputs: &[ty::t], output: ty::t) -> Type {
+pub fn type_of_rust_fn(cx: &mut CrateContext,
+                       inputs: &[ty::t],
+                       output: ty::t) -> Type {
     let mut atys: ~[Type] = ~[];
 
     // Arg 0: Output pointer.
     // (if the output type is non-immediate)
-    let output_is_immediate = ty::type_is_immediate(cx.tcx, output);
+    let use_out_pointer = return_uses_outptr(cx.tcx, output);
     let lloutputtype = type_of(cx, output);
-    if !output_is_immediate {
+    if use_out_pointer {
         atys.push(lloutputtype.ptr_to());
     }
 
@@ -55,7 +62,7 @@ pub fn type_of_fn(cx: &mut CrateContext, inputs: &[ty::t], output: ty::t) -> Typ
     atys.push_all(type_of_explicit_args(cx, inputs));
 
     // Use the output as the actual return value if it's immediate.
-    if output_is_immediate && !ty::type_is_nil(output) {
+    if !use_out_pointer && !ty::type_is_voidish(output) {
         Type::func(atys, &lloutputtype)
     } else {
         Type::func(atys, &Type::void())
@@ -64,13 +71,21 @@ pub fn type_of_fn(cx: &mut CrateContext, inputs: &[ty::t], output: ty::t) -> Typ
 
 // Given a function type and a count of ty params, construct an llvm type
 pub fn type_of_fn_from_ty(cx: &mut CrateContext, fty: ty::t) -> Type {
-    match ty::get(fty).sty {
-        ty::ty_closure(ref f) => type_of_fn(cx, f.sig.inputs, f.sig.output),
-        ty::ty_bare_fn(ref f) => type_of_fn(cx, f.sig.inputs, f.sig.output),
+    return match ty::get(fty).sty {
+        ty::ty_closure(ref f) => {
+            type_of_rust_fn(cx, f.sig.inputs, f.sig.output)
+        }
+        ty::ty_bare_fn(ref f) => {
+            if f.abis.is_rust() || f.abis.is_intrinsic() {
+                type_of_rust_fn(cx, f.sig.inputs, f.sig.output)
+            } else {
+                foreign::lltype_for_foreign_fn(cx, fty)
+            }
+        }
         _ => {
             cx.sess.bug("type_of_fn_from_ty given non-closure, non-bare-fn")
         }
-    }
+    };
 }
 
 // A "sizing type" is an LLVM type, the size and alignment of which are
@@ -250,7 +265,9 @@ pub fn type_of(cx: &mut CrateContext, t: ty::t) -> Type {
           Type::array(&type_of(cx, mt.ty), n as u64)
       }
 
-      ty::ty_bare_fn(_) => type_of_fn_from_ty(cx, t).ptr_to(),
+      ty::ty_bare_fn(_) => {
+          type_of_fn_from_ty(cx, t).ptr_to()
+      }
       ty::ty_closure(_) => {
           let ty = type_of_fn_from_ty(cx, t);
           Type::func_pair(cx, &ty)
index 80d6ce6ab080971f36967675ba5f299d92c1d671..685699f781900e741a98d3c44516ef5df2a8cc1e 100644 (file)
@@ -1545,6 +1545,11 @@ pub fn subst(cx: ctxt,
 
 // Type utilities
 
+pub fn type_is_voidish(ty: t) -> bool {
+    //! "nil" and "bot" are void types in that they represent 0 bits of information
+    type_is_nil(ty) || type_is_bot(ty)
+}
+
 pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
 
 pub fn type_is_bot(ty: t) -> bool {
index 7dbbf81f15dc1d1cdc13552517a4fad0a429573e..0bf0a74b2f43b764bfa262cf2d714e9dbe885282 100644 (file)
@@ -68,6 +68,7 @@ pub mod middle {
     pub mod reachable;
     pub mod graph;
     pub mod cfg;
+    pub mod stack_check;
 }
 
 pub mod front {
index 3bc0a7167e9bdd8a2ce7f59c3e466f59293c5ab0..5ba52326579e1b1d820218f1b83e0df5f663d3d1 100644 (file)
@@ -862,3 +862,15 @@ fn user_string(&self, tcx: ctxt) -> ~str {
         ty_to_str(tcx, *self)
     }
 }
+
+impl Repr for AbiSet {
+    fn repr(&self, _tcx: ctxt) -> ~str {
+        self.to_str()
+    }
+}
+
+impl UserString for AbiSet {
+    fn user_string(&self, _tcx: ctxt) -> ~str {
+        self.to_str()
+    }
+}
index 7b5e05a5671ab505d4b47f245c684f6f2fe6c8aa..be6871ae6ffa7657e2a96dd5d32d4302608b0480 100644 (file)
@@ -8,15 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//error-pattern:libc::c_int or libc::c_long should be used
+#[forbid(ctypes)];
+
 mod xx {
     extern {
-        pub fn strlen(str: *u8) -> uint;
-        pub fn foo(x: int, y: uint);
+        pub fn strlen(str: *u8) -> uint; //~ ERROR found rust type `uint`
+        pub fn foo(x: int, y: uint); //~ ERROR found rust type `int`
+        //~^ ERROR found rust type `uint`
     }
 }
 
 fn main() {
-  // let it fail to verify warning message
-  fail!()
 }
index 0aa6b3cc83d52215ab1be144fe8269e6fda2a892..ba671a1c4949f3bf82ea231708f881591fcbcd9b 100644 (file)
@@ -8,9 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-win32 #5745
-// xfail-macos Broken on mac i686
-
 struct TwoU16s {
     one: u16, two: u16
 }
index c4d50d1766c5dc469ab2a304d3fea4a2d474e0be..5d2fd14075844f46379e416535bd9445d590e38f 100644 (file)
@@ -8,9 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-win32 #5745
-// xfail-macos Broken on mac i686
-
 struct TwoU8s {
     one: u8, two: u8
 }
index bbd5857335db6eb019211e578d5b7fe3e081ee54..1e5ea7126e4403396c409eac4f34335c2457d53b 100644 (file)
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// xfail-test - FIXME(#8538) some kind of problem linking induced by extern "C" fns that I do not understand
 // xfail-fast - windows doesn't like this
 
 // Smallest hello world with no runtime