about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-10-10 19:56:22 -0700
committerbors <bors@rust-lang.org>2013-10-10 19:56:22 -0700
commit0daced63fc1a4f153e77c55cb6c1a2d02d3bb6bd (patch)
tree3496d7d393ad27ff6a6904b7cbdd9bfd859daeb3
parentafa42a02379637fb621ffb2f6618344aaabadfcb (diff)
parent95fc31ae9bf8e9f859e6a7ceb43d645ebef33905 (diff)
downloadrust-0daced63fc1a4f153e77c55cb6c1a2d02d3bb6bd.tar.gz
rust-0daced63fc1a4f153e77c55cb6c1a2d02d3bb6bd.zip
auto merge of #9517 : crabtw/rust/cabi, r=nikomatsakis
I borrow some ideas from clang's ABIInfo.h and TargetInfo.cpp.
LLVMType is replaced with ArgType, which is similar to clang's ABIArgInfo,
and I also merge attrs of FnType into it.

Now ABI implementation doesn't need to insert hidden return pointer
to arg_tys of FnType. Instead it is handled in foreign.rs.

This change also fixes LLVM assertion failure when compiling MIPS target.
-rw-r--r--src/librustc/middle/trans/cabi.rs75
-rw-r--r--src/librustc/middle/trans/cabi_arm.rs39
-rw-r--r--src/librustc/middle/trans/cabi_mips.rs70
-rw-r--r--src/librustc/middle/trans/cabi_x86.rs37
-rw-r--r--src/librustc/middle/trans/cabi_x86_64.rs46
-rw-r--r--src/librustc/middle/trans/foreign.rs127
6 files changed, 206 insertions, 188 deletions
diff --git a/src/librustc/middle/trans/cabi.rs b/src/librustc/middle/trans/cabi.rs
index 005483a075f..8648bf6f348 100644
--- a/src/librustc/middle/trans/cabi.rs
+++ b/src/librustc/middle/trans/cabi.rs
@@ -18,10 +18,62 @@ use middle::trans::cabi_mips;
 use middle::trans::type_::Type;
 use syntax::abi::{X86, X86_64, Arm, Mips};
 
+#[deriving(Clone, Eq)]
+pub enum ArgKind {
+    /// Pass the argument directly using the normal converted
+    /// LLVM type or by coercing to another specified type
+    Direct,
+    /// Pass the argument indirectly via a hidden pointer
+    Indirect
+}
+
+/// Information about how a specific C type
+/// should be passed to or returned from a function
+///
+/// This is borrowed from clang's ABIInfo.h
 #[deriving(Clone)]
-pub struct LLVMType {
-    cast: bool,
-    ty: Type
+pub struct ArgType {
+    kind: ArgKind,
+    /// Original LLVM type
+    ty: Type,
+    /// Coerced LLVM Type
+    cast: option::Option<Type>,
+    /// Dummy argument, which is emitted before the real argument
+    pad: option::Option<Type>,
+    /// LLVM attribute of argument
+    attr: option::Option<Attribute>
+}
+
+impl ArgType {
+    pub fn direct(ty: Type, cast: option::Option<Type>,
+                            pad: option::Option<Type>,
+                            attr: option::Option<Attribute>) -> ArgType {
+        ArgType {
+            kind: Direct,
+            ty: ty,
+            cast: cast,
+            pad: pad,
+            attr: attr
+        }
+    }
+
+    pub fn indirect(ty: Type, attr: option::Option<Attribute>) -> ArgType {
+        ArgType {
+            kind: Indirect,
+            ty: ty,
+            cast: option::None,
+            pad: option::None,
+            attr: attr
+        }
+    }
+
+    pub fn is_direct(&self) -> bool {
+        return self.kind == Direct;
+    }
+
+    pub fn is_indirect(&self) -> bool {
+        return self.kind == Indirect;
+    }
 }
 
 /// Metadata describing how the arguments to a native function
@@ -30,22 +82,11 @@ pub struct LLVMType {
 /// 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],
-
-    /// 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>],
+    /// The LLVM types of each argument.
+    arg_tys: ~[ArgType],
 
     /// LLVM return type.
-    ret_ty: LLVMType,
-
-    /// If true, then an implicit pointer should be added for the result.
-    sret: bool
+    ret_ty: ArgType,
 }
 
 pub fn compute_abi_info(ccx: &mut CrateContext,
diff --git a/src/librustc/middle/trans/cabi_arm.rs b/src/librustc/middle/trans/cabi_arm.rs
index 38431bb9293..5add2250038 100644
--- a/src/librustc/middle/trans/cabi_arm.rs
+++ b/src/librustc/middle/trans/cabi_arm.rs
@@ -11,14 +11,14 @@
 #[allow(non_uppercase_pattern_statics)];
 
 use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
-use lib::llvm::{Attribute, StructRetAttribute};
-use middle::trans::cabi::{FnType, LLVMType};
+use lib::llvm::StructRetAttribute;
+use middle::trans::cabi::{FnType, ArgType};
 use middle::trans::context::CrateContext;
 
 use middle::trans::type_::Type;
 
 use std::num;
-use std::option::{Option, None, Some};
+use std::option::{None, Some};
 
 fn align_up_to(off: uint, a: uint) -> uint {
     return (off + a - 1u) / a * a;
@@ -85,9 +85,9 @@ fn ty_size(ty: Type) -> uint {
     }
 }
 
-fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
+fn classify_ret_ty(ty: Type) -> ArgType {
     if is_reg_ty(ty) {
-        return (LLVMType { cast: false, ty: ty }, None);
+        return ArgType::direct(ty, None, None, None);
     }
     let size = ty_size(ty);
     if size <= 4 {
@@ -98,14 +98,14 @@ fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
         } else {
             Type::i32()
         };
-        return (LLVMType { cast: true, ty: llty }, None);
+        return ArgType::direct(ty, Some(llty), None, None);
     }
-    (LLVMType { cast: false, ty: ty.ptr_to() }, Some(StructRetAttribute))
+    ArgType::indirect(ty, Some(StructRetAttribute))
 }
 
-fn classify_arg_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
+fn classify_arg_ty(ty: Type) -> ArgType {
     if is_reg_ty(ty) {
-        return (LLVMType { cast: false, ty: ty }, None);
+        return ArgType::direct(ty, None, None, None);
     }
     let align = ty_align(ty);
     let size = ty_size(ty);
@@ -114,7 +114,7 @@ fn classify_arg_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
     } else {
         Type::array(&Type::i64(), ((size + 7) / 8) as u64)
     };
-    (LLVMType { cast: true, ty: llty }, None)
+    ArgType::direct(ty, Some(llty), None, None)
 }
 
 fn is_reg_ty(ty: Type) -> bool {
@@ -132,32 +132,19 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
                         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);
+        let ty = classify_arg_ty(aty);
         arg_tys.push(ty);
-        attrs.push(attr);
     }
 
-    let (ret_ty, ret_attr) = if ret_def {
+    let ret_ty = if ret_def {
         classify_ret_ty(rty)
     } else {
-        (LLVMType { cast: false, ty: Type::void() }, None)
+        ArgType::direct(Type::void(), None, None, None)
     };
 
-    let mut ret_ty = ret_ty;
-
-    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() };
-    }
-
     return FnType {
         arg_tys: arg_tys,
         ret_ty: ret_ty,
-        attrs: attrs,
-        sret: sret
     };
 }
diff --git a/src/librustc/middle/trans/cabi_mips.rs b/src/librustc/middle/trans/cabi_mips.rs
index 8bdef802afb..3f2d18ddbe2 100644
--- a/src/librustc/middle/trans/cabi_mips.rs
+++ b/src/librustc/middle/trans/cabi_mips.rs
@@ -12,9 +12,8 @@
 
 use std::libc::c_uint;
 use std::num;
-use std::vec;
 use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
-use lib::llvm::{Attribute, StructRetAttribute};
+use lib::llvm::StructRetAttribute;
 use middle::trans::context::CrateContext;
 use middle::trans::context::task_llcx;
 use middle::trans::cabi::*;
@@ -86,15 +85,15 @@ fn ty_size(ty: Type) -> uint {
     }
 }
 
-fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
-    return if is_reg_ty(ty) {
-        (LLVMType { cast: false, ty: ty }, None)
+fn classify_ret_ty(ty: Type) -> ArgType {
+    if is_reg_ty(ty) {
+        ArgType::direct(ty, None, None, None)
     } else {
-        (LLVMType { cast: false, ty: ty.ptr_to() }, Some(StructRetAttribute))
-    };
+        ArgType::indirect(ty, Some(StructRetAttribute))
+    }
 }
 
-fn classify_arg_ty(ty: Type, offset: &mut uint) -> (LLVMType, Option<Attribute>) {
+fn classify_arg_ty(ty: Type, offset: &mut uint) -> ArgType {
     let orig_offset = *offset;
     let size = ty_size(ty) * 8;
     let mut align = ty_align(ty);
@@ -103,20 +102,16 @@ fn classify_arg_ty(ty: Type, offset: &mut uint) -> (LLVMType, Option<Attribute>)
     *offset = align_up_to(*offset, align);
     *offset += align_up_to(size, align * 8) / 8;
 
-    let padding = padding_ty(align, orig_offset);
-    return if !is_reg_ty(ty) {
-        (LLVMType {
-            cast: true,
-            ty: struct_ty(ty, padding, true)
-        }, None)
-    } else if padding.is_some() {
-        (LLVMType {
-            cast: true,
-            ty: struct_ty(ty, padding, false)
-        }, None)
+    if is_reg_ty(ty) {
+        ArgType::direct(ty, None, None, None)
     } else {
-        (LLVMType { cast: false, ty: ty }, None)
-    };
+        ArgType::direct(
+            ty,
+            Some(struct_ty(ty)),
+            padding_ty(align, orig_offset),
+            None
+        )
+    }
 }
 
 fn is_reg_ty(ty: Type) -> bool {
@@ -157,18 +152,9 @@ fn coerce_to_int(size: uint) -> ~[Type] {
     args
 }
 
-fn struct_ty(ty: Type,
-             padding: Option<Type>,
-             coerce: bool) -> Type {
+fn struct_ty(ty: Type) -> Type {
     let size = ty_size(ty) * 8;
-    let mut fields = padding.map_default(~[], |p| ~[p]);
-
-    if coerce {
-        fields = vec::append(fields, coerce_to_int(size));
-    } else {
-        fields.push(ty);
-    }
-
+    let fields = coerce_to_int(size);
     return Type::struct_(fields, false);
 }
 
@@ -176,35 +162,23 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
                         atys: &[Type],
                         rty: Type,
                         ret_def: bool) -> FnType {
-    let (ret_ty, ret_attr) = if ret_def {
+    let ret_ty = if ret_def {
         classify_ret_ty(rty)
     } else {
-        (LLVMType { cast: false, ty: Type::void() }, None)
+        ArgType::direct(Type::void(), None, None, None)
     };
 
-    let mut ret_ty = ret_ty;
-
-    let sret = ret_attr.is_some();
+    let sret = ret_ty.is_indirect();
     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);
+        let ty = 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() };
-    }
-
     return FnType {
         arg_tys: arg_tys,
         ret_ty: ret_ty,
-        attrs: attrs,
-        sret: sret
     };
 }
diff --git a/src/librustc/middle/trans/cabi_x86.rs b/src/librustc/middle/trans/cabi_x86.rs
index de53d8dd3da..244087f814c 100644
--- a/src/librustc/middle/trans/cabi_x86.rs
+++ b/src/librustc/middle/trans/cabi_x86.rs
@@ -21,16 +21,10 @@ pub fn compute_abi_info(ccx: &mut CrateContext,
                         rty: Type,
                         ret_def: bool) -> FnType {
     let mut arg_tys = ~[];
-    let mut attrs = ~[];
 
     let ret_ty;
-    let sret;
     if !ret_def {
-        ret_ty = LLVMType {
-            cast: false,
-            ty: Type::void(),
-        };
-        sret = false;
+        ret_ty = ArgType::direct(Type::void(), None, None, None);
     } else if rty.kind() == Struct {
         // Returning a structure. Most often, this will use
         // a hidden first argument. On some platforms, though,
@@ -58,43 +52,22 @@ pub fn compute_abi_info(ccx: &mut CrateContext,
 
         match strategy {
             RetValue(t) => {
-                ret_ty = LLVMType {
-                    cast: true,
-                    ty: t
-                };
-                sret = false;
+                ret_ty = ArgType::direct(rty, Some(t), None, None);
             }
             RetPointer => {
-                arg_tys.push(LLVMType {
-                    cast: false,
-                    ty: rty.ptr_to()
-                });
-                attrs.push(Some(StructRetAttribute));
-
-                ret_ty = LLVMType {
-                    cast: false,
-                    ty: Type::void(),
-                };
-                sret = true;
+                ret_ty = ArgType::indirect(rty, Some(StructRetAttribute));
             }
         }
     } else {
-        ret_ty = LLVMType {
-            cast: false,
-            ty: rty
-        };
-        sret = false;
+        ret_ty = ArgType::direct(rty, None, None, None);
     }
 
     for &a in atys.iter() {
-        arg_tys.push(LLVMType { cast: false, ty: a });
-        attrs.push(None);
+        arg_tys.push(ArgType::direct(a, None, None, None));
     }
 
     return FnType {
         arg_tys: arg_tys,
         ret_ty: ret_ty,
-        attrs: attrs,
-        sret: sret
     };
 }
diff --git a/src/librustc/middle/trans/cabi_x86_64.rs b/src/librustc/middle/trans/cabi_x86_64.rs
index fad30885771..b35ffe5c965 100644
--- a/src/librustc/middle/trans/cabi_x86_64.rs
+++ b/src/librustc/middle/trans/cabi_x86_64.rs
@@ -22,8 +22,6 @@ use middle::trans::context::CrateContext;
 use middle::trans::type_::Type;
 
 use std::num;
-use std::option;
-use std::option::Option;
 use std::vec;
 
 #[deriving(Clone, Eq)]
@@ -340,50 +338,34 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
                         ret_def: bool) -> FnType {
     fn x86_64_ty(ty: Type,
                  is_mem_cls: &fn(cls: &[RegClass]) -> bool,
-                 attr: Attribute) -> (LLVMType, Option<Attribute>) {
+                 attr: Attribute) -> ArgType {
 
-        let (cast, attr, ty) = if !ty.is_reg_ty() {
+        if !ty.is_reg_ty() {
             let cls = classify_ty(ty);
             if is_mem_cls(cls) {
-                (false, option::Some(attr), ty.ptr_to())
+                ArgType::indirect(ty, Some(attr))
             } else {
-                (true, option::None, llreg_ty(cls))
+                ArgType::direct(ty, Some(llreg_ty(cls)), None, None)
             }
         } else {
-            (false, option::None, ty)
-        };
-
-        (LLVMType { cast: cast, ty: ty }, attr)
+            ArgType::direct(ty, None, None, None)
+        }
     }
 
     let mut arg_tys = ~[];
-    let mut attrs = ~[];
     for t in atys.iter() {
-        let (ty, attr) = x86_64_ty(*t, |cls| cls.is_pass_byval(), ByValAttribute);
+        let ty = x86_64_ty(*t, |cls| cls.is_pass_byval(), ByValAttribute);
         arg_tys.push(ty);
-        attrs.push(attr);
-    }
-    let (ret_ty, ret_attr) = x86_64_ty(rty, |cls| cls.is_ret_bysret(),
-                                       StructRetAttribute);
-    let mut ret_ty = ret_ty;
-    let sret = ret_attr.is_some();
-    if sret {
-        arg_tys = vec::append(~[ret_ty], arg_tys);
-        ret_ty = LLVMType {
-                   cast:  false,
-                   ty: Type::void()
-                 };
-        attrs = vec::append(~[ret_attr], attrs);
-    } else if !ret_def {
-        ret_ty = LLVMType {
-                   cast: false,
-                   ty: Type::void()
-                 };
     }
+
+    let ret_ty = if ret_def {
+        x86_64_ty(rty, |cls| cls.is_ret_bysret(), StructRetAttribute)
+    } else {
+        ArgType::direct(Type::void(), None, None, None)
+    };
+
     return FnType {
         arg_tys: arg_tys,
         ret_ty: ret_ty,
-        attrs: attrs,
-        sret: sret
     };
 }
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index f8554ea397a..9c83f732204 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -11,7 +11,7 @@
 
 use back::{link};
 use std::libc::c_uint;
-use lib::llvm::{ValueRef, Attribute, CallConv, StructRetAttribute};
+use lib::llvm::{ValueRef, CallConv, StructRetAttribute};
 use lib::llvm::llvm;
 use lib;
 use middle::trans::machine;
@@ -183,8 +183,7 @@ pub fn trans_native_call(bcx: @mut Block,
                                          llsig.llret_ty,
                                          ret_def);
 
-    let all_arg_tys: &[cabi::LLVMType] = fn_type.arg_tys;
-    let all_attributes: &[Option<Attribute>] = fn_type.attrs;
+    let arg_tys: &[cabi::ArgType] = fn_type.arg_tys;
 
     let mut llargs_foreign = ~[];
 
@@ -192,20 +191,18 @@ pub fn trans_native_call(bcx: @mut Block,
     // 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 {
+    if fn_type.ret_ty.is_indirect() {
+        match fn_type.ret_ty.cast {
+            Some(ty) => {
                 let llcastedretptr =
-                    BitCast(bcx, llretptr, all_arg_tys[0].ty.ptr_to());
+                    BitCast(bcx, llretptr, ty.ptr_to());
                 llargs_foreign.push(llcastedretptr);
-            } else {
+            }
+            None => {
                 llargs_foreign.push(llretptr);
             }
-            (all_arg_tys.tail(), all_attributes.tail())
-        } else {
-            (all_arg_tys, all_attributes)
         }
-    };
+    }
 
     for (i, &llarg_rust) in llargs_rust.iter().enumerate() {
         let mut llarg_rust = llarg_rust;
@@ -231,16 +228,16 @@ pub fn trans_native_call(bcx: @mut Block,
                ccx.tn.val_to_str(llarg_rust));
 
         // 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());
+        match arg_tys[i].cast {
+            Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()),
+            None => ()
         }
 
         debug2!("llarg_rust={} (after casting)",
                ccx.tn.val_to_str(llarg_rust));
 
         // Finally, load the value if needed for the foreign ABI
-        let foreign_indirect = attributes[i].is_some();
+        let foreign_indirect = arg_tys[i].is_indirect();
         let llarg_foreign = if foreign_indirect {
             llarg_rust
         } else {
@@ -250,6 +247,11 @@ pub fn trans_native_call(bcx: @mut Block,
         debug2!("argument {}, llarg_foreign={}",
                i, ccx.tn.val_to_str(llarg_foreign));
 
+        // fill padding with undef value
+        match arg_tys[i].pad {
+            Some(ty) => llargs_foreign.push(C_undef(ty)),
+            None => ()
+        }
         llargs_foreign.push(llarg_foreign);
     }
 
@@ -268,7 +270,7 @@ pub fn trans_native_call(bcx: @mut Block,
     // any attributes with ABI implications directly to the call instruction. Right now, the
     // only attribute we need to worry about is `sret`.
     let attrs;
-    if fn_type.sret {
+    if fn_type.ret_ty.is_indirect() {
         attrs = &[(1, StructRetAttribute)];
     } else {
         attrs = &[];
@@ -280,9 +282,12 @@ pub fn trans_native_call(bcx: @mut Block,
     // 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 {
+    if ret_def && !fn_type.ret_ty.is_indirect() {
         let llrust_ret_ty = llsig.llret_ty;
-        let llforeign_ret_ty = fn_type.ret_ty.ty;
+        let llforeign_ret_ty = match fn_type.ret_ty.cast {
+            Some(ty) => ty,
+            None => fn_type.ret_ty.ty
+        };
 
         debug2!("llretptr={}", ccx.tn.val_to_str(llretptr));
         debug2!("llforeign_retval={}", ccx.tn.val_to_str(llforeign_retval));
@@ -474,17 +479,17 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
         // 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;
+        let next_foreign_arg: &fn(pad: bool) -> c_uint = {
+            |pad: bool| {
+                next_foreign_arg_counter += if pad { 2 } else { 1 };
                 next_foreign_arg_counter - 1
             }
         };
 
         // If there is an out pointer on the foreign function
         let foreign_outptr = {
-            if tys.fn_ty.sret {
-                Some(llvm::LLVMGetParam(llwrapfn, next_foreign_arg()))
+            if tys.fn_ty.ret_ty.is_indirect() {
+                Some(llvm::LLVMGetParam(llwrapfn, next_foreign_arg(false)))
             } else {
                 None
             }
@@ -553,9 +558,12 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
         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 llforeign_arg_ty = tys.fn_ty.arg_tys[i];
+            let foreign_indirect = llforeign_arg_ty.is_indirect();
+
+            // skip padding
+            let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some());
             let mut llforeign_arg = llvm::LLVMGetParam(llwrapfn, foreign_index);
 
             debug2!("llforeign_arg \\#{}: {}",
@@ -578,7 +586,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
             // 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 {
+            if llforeign_arg_ty.cast.is_some() {
                 assert!(!foreign_indirect);
                 llforeign_arg = llvm::LLVMBuildBitCast(
                     builder, llforeign_arg,
@@ -604,7 +612,10 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
         };
 
         // Get the return value where the foreign fn expects it.
-        let llforeign_ret_ty = tys.fn_ty.ret_ty.ty;
+        let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast {
+            Some(ty) => ty,
+            None => tys.fn_ty.ret_ty.ty
+        };
         match foreign_outptr {
             None if !tys.ret_def => {
                 // Function returns `()` or `bot`, which in Rust is the LLVM
@@ -744,9 +755,38 @@ fn foreign_types_for_fn_ty(ccx: &mut CrateContext,
 }
 
 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;
+    let mut llargument_tys = ~[];
+
+    let ret_ty = tys.fn_ty.ret_ty;
+    let llreturn_ty = if ret_ty.is_indirect() {
+        llargument_tys.push(ret_ty.ty.ptr_to());
+        Type::void()
+    } else {
+        match ret_ty.cast {
+            Some(ty) => ty,
+            None => ret_ty.ty
+        }
+    };
+
+    for &arg_ty in tys.fn_ty.arg_tys.iter() {
+        // add padding
+        match arg_ty.pad {
+            Some(ty) => llargument_tys.push(ty),
+            None => ()
+        }
+
+        let llarg_ty = if arg_ty.is_indirect() {
+            arg_ty.ty.ptr_to()
+        } else {
+            match arg_ty.cast {
+                Some(ty) => ty,
+                None => arg_ty.ty
+            }
+        };
+
+        llargument_tys.push(llarg_ty);
+    }
+
     Type::func(llargument_tys, &llreturn_ty)
 }
 
@@ -757,8 +797,27 @@ pub fn lltype_for_foreign_fn(ccx: &mut CrateContext, ty: ty::t) -> Type {
 
 fn add_argument_attributes(tys: &ForeignTypes,
                            llfn: ValueRef) {
-    for (i, a) in tys.fn_ty.attrs.iter().enumerate() {
-        match *a {
+    let mut i = 0;
+
+    if tys.fn_ty.ret_ty.is_indirect() {
+        match tys.fn_ty.ret_ty.attr {
+            Some(attr) => {
+                let llarg = get_param(llfn, i);
+                unsafe {
+                    llvm::LLVMAddAttribute(llarg, attr as c_uint);
+                }
+            }
+            None => {}
+        }
+
+        i += 1;
+    }
+
+    for &arg_ty in tys.fn_ty.arg_tys.iter() {
+        // skip padding
+        if arg_ty.pad.is_some() { i += 1; }
+
+        match arg_ty.attr {
             Some(attr) => {
                 let llarg = get_param(llfn, i);
                 unsafe {
@@ -767,5 +826,7 @@ fn add_argument_attributes(tys: &ForeignTypes,
             }
             None => ()
         }
+
+        i += 1;
     }
 }