about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/rt/thread.rs4
-rw-r--r--src/libcore/unstable.rs4
-rw-r--r--src/librustc/lib/llvm.rs2
-rw-r--r--src/librustc/middle/lint.rs24
-rw-r--r--src/librustc/middle/trans/build.rs2
-rw-r--r--src/librustc/middle/trans/callee.rs2
-rw-r--r--src/librustc/middle/trans/foreign.rs373
-rw-r--r--src/librustc/middle/trans/type_of.rs22
-rw-r--r--src/libstd/time.rs16
-rw-r--r--src/rt/rust_builtin.cpp44
-rw-r--r--src/rt/rustrt.def.in7
-rw-r--r--src/rustllvm/RustWrapper.cpp4
-rw-r--r--src/test/run-pass/extern-pass-TwoU64s-ref.rs31
-rw-r--r--src/test/run-pass/extern-pass-TwoU64s.rs32
-rw-r--r--src/test/run-pass/extern-pass-char.rs22
-rw-r--r--src/test/run-pass/extern-pass-double.rs20
-rw-r--r--src/test/run-pass/extern-pass-u32.rs22
-rw-r--r--src/test/run-pass/extern-pass-u64.rs22
18 files changed, 492 insertions, 161 deletions
diff --git a/src/libcore/rt/thread.rs b/src/libcore/rt/thread.rs
index cd461274512..be1d86c9cf7 100644
--- a/src/libcore/rt/thread.rs
+++ b/src/libcore/rt/thread.rs
@@ -22,7 +22,7 @@ struct Thread {
 impl Thread {
     static fn start(main: ~fn()) -> Thread {
         fn substart(main: &fn()) -> *raw_thread {
-            unsafe { rust_raw_thread_start(main) }
+            unsafe { rust_raw_thread_start(&main) }
         }
         let raw = substart(main);
         Thread {
@@ -39,6 +39,6 @@ impl Drop for Thread {
 }
 
 extern {
-    pub unsafe fn rust_raw_thread_start(f: &fn()) -> *raw_thread;
+    pub unsafe fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread;
     pub unsafe fn rust_raw_thread_join_delete(thread: *raw_thread);
 }
diff --git a/src/libcore/unstable.rs b/src/libcore/unstable.rs
index 7936b18dbe2..9b6dcc31234 100644
--- a/src/libcore/unstable.rs
+++ b/src/libcore/unstable.rs
@@ -47,7 +47,7 @@ mod rustrt {
         pub unsafe fn rust_lock_little_lock(lock: rust_little_lock);
         pub unsafe fn rust_unlock_little_lock(lock: rust_little_lock);
 
-        pub unsafe fn rust_raw_thread_start(f: &fn()) -> *raw_thread;
+        pub unsafe fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread;
         pub unsafe fn rust_raw_thread_join_delete(thread: *raw_thread);
     }
 }
@@ -72,7 +72,7 @@ pub fn run_in_bare_thread(f: ~fn()) {
             let closure: &fn() = || {
                 f()
             };
-            let thread = rustrt::rust_raw_thread_start(closure);
+            let thread = rustrt::rust_raw_thread_start(&closure);
             rustrt::rust_raw_thread_join_delete(thread);
             chan.send(());
         }
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index b0cc18a30a4..23eb6743b9f 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -1443,7 +1443,7 @@ pub mod llvm {
         /** Prepares inline assembly. */
         pub unsafe fn LLVMInlineAsm(Ty: TypeRef, AsmString: *c_char,
                                     Constraints: *c_char, SideEffects: Bool,
-                                    AlignStack: Bool, Dialect: AsmDialect)
+                                    AlignStack: Bool, Dialect: c_uint)
                                  -> ValueRef;
     }
 }
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 93f0557028e..47ec756fe0e 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -78,6 +78,7 @@ pub enum lint {
     deprecated_self,
     deprecated_mutable_fields,
     deprecated_drop,
+    foreign_mode,
 
     managed_heap_memory,
     owned_heap_memory,
@@ -182,6 +183,13 @@ pub fn get_lint_dict() -> LintDict {
             default: warn
          }),
 
+        (@~"foreign_mode",
+         @LintSpec {
+            lint: foreign_mode,
+            desc: "warn about deprecated uses of modes in foreign fns",
+            default: warn
+         }),
+
         (@~"deprecated_pattern",
          @LintSpec {
             lint: deprecated_pattern,
@@ -753,6 +761,20 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
 
     fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
                         decl: &ast::fn_decl) {
+        // warn about `&&` mode on foreign functions, both because it is
+        // deprecated and because its semantics have changed recently:
+        for decl.inputs.eachi |i, arg| {
+            match ty::resolved_mode(cx, arg.mode) {
+                ast::by_val | ast::by_copy => {}
+                ast::by_ref => {
+                    cx.sess.span_lint(
+                        foreign_mode, fn_id, fn_id, arg.ty.span,
+                        fmt!("foreign function uses `&&` mode \
+                              on argument %u", i));
+                }
+            }
+        }
+
         let tys = vec::map(decl.inputs, |a| a.ty );
         for vec::each(vec::append_one(tys, decl.output)) |ty| {
             match ty.node {
@@ -785,7 +807,7 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
       if attr::foreign_abi(it.attrs) !=
             either::Right(ast::foreign_abi_rust_intrinsic) => {
         for nmod.items.each |ni| {
-            match /*bad*/copy ni.node {
+            match ni.node {
               ast::foreign_item_fn(ref decl, _, _) => {
                 check_foreign_fn(cx, it.id, decl);
               }
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index e75cf14ff69..850ea908e74 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -885,7 +885,7 @@ pub fn InlineAsmCall(cx: block, asm: *c_char, cons: *c_char,
 
         let llfty = T_fn(~[], T_void());
         let v = llvm::LLVMInlineAsm(llfty, asm, cons, volatile,
-                                    alignstack, dia);
+                                    alignstack, dia as c_uint);
 
         Call(cx, v, ~[])
     }
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 9e38252dc9a..5052ce07671 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -757,7 +757,7 @@ pub fn trans_arg_expr(bcx: block,
 
         if formal_ty.ty != arg_datum.ty {
             // this could happen due to e.g. subtyping
-            let llformal_ty = type_of::type_of_explicit_arg(ccx, formal_ty);
+            let llformal_ty = type_of::type_of_explicit_arg(ccx, &formal_ty);
             debug!("casting actual type (%s) to match formal (%s)",
                    bcx.val_str(val), bcx.llty_str(llformal_ty));
             val = PointerCast(bcx, val, llformal_ty);
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 1e3c4f21bd8..fde1de5b63f 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -58,59 +58,90 @@ pub fn link_name(ccx: @CrateContext, i: @ast::foreign_item) -> @~str {
     }
 }
 
-struct c_stack_tys {
-    arg_tys: ~[TypeRef],
-    ret_ty: TypeRef,
+struct ShimTypes {
+    fn_sig: ty::FnSig,
+
+    /// 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: TypeRef,
+
+    /// Type of the shim function itself.
     shim_fn_ty: TypeRef,
+
+    /// Adapter object for handling native ABI rules (trust me, you
+    /// don't want to know).
     fn_ty: cabi::FnType
 }
 
-fn c_arg_and_ret_lltys(ccx: @CrateContext,
-                       id: ast::node_id) -> (~[TypeRef], TypeRef, ty::t) {
-    match ty::get(ty::node_id_to_type(ccx.tcx, id)).sty {
-        ty::ty_bare_fn(ref fn_ty) => {
-            let llargtys = type_of_explicit_args(ccx, fn_ty.sig.inputs);
-            let llretty = type_of::type_of(ccx, fn_ty.sig.output);
-            (llargtys, llretty, fn_ty.sig.output)
-        }
-        _ => ccx.sess.bug(~"c_arg_and_ret_lltys called on non-function type")
-    }
+struct LlvmSignature {
+    llarg_tys: ~[TypeRef],
+    llret_ty: TypeRef,
 }
 
-fn c_stack_tys(ccx: @CrateContext,
-               id: ast::node_id) -> @c_stack_tys {
-    let (llargtys, llretty, ret_ty) = c_arg_and_ret_lltys(ccx, id);
-    // XXX: Bad copy.
-    let bundle_ty = T_struct(vec::append_one(copy llargtys, T_ptr(llretty)));
-    let ret_def = !ty::type_is_bot(ret_ty) && !ty::type_is_nil(ret_ty);
-    let fn_ty = abi_info(ccx.sess.targ_cfg.arch).
-                    compute_info(llargtys, llretty, ret_def);
-    return @c_stack_tys {
-        arg_tys: llargtys,
-        ret_ty: llretty,
+fn foreign_signature(ccx: @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.
+     */
+
+    let llarg_tys = fn_sig.inputs.map(|arg| 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}
+}
+
+fn shim_types(ccx: @CrateContext, id: ast::node_id) -> ShimTypes {
+    let fn_sig = match ty::get(ty::node_id_to_type(ccx.tcx, id)).sty {
+        ty::ty_bare_fn(ref fn_ty) => copy fn_ty.sig,
+        _ => ccx.sess.bug(~"c_arg_and_ret_lltys called on non-function type")
+    };
+    let llsig = foreign_signature(ccx, &fn_sig);
+    let bundle_ty = T_struct(vec::append_one(copy llsig.llarg_tys,
+                                             T_ptr(llsig.llret_ty)));
+    let ret_def =
+        !ty::type_is_bot(fn_sig.output) &&
+        !ty::type_is_nil(fn_sig.output);
+    let fn_ty =
+        abi_info(ccx.sess.targ_cfg.arch).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: T_fn(~[T_ptr(bundle_ty)], T_void()),
         fn_ty: fn_ty
-    };
+    }
 }
 
-type shim_arg_builder = &self/fn(bcx: block, tys: @c_stack_tys,
-                                 llargbundle: ValueRef) -> ~[ValueRef];
+type shim_arg_builder<'self> =
+    &'self fn(bcx: block, tys: &ShimTypes,
+              llargbundle: ValueRef) -> ~[ValueRef];
 
-type shim_ret_builder = &self/fn(bcx: block, tys: @c_stack_tys,
-                                 llargbundle: ValueRef, llretval: ValueRef);
+type shim_ret_builder<'self> =
+    &'self fn(bcx: block, tys: &ShimTypes,
+              llargbundle: ValueRef,
+              llretval: ValueRef);
 
 fn build_shim_fn_(ccx: @CrateContext,
                   +shim_name: ~str,
                   llbasefn: ValueRef,
-                  tys: @c_stack_tys,
+                  tys: &ShimTypes,
                   cc: lib::llvm::CallConv,
                   arg_builder: shim_arg_builder,
-                  ret_builder: shim_ret_builder) -> ValueRef {
-
+                  ret_builder: shim_ret_builder) -> ValueRef
+{
     let llshimfn = decl_internal_cdecl_fn(
         ccx.llmod, shim_name, tys.shim_fn_ty);
 
@@ -122,8 +153,7 @@ fn build_shim_fn_(ccx: @CrateContext,
     let llargvals = arg_builder(bcx, tys, llargbundle);
 
     // Create the call itself and store the return value:
-    let llretval = CallWithConv(bcx, llbasefn,
-                                llargvals, cc); // r
+    let llretval = CallWithConv(bcx, llbasefn, llargvals, cc);
 
     ret_builder(bcx, tys, llargbundle, llretval);
 
@@ -133,21 +163,22 @@ fn build_shim_fn_(ccx: @CrateContext,
     return llshimfn;
 }
 
-type wrap_arg_builder = &self/fn(bcx: block, tys: @c_stack_tys,
-                                 llwrapfn: ValueRef,
-                                 llargbundle: ValueRef);
+type wrap_arg_builder<'self> =
+    &'self fn(bcx: block, tys: &ShimTypes,
+              llwrapfn: ValueRef, llargbundle: ValueRef);
 
-type wrap_ret_builder = &self/fn(bcx: block, tys: @c_stack_tys,
-                                 llargbundle: ValueRef);
+type wrap_ret_builder<'self> =
+    &'self fn(bcx: block, tys: &ShimTypes,
+              llargbundle: ValueRef);
 
 fn build_wrap_fn_(ccx: @CrateContext,
-                  tys: @c_stack_tys,
+                  tys: &ShimTypes,
                   llshimfn: ValueRef,
                   llwrapfn: ValueRef,
                   shim_upcall: ValueRef,
                   arg_builder: wrap_arg_builder,
-                  ret_builder: wrap_ret_builder) {
-
+                  ret_builder: wrap_ret_builder)
+{
     let _icx = ccx.insn_ctxt("foreign::build_wrap_fn_");
     let fcx = new_fn_ctxt(ccx, ~[], llwrapfn, None);
     let bcx = top_scope_block(fcx, None);
@@ -199,36 +230,83 @@ fn build_wrap_fn_(ccx: @CrateContext,
 //         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.
+// 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: @CrateContext,
                          foreign_mod: &ast::foreign_mod,
-                         abi: ast::foreign_abi) {
-
+                         abi: ast::foreign_abi)
+{
     let _icx = ccx.insn_ctxt("foreign::trans_foreign_mod");
 
+    let mut cc = match abi {
+        ast::foreign_abi_rust_intrinsic |
+        ast::foreign_abi_cdecl => lib::llvm::CCallConv,
+        ast::foreign_abi_stdcall => lib::llvm::X86StdcallCallConv
+    };
+
+    for vec::each(foreign_mod.items) |foreign_item| {
+        match foreign_item.node {
+            ast::foreign_item_fn(*) => {
+                let id = foreign_item.id;
+                if abi != ast::foreign_abi_rust_intrinsic {
+                    let llwrapfn = get_item_val(ccx, id);
+                    let tys = shim_types(ccx, id);
+                    if attr::attrs_contains_name(
+                        foreign_item.attrs, "rust_stack")
+                    {
+                        build_direct_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);
+                    }
+                } else {
+                    // Intrinsics are emitted by monomorphic fn
+                }
+            }
+            ast::foreign_item_const(*) => {
+                let ident = ccx.sess.parse_sess.interner.get(
+                    foreign_item.ident);
+                ccx.item_symbols.insert(foreign_item.id, copy *ident);
+            }
+        }
+    }
+
     fn build_shim_fn(ccx: @CrateContext,
                      foreign_item: @ast::foreign_item,
-                     tys: @c_stack_tys,
-                     cc: lib::llvm::CallConv) -> ValueRef {
+                     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 = ccx.insn_ctxt("foreign::build_shim_fn");
 
-        fn build_args(bcx: block, tys: @c_stack_tys,
+        fn build_args(bcx: block, tys: &ShimTypes,
                       llargbundle: ValueRef) -> ~[ValueRef] {
             let _icx = bcx.insn_ctxt("foreign::shim::build_args");
-            return tys.fn_ty.build_shim_args(bcx, tys.arg_tys, llargbundle);
+            tys.fn_ty.build_shim_args(
+                bcx, tys.llsig.llarg_tys, llargbundle)
         }
 
-        fn build_ret(bcx: block, tys: @c_stack_tys,
+        fn build_ret(bcx: block, tys: &ShimTypes,
                      llargbundle: ValueRef, llretval: ValueRef)  {
             let _icx = bcx.insn_ctxt("foreign::shim::build_ret");
-            tys.fn_ty.build_shim_ret(bcx, tys.arg_tys, tys.ret_def,
-                                     llargbundle, llretval);
+            tys.fn_ty.build_shim_ret(
+                bcx, tys.llsig.llarg_tys,
+                tys.ret_def, llargbundle, llretval);
         }
 
         let lname = link_name(ccx, foreign_item);
@@ -239,7 +317,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
                            build_args, build_ret);
     }
 
-    fn base_fn(ccx: @CrateContext, lname: &str, tys: @c_stack_tys,
+    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| {
@@ -250,7 +328,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
     // FIXME (#2535): this is very shaky and probably gets ABIs wrong all
     // over the place
     fn build_direct_fn(ccx: @CrateContext, decl: ValueRef,
-                       item: @ast::foreign_item, tys: @c_stack_tys,
+                       item: @ast::foreign_item, tys: &ShimTypes,
                        cc: lib::llvm::CallConv) {
         let fcx = new_fn_ctxt(ccx, ~[], decl, None);
         let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
@@ -269,66 +347,55 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
     }
 
     fn build_wrap_fn(ccx: @CrateContext,
-                     tys: @c_stack_tys,
+                     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 = ccx.insn_ctxt("foreign::build_wrap_fn");
 
-        fn build_args(bcx: block, tys: @c_stack_tys,
+        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
+                       ccx.upcalls.call_shim_on_c_stack,
+                       build_args, build_ret);
+
+        fn build_args(bcx: block, tys: &ShimTypes,
                       llwrapfn: ValueRef, llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::wrap::build_args");
-            let mut i = 0u;
-            let n = vec::len(tys.arg_tys);
+            let ccx = bcx.ccx();
+            let n = vec::len(tys.llsig.llarg_tys);
             let implicit_args = first_real_arg; // return + env
-            while i < n {
-                let llargval = get_param(llwrapfn, i + implicit_args);
+            for uint::range(0, n) |i| {
+                let mut llargval = get_param(llwrapfn, i + implicit_args);
+
+                // 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);
+                }
+
                 store_inbounds(bcx, llargval, llargbundle, ~[0u, i]);
-                i += 1u;
             }
             let llretptr = get_param(llwrapfn, 0u);
             store_inbounds(bcx, llretptr, llargbundle, ~[0u, n]);
         }
 
-        fn build_ret(bcx: block, _tys: @c_stack_tys,
+        fn build_ret(bcx: block, _tys: &ShimTypes,
                      _llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::wrap::build_ret");
             RetVoid(bcx);
         }
-
-        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
-                       ccx.upcalls.call_shim_on_c_stack,
-                       build_args, build_ret);
-    }
-
-    let mut cc = match abi {
-      ast::foreign_abi_rust_intrinsic |
-      ast::foreign_abi_cdecl => lib::llvm::CCallConv,
-      ast::foreign_abi_stdcall => lib::llvm::X86StdcallCallConv
-    };
-
-    for vec::each(foreign_mod.items) |foreign_item| {
-      match foreign_item.node {
-        ast::foreign_item_fn(*) => {
-          let id = foreign_item.id;
-          if abi != ast::foreign_abi_rust_intrinsic {
-              let llwrapfn = get_item_val(ccx, id);
-              let tys = c_stack_tys(ccx, id);
-              if attr::attrs_contains_name(foreign_item.attrs, "rust_stack") {
-                  build_direct_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);
-              }
-          } else {
-              // Intrinsics are emitted by monomorphic fn
-          }
-        }
-        ast::foreign_item_const(*) => {
-            let ident = ccx.sess.parse_sess.interner.get(foreign_item.ident);
-            ccx.item_symbols.insert(foreign_item.id, copy *ident);
-        }
-      }
     }
 }
 
@@ -842,6 +909,32 @@ pub fn trans_intrinsic(ccx: @CrateContext,
     finish_fn(fcx, lltop);
 }
 
+/**
+ * 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: @CrateContext,
                         +path: ast_map::path,
                         decl: &ast::fn_decl,
@@ -867,28 +960,51 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
     }
 
     fn build_shim_fn(ccx: @CrateContext, +path: ast_map::path,
-                     llrustfn: ValueRef, tys: @c_stack_tys) -> ValueRef {
+                     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 = ccx.insn_ctxt("foreign::foreign::build_shim_fn");
 
-        fn build_args(bcx: block, tys: @c_stack_tys,
+        fn build_args(bcx: block, tys: &ShimTypes,
                       llargbundle: ValueRef) -> ~[ValueRef] {
             let _icx = bcx.insn_ctxt("foreign::extern::shim::build_args");
+            let ccx = bcx.ccx();
             let mut llargvals = ~[];
             let mut i = 0u;
-            let n = vec::len(tys.arg_tys);
+            let n = tys.fn_sig.inputs.len();
             let llretptr = load_inbounds(bcx, llargbundle, ~[0u, n]);
             llargvals.push(llretptr);
             let llenvptr = C_null(T_opaque_box_ptr(bcx.ccx()));
             llargvals.push(llenvptr);
             while i < n {
-                let llargval = load_inbounds(bcx, llargbundle, ~[0u, i]);
+                // Get a pointer to the argument:
+                let mut llargval = GEPi(bcx, llargbundle, [0u, i]);
+
+                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);
+                }
+
                 llargvals.push(llargval);
                 i += 1u;
             }
             return llargvals;
         }
 
-        fn build_ret(_bcx: block, _tys: @c_stack_tys,
+        fn build_ret(_bcx: block, _tys: &ShimTypes,
                      _llargbundle: ValueRef, _llretval: ValueRef)  {
             // Nop. The return pointer in the Rust ABI function
             // is wired directly into the return slot in the shim struct
@@ -904,36 +1020,48 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
     }
 
     fn build_wrap_fn(ccx: @CrateContext, llshimfn: ValueRef,
-                     llwrapfn: ValueRef, tys: @c_stack_tys) {
+                     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 = ccx.insn_ctxt("foreign::foreign::build_wrap_fn");
 
-        fn build_args(bcx: block, tys: @c_stack_tys,
+        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
+                       ccx.upcalls.call_shim_on_rust_stack,
+                       build_args, build_ret);
+
+        fn build_args(bcx: block, tys: &ShimTypes,
                       llwrapfn: ValueRef, llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::foreign::wrap::build_args");
-            tys.fn_ty.build_wrap_args(bcx, tys.ret_ty,
-                                      llwrapfn, llargbundle);
+            tys.fn_ty.build_wrap_args(
+                bcx, tys.llsig.llret_ty,
+                llwrapfn, llargbundle);
         }
 
-        fn build_ret(bcx: block, tys: @c_stack_tys,
+        fn build_ret(bcx: block, tys: &ShimTypes,
                      llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::foreign::wrap::build_ret");
-            tys.fn_ty.build_wrap_ret(bcx, tys.arg_tys, llargbundle);
+            tys.fn_ty.build_wrap_ret(
+                bcx, tys.llsig.llarg_tys, llargbundle);
         }
-
-        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
-                       ccx.upcalls.call_shim_on_rust_stack,
-                       build_args, build_ret);
     }
 
-    let tys = c_stack_tys(ccx, id);
+    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, copy path, decl, body, id);
     // The internal shim function - runs on the Rust stack
-    let llshimfn = build_shim_fn(ccx, path, llrustfn, tys);
+    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)
+    build_wrap_fn(ccx, llshimfn, llwrapfn, &tys)
 }
 
 pub fn register_foreign_fn(ccx: @CrateContext,
@@ -944,11 +1072,8 @@ pub fn register_foreign_fn(ccx: @CrateContext,
                         -> ValueRef {
     let _icx = ccx.insn_ctxt("foreign::register_foreign_fn");
     let t = ty::node_id_to_type(ccx.tcx, node_id);
-    let (llargtys, llretty, ret_ty) = c_arg_and_ret_lltys(ccx, node_id);
-    let ret_def = !ty::type_is_bot(ret_ty) && !ty::type_is_nil(ret_ty);
-    let fn_ty = abi_info(ccx.sess.targ_cfg.arch).
-                    compute_info(llargtys, llretty, ret_def);
-    do fn_ty.decl_fn |fnty| {
+    let tys = shim_types(ccx, node_id);
+    do tys.fn_ty.decl_fn |fnty| {
         register_fn_fuller(ccx, sp, /*bad*/copy path, node_id, attrs,
                            t, lib::llvm::CCallConv, fnty)
     }
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 68eb0852445..2a390a03fa1 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -21,24 +21,22 @@ use util::ppaux;
 use core::option::None;
 use syntax::ast;
 
-pub fn type_of_explicit_arg(ccx: @CrateContext, arg: ty::arg) -> TypeRef {
-    let llty = type_of(ccx, arg.ty);
+pub fn arg_is_indirect(ccx: @CrateContext, arg: &ty::arg) -> bool {
     match ty::resolved_mode(ccx.tcx, arg.mode) {
-        ast::by_val => llty,
-        ast::by_copy => {
-            if ty::type_is_immediate(arg.ty) {
-                llty
-            } else {
-                T_ptr(llty)
-            }
-        }
-        _ => T_ptr(llty)
+        ast::by_val => false,
+        ast::by_copy => !ty::type_is_immediate(arg.ty),
+        ast::by_ref => true
     }
 }
 
+pub fn type_of_explicit_arg(ccx: @CrateContext, arg: &ty::arg) -> TypeRef {
+    let llty = type_of(ccx, arg.ty);
+    if arg_is_indirect(ccx, arg) {T_ptr(llty)} else {llty}
+}
+
 pub fn type_of_explicit_args(ccx: @CrateContext,
                              inputs: &[ty::arg]) -> ~[TypeRef] {
-    inputs.map(|arg| type_of_explicit_arg(ccx, *arg))
+    inputs.map(|arg| type_of_explicit_arg(ccx, arg))
 }
 
 pub fn type_of_fn(cx: @CrateContext, inputs: &[ty::arg],
diff --git a/src/libstd/time.rs b/src/libstd/time.rs
index d6e19515ba6..d768eef9a8c 100644
--- a/src/libstd/time.rs
+++ b/src/libstd/time.rs
@@ -30,10 +30,10 @@ pub mod rustrt {
 
         pub unsafe fn rust_tzset();
         // FIXME: The i64 values can be passed by-val when #2064 is fixed.
-        pub unsafe fn rust_gmtime(&&sec: i64, &&nsec: i32, &&result: Tm);
-        pub unsafe fn rust_localtime(&&sec: i64, &&nsec: i32, &&result: Tm);
-        pub unsafe fn rust_timegm(&&tm: Tm, sec: &mut i64);
-        pub unsafe fn rust_mktime(&&tm: Tm, sec: &mut i64);
+        pub unsafe fn rust_gmtime(sec: i64, nsec: i32, result: &mut Tm);
+        pub unsafe fn rust_localtime(sec: i64, nsec: i32, result: &mut Tm);
+        pub unsafe fn rust_timegm(tm: &Tm, sec: &mut i64);
+        pub unsafe fn rust_mktime(tm: &Tm, sec: &mut i64);
     }
 }
 
@@ -172,7 +172,7 @@ pub fn at_utc(clock: Timespec) -> Tm {
     unsafe {
         let mut Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
-        rustrt::rust_gmtime(sec, nsec, tm);
+        rustrt::rust_gmtime(sec, nsec, &mut tm);
         tm
     }
 }
@@ -187,7 +187,7 @@ pub fn at(clock: Timespec) -> Tm {
     unsafe {
         let mut Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
-        rustrt::rust_localtime(sec, nsec, tm);
+        rustrt::rust_localtime(sec, nsec, &mut tm);
         tm
     }
 }
@@ -217,9 +217,9 @@ pub impl Tm {
         unsafe {
             let mut sec = 0i64;
             if self.tm_gmtoff == 0_i32 {
-                rustrt::rust_timegm(*self, &mut sec);
+                rustrt::rust_timegm(self, &mut sec);
             } else {
-                rustrt::rust_mktime(*self, &mut sec);
+                rustrt::rust_mktime(self, &mut sec);
             }
             Timespec::new(sec, self.tm_nsec)
         }
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 8d83e2036b9..5a9de9735ba 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -434,18 +434,18 @@ rust_tzset() {
 }
 
 extern "C" CDECL void
-rust_gmtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
+rust_gmtime(int64_t sec, int32_t nsec, rust_tm *timeptr) {
     tm tm;
-    time_t s = *sec;
+    time_t s = sec;
     GMTIME(&s, &tm);
 
-    tm_to_rust_tm(&tm, timeptr, 0, "UTC", *nsec);
+    tm_to_rust_tm(&tm, timeptr, 0, "UTC", nsec);
 }
 
 extern "C" CDECL void
-rust_localtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
+rust_localtime(int64_t sec, int32_t nsec, rust_tm *timeptr) {
     tm tm;
-    time_t s = *sec;
+    time_t s = sec;
     LOCALTIME(&s, &tm);
 
 #if defined(__WIN32__)
@@ -457,7 +457,7 @@ rust_localtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
     const char *zone = tm.tm_zone;
 #endif
 
-    tm_to_rust_tm(&tm, timeptr, gmtoff, zone, *nsec);
+    tm_to_rust_tm(&tm, timeptr, gmtoff, zone, nsec);
 }
 
 extern "C" CDECL void
@@ -844,6 +844,38 @@ rust_readdir() {
 
 #endif
 
+// These functions are used in the unit tests for C ABI calls.
+
+extern "C" CDECL uint32_t
+rust_dbg_extern_identity_u32(uint32_t u) {
+    return u;
+}
+
+extern "C" CDECL uint64_t
+rust_dbg_extern_identity_u64(uint64_t u) {
+    return u;
+}
+
+struct TwoU64s {
+    uint64_t one;
+    uint64_t two;
+};
+
+extern "C" CDECL TwoU64s
+rust_dbg_extern_identity_TwoU64s(TwoU64s u) {
+    return u;
+}
+
+extern "C" CDECL double
+rust_dbg_extern_identity_double(double u) {
+    return u;
+}
+
+extern "C" CDECL char
+rust_dbg_extern_identity_u8(char u) {
+    return u;
+}
+
 
 //
 // Local Variables:
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 886d945b144..7fb6334ca75 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -195,4 +195,9 @@ rust_get_exchange_count_ptr
 rust_get_sched_tls_key
 swap_registers
 rust_readdir
-rust_opendir
\ No newline at end of file
+rust_opendir
+rust_dbg_extern_identity_u32
+rust_dbg_extern_identity_u64
+rust_dbg_extern_identity_TwoU64s
+rust_dbg_extern_identity_double
+rust_dbg_extern_identity_u8
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 7686dcd4ff4..12b305720cc 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -546,8 +546,8 @@ extern "C" LLVMValueRef LLVMInlineAsm(LLVMTypeRef Ty,
                                       char *Constraints,
                                       LLVMBool HasSideEffects,
                                       LLVMBool IsAlignStack,
-                                      InlineAsm::AsmDialect Dialect) {
+                                      unsigned Dialect) {
     return wrap(InlineAsm::get(unwrap<FunctionType>(Ty), AsmString,
                                Constraints, HasSideEffects,
-                               IsAlignStack, Dialect));
+                               IsAlignStack, (InlineAsm::AsmDialect) Dialect));
 }
diff --git a/src/test/run-pass/extern-pass-TwoU64s-ref.rs b/src/test/run-pass/extern-pass-TwoU64s-ref.rs
new file mode 100644
index 00000000000..00754afa703
--- /dev/null
+++ b/src/test/run-pass/extern-pass-TwoU64s-ref.rs
@@ -0,0 +1,31 @@
+// Copyright 2012 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.
+
+// Test that we ignore modes when calling extern functions.
+
+// xfail-test --- broken on 32-bit ABIs! (#5347)
+
+#[deriving_eq]
+struct TwoU64s {
+    one: u64, two: u64
+}
+
+pub extern {
+    pub fn rust_dbg_extern_identity_TwoU64s(&&u: TwoU64s) -> TwoU64s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU64s {one: 22, two: 23};
+        let y = rust_dbg_extern_identity_TwoU64s(x);
+        fail_unless!(x == y);
+    }
+}
+
diff --git a/src/test/run-pass/extern-pass-TwoU64s.rs b/src/test/run-pass/extern-pass-TwoU64s.rs
new file mode 100644
index 00000000000..2baf383ce54
--- /dev/null
+++ b/src/test/run-pass/extern-pass-TwoU64s.rs
@@ -0,0 +1,32 @@
+// Copyright 2012 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.
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+// xfail-test --- broken on 32-bit ABIs! (#5347)
+
+#[deriving_eq]
+struct TwoU64s {
+    one: u64, two: u64
+}
+
+pub extern {
+    pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU64s {one: 22, two: 23};
+        let y = rust_dbg_extern_identity_TwoU64s(x);
+        fail_unless!(x == y);
+    }
+}
+
diff --git a/src/test/run-pass/extern-pass-char.rs b/src/test/run-pass/extern-pass-char.rs
new file mode 100644
index 00000000000..104ea342cd5
--- /dev/null
+++ b/src/test/run-pass/extern-pass-char.rs
@@ -0,0 +1,22 @@
+// Copyright 2012 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.
+
+// Test a function that takes/returns a u8.
+
+pub extern {
+    pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
+}
+
+pub fn main() {
+    unsafe {
+        fail_unless!(22_u8 == rust_dbg_extern_identity_u8(22_u8));
+    }
+}
+
diff --git a/src/test/run-pass/extern-pass-double.rs b/src/test/run-pass/extern-pass-double.rs
new file mode 100644
index 00000000000..afdec4d1002
--- /dev/null
+++ b/src/test/run-pass/extern-pass-double.rs
@@ -0,0 +1,20 @@
+// Copyright 2012 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.
+
+pub extern {
+    pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
+}
+
+pub fn main() {
+    unsafe {
+        fail_unless!(22.0_f64 == rust_dbg_extern_identity_double(22.0_f64));
+    }
+}
+
diff --git a/src/test/run-pass/extern-pass-u32.rs b/src/test/run-pass/extern-pass-u32.rs
new file mode 100644
index 00000000000..0d6220e7b25
--- /dev/null
+++ b/src/test/run-pass/extern-pass-u32.rs
@@ -0,0 +1,22 @@
+// Copyright 2012 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.
+
+// Test a function that takes/returns a u32.
+
+pub extern {
+    pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
+}
+
+pub fn main() {
+    unsafe {
+        fail_unless!(22_u32 == rust_dbg_extern_identity_u32(22_u32));
+    }
+}
+
diff --git a/src/test/run-pass/extern-pass-u64.rs b/src/test/run-pass/extern-pass-u64.rs
new file mode 100644
index 00000000000..31777035238
--- /dev/null
+++ b/src/test/run-pass/extern-pass-u64.rs
@@ -0,0 +1,22 @@
+// Copyright 2012 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.
+
+// Test a call to a function that takes/returns a u64.
+
+pub extern {
+    pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
+}
+
+pub fn main() {
+    unsafe {
+        fail_unless!(22_u64 == rust_dbg_extern_identity_u64(22_u64));
+    }
+}
+