about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-09-20 05:16:06 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-11-19 02:14:31 +0200
commit88f70323e451f63f812c6cd92cc1d654b32971e1 (patch)
treea7531ba09b2de91cb954d2ced959dc16b6b0791c
parentf2e7e17d9e9b8faeb13c388c56ef135978a77c58 (diff)
downloadrust-88f70323e451f63f812c6cd92cc1d654b32971e1.tar.gz
rust-88f70323e451f63f812c6cd92cc1d654b32971e1.zip
rustc_trans: nest abi::ArgType's for fat pointers instead of eagerly flattening.
-rw-r--r--src/librustc_trans/abi.rs108
-rw-r--r--src/librustc_trans/common.rs20
-rw-r--r--src/librustc_trans/mir/block.rs75
-rw-r--r--src/librustc_trans/mir/mod.rs95
4 files changed, 161 insertions, 137 deletions
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index 6aa49080dd0..689976b6c42 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -420,7 +420,7 @@ impl CastTarget {
 /// should be passed to or returned from a function
 ///
 /// This is borrowed from clang's ABIInfo.h
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
 pub struct ArgType<'tcx> {
     kind: ArgKind,
     pub layout: FullLayout<'tcx>,
@@ -429,7 +429,8 @@ pub struct ArgType<'tcx> {
     /// Dummy argument, which is emitted before the real argument.
     pub pad: Option<Reg>,
     /// Attributes of argument.
-    pub attrs: ArgAttributes
+    pub attrs: ArgAttributes,
+    pub nested: Vec<ArgType<'tcx>>
 }
 
 impl<'a, 'tcx> ArgType<'tcx> {
@@ -439,11 +440,13 @@ impl<'a, 'tcx> ArgType<'tcx> {
             layout,
             cast: None,
             pad: None,
-            attrs: ArgAttributes::default()
+            attrs: ArgAttributes::default(),
+            nested: vec![]
         }
     }
 
     pub fn make_indirect(&mut self, ccx: &CrateContext<'a, 'tcx>) {
+        assert!(self.nested.is_empty());
         assert_eq!(self.kind, ArgKind::Direct);
 
         // Wipe old attributes, likely not valid through indirection.
@@ -460,6 +463,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
     }
 
     pub fn ignore(&mut self) {
+        assert!(self.nested.is_empty());
         assert_eq!(self.kind, ArgKind::Direct);
         self.kind = ArgKind::Ignore;
     }
@@ -482,10 +486,12 @@ impl<'a, 'tcx> ArgType<'tcx> {
     }
 
     pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
+        assert!(self.nested.is_empty());
         self.cast = Some(target.into());
     }
 
     pub fn pad_with(&mut self, reg: Reg) {
+        assert!(self.nested.is_empty());
         self.pad = Some(reg);
     }
 
@@ -561,6 +567,12 @@ impl<'a, 'tcx> ArgType<'tcx> {
     }
 
     pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: LvalueRef<'tcx>) {
+        if !self.nested.is_empty() {
+            for (i, arg) in self.nested.iter().enumerate() {
+                arg.store_fn_arg(bcx, idx, dst.project_field(bcx, i));
+            }
+            return;
+        }
         if self.pad.is_some() {
             *idx += 1;
         }
@@ -578,7 +590,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
 ///
 /// I will do my best to describe this structure, but these
 /// comments are reverse-engineered and may be inaccurate. -NDM
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 pub struct FnType<'tcx> {
     /// The LLVM types of each argument.
     pub args: Vec<ArgType<'tcx>>,
@@ -613,7 +625,8 @@ impl<'a, 'tcx> FnType<'tcx> {
                       extra_args: &[Ty<'tcx>]) -> FnType<'tcx> {
         let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args);
         // Don't pass the vtable, it's not an argument of the virtual fn.
-        fn_ty.args[1].ignore();
+        assert_eq!(fn_ty.args[0].nested.len(), 2);
+        fn_ty.args[0].nested[1].ignore();
         fn_ty.adjust_for_abi(ccx, sig);
         fn_ty
     }
@@ -766,7 +779,7 @@ impl<'a, 'tcx> FnType<'tcx> {
         for ty in inputs.iter().chain(extra_args.iter()) {
             let mut arg = arg_of(ty, false);
 
-            if let ty::layout::Layout::FatPointer { .. } = *arg.layout.layout {
+            if type_is_fat_ptr(ccx, ty) {
                 let mut data = ArgType::new(arg.layout.field(ccx, 0));
                 let mut info = ArgType::new(arg.layout.field(ccx, 1));
 
@@ -780,14 +793,16 @@ impl<'a, 'tcx> FnType<'tcx> {
                         info.attrs.set(ArgAttribute::NoAlias);
                     }
                 }
-                args.push(data);
-                args.push(info);
+                // FIXME(eddyb) other ABIs don't have logic for nested.
+                if rust_abi {
+                    arg.nested = vec![data, info];
+                }
             } else {
                 if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
                     arg.attrs.set_dereferenceable(ccx.size_of(inner));
                 }
-                args.push(arg);
             }
+            args.push(arg);
         }
 
         FnType {
@@ -854,6 +869,13 @@ impl<'a, 'tcx> FnType<'tcx> {
             }
             for arg in &mut self.args {
                 if arg.is_ignore() { continue; }
+                if !arg.nested.is_empty() {
+                    for arg in &mut arg.nested {
+                        assert!(arg.nested.is_empty());
+                        fixup(arg);
+                    }
+                    continue;
+                }
                 fixup(arg);
             }
             if self.ret.is_indirect() {
@@ -915,24 +937,36 @@ impl<'a, 'tcx> FnType<'tcx> {
             ccx.immediate_llvm_type_of(self.ret.layout.ty)
         };
 
-        for arg in &self.args {
-            if arg.is_ignore() {
-                continue;
-            }
-            // add padding
-            if let Some(ty) = arg.pad {
-                llargument_tys.push(ty.llvm_type(ccx));
-            }
+        {
+            let mut push = |arg: &ArgType<'tcx>| {
+                if arg.is_ignore() {
+                    return;
+                }
+                // add padding
+                if let Some(ty) = arg.pad {
+                    llargument_tys.push(ty.llvm_type(ccx));
+                }
 
-            let llarg_ty = if arg.is_indirect() {
-                arg.memory_ty(ccx).ptr_to()
-            } else if let Some(cast) = arg.cast {
-                cast.llvm_type(ccx)
-            } else {
-                ccx.immediate_llvm_type_of(arg.layout.ty)
-            };
+                let llarg_ty = if arg.is_indirect() {
+                    arg.memory_ty(ccx).ptr_to()
+                } else if let Some(cast) = arg.cast {
+                    cast.llvm_type(ccx)
+                } else {
+                    ccx.immediate_llvm_type_of(arg.layout.ty)
+                };
 
-            llargument_tys.push(llarg_ty);
+                llargument_tys.push(llarg_ty);
+            };
+            for arg in &self.args {
+                if !arg.nested.is_empty() {
+                    for arg in &arg.nested {
+                        assert!(arg.nested.is_empty());
+                        push(arg);
+                    }
+                    continue;
+                }
+                push(arg);
+            }
         }
 
         if self.variadic {
@@ -948,12 +982,22 @@ impl<'a, 'tcx> FnType<'tcx> {
             self.ret.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn);
         }
         i += 1;
-        for arg in &self.args {
+        let mut apply = |arg: &ArgType| {
             if !arg.is_ignore() {
                 if arg.pad.is_some() { i += 1; }
                 arg.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn);
                 i += 1;
             }
+        };
+        for arg in &self.args {
+            if !arg.nested.is_empty() {
+                for arg in &arg.nested {
+                    assert!(arg.nested.is_empty());
+                    apply(arg);
+                }
+                continue;
+            }
+            apply(arg);
         }
     }
 
@@ -963,12 +1007,22 @@ impl<'a, 'tcx> FnType<'tcx> {
             self.ret.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
         }
         i += 1;
-        for arg in &self.args {
+        let mut apply = |arg: &ArgType| {
             if !arg.is_ignore() {
                 if arg.pad.is_some() { i += 1; }
                 arg.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
                 i += 1;
             }
+        };
+        for arg in &self.args {
+            if !arg.nested.is_empty() {
+                for arg in &arg.nested {
+                    assert!(arg.nested.is_empty());
+                    apply(arg);
+                }
+                continue;
+            }
+            apply(arg);
         }
 
         if self.cconv != llvm::CCallConv {
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index d7397e359a1..426a44671bc 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -27,7 +27,7 @@ use type_::Type;
 use value::Value;
 use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::ty::layout::{self, HasDataLayout, Layout, LayoutOf};
+use rustc::ty::layout::{self, HasDataLayout, LayoutOf};
 use rustc::ty::subst::{Kind, Subst, Substs};
 use rustc::hir;
 
@@ -41,10 +41,15 @@ use syntax_pos::{Span, DUMMY_SP};
 pub use context::{CrateContext, SharedCrateContext};
 
 pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
-    if let Layout::FatPointer { .. } = *ccx.layout_of(ty).layout {
-        true
-    } else {
-        false
+    match ty.sty {
+        ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
+        ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
+            !ccx.shared().type_is_sized(ty)
+        }
+        ty::TyAdt(def, _) if def.is_box() => {
+            !ccx.shared().type_is_sized(ty.boxed_ty())
+        }
+        _ => false
     }
 }
 
@@ -63,9 +68,8 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
 pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
                                   -> bool {
     let layout = ccx.layout_of(ty);
-    match *layout.layout {
-        Layout::FatPointer => true,
-        Layout::Univariant => {
+    match *layout.fields {
+        layout::FieldPlacement::Arbitrary { .. } => {
             // There must be only 2 fields.
             if layout.fields.count() != 2 {
                 return false;
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index da3f6559dac..7dbb8253e42 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -215,13 +215,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
             }
 
             mir::TerminatorKind::Return => {
-                let ret = self.fn_ty.ret;
-                if ret.is_ignore() || ret.is_indirect() {
+                if self.fn_ty.ret.is_ignore() || self.fn_ty.ret.is_indirect() {
                     bcx.ret_void();
                     return;
                 }
 
-                let llval = if let Some(cast_ty) = ret.cast {
+                let llval = if let Some(cast_ty) = self.fn_ty.ret.cast {
                     let op = match self.locals[mir::RETURN_POINTER] {
                         LocalRef::Operand(Some(op)) => op,
                         LocalRef::Operand(None) => bug!("use of return before def"),
@@ -234,7 +233,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                     };
                     let llslot = match op.val {
                         Immediate(_) | Pair(..) => {
-                            let scratch = LvalueRef::alloca(&bcx, ret.layout.ty, "ret");
+                            let scratch = LvalueRef::alloca(&bcx, self.fn_ty.ret.layout.ty, "ret");
                             op.store(&bcx, scratch);
                             scratch.llval
                         }
@@ -246,7 +245,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                     };
                     let load = bcx.load(
                         bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()),
-                        Some(ret.layout.align(bcx.ccx)));
+                        Some(self.fn_ty.ret.layout.align(bcx.ccx)));
                     load
                 } else {
                     let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER));
@@ -562,9 +561,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                     (&args[..], None)
                 };
 
-                let mut idx = 0;
-                for arg in first_args {
+                for (idx, arg) in first_args.iter().enumerate() {
                     let mut op = self.trans_operand(&bcx, arg);
+                    if idx == 0 {
+                        if let Pair(_, meta) = op.val {
+                            if let Some(ty::InstanceDef::Virtual(_, idx)) = def {
+                                let llmeth = meth::VirtualIndex::from_index(idx)
+                                    .get_fn(&bcx, meta);
+                                let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
+                                llfn = Some(bcx.pointercast(llmeth, llty));
+                            }
+                        }
+                    }
 
                     // The callee needs to own the argument memory if we pass it
                     // by-ref, so make a local copy of non-immediate constants.
@@ -574,12 +582,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                         op.val = Ref(tmp.llval, tmp.alignment);
                     }
 
-                    self.trans_argument(&bcx, op, &mut llargs, &fn_ty,
-                                        &mut idx, &mut llfn, &def);
+                    self.trans_argument(&bcx, op, &mut llargs, &fn_ty.args[idx]);
                 }
                 if let Some(tup) = untuple {
-                    self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty,
-                                                  &mut idx, &mut llfn, &def)
+                    self.trans_arguments_untupled(&bcx, tup, &mut llargs,
+                        &fn_ty.args[first_args.len()..])
                 }
 
                 let fn_ptr = match (llfn, instance) {
@@ -602,36 +609,22 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                       bcx: &Builder<'a, 'tcx>,
                       op: OperandRef<'tcx>,
                       llargs: &mut Vec<ValueRef>,
-                      fn_ty: &FnType<'tcx>,
-                      next_idx: &mut usize,
-                      llfn: &mut Option<ValueRef>,
-                      def: &Option<ty::InstanceDef<'tcx>>) {
+                      arg: &ArgType<'tcx>) {
         if let Pair(a, b) = op.val {
             // Treat the values in a fat pointer separately.
-            if common::type_is_fat_ptr(bcx.ccx, op.ty) {
-                let (ptr, meta) = (a, b);
-                if *next_idx == 0 {
-                    if let Some(ty::InstanceDef::Virtual(_, idx)) = *def {
-                        let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta);
-                        let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
-                        *llfn = Some(bcx.pointercast(llmeth, llty));
-                    }
-                }
-
+            if !arg.nested.is_empty() {
+                assert_eq!(arg.nested.len(), 2);
                 let imm_op = |x| OperandRef {
                     val: Immediate(x),
                     // We won't be checking the type again.
                     ty: bcx.tcx().types.err
                 };
-                self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def);
-                self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def);
+                self.trans_argument(bcx, imm_op(a), llargs, &arg.nested[0]);
+                self.trans_argument(bcx, imm_op(b), llargs, &arg.nested[1]);
                 return;
             }
         }
 
-        let arg = &fn_ty.args[*next_idx];
-        *next_idx += 1;
-
         // Fill padding with undef value, where applicable.
         if let Some(ty) = arg.pad {
             llargs.push(C_undef(ty.llvm_type(bcx.ccx)));
@@ -686,10 +679,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                                 bcx: &Builder<'a, 'tcx>,
                                 operand: &mir::Operand<'tcx>,
                                 llargs: &mut Vec<ValueRef>,
-                                fn_ty: &FnType<'tcx>,
-                                next_idx: &mut usize,
-                                llfn: &mut Option<ValueRef>,
-                                def: &Option<ty::InstanceDef<'tcx>>) {
+                                args: &[ArgType<'tcx>]) {
         let tuple = self.trans_operand(bcx, operand);
 
         let arg_types = match tuple.ty.sty {
@@ -702,18 +692,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
         match tuple.val {
             Ref(llval, align) => {
                 let tuple_ptr = LvalueRef::new_sized(llval, tuple.ty, align);
-                for (n, &ty) in arg_types.iter().enumerate() {
+                for n in 0..arg_types.len() {
                     let field_ptr = tuple_ptr.project_field(bcx, n);
-                    let op = if common::type_is_fat_ptr(bcx.ccx, ty) {
-                        field_ptr.load(bcx)
-                    } else {
-                        // trans_argument will load this if it needs to
-                        OperandRef {
-                            val: Ref(field_ptr.llval, field_ptr.alignment),
-                            ty
-                        }
-                    };
-                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
+                    self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[n]);
                 }
 
             }
@@ -728,7 +709,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                         val: Immediate(elem),
                         ty,
                     };
-                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
+                    self.trans_argument(bcx, op, llargs, &args[n]);
                 }
             }
             Pair(a, b) => {
@@ -740,7 +721,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                         val: Immediate(elem),
                         ty,
                     };
-                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
+                    self.trans_argument(bcx, op, llargs, &args[n]);
                 }
             }
         }
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index a1e89013bdb..7440551f322 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -22,7 +22,7 @@ use builder::Builder;
 use common::{self, CrateContext, Funclet};
 use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
 use monomorphize::Instance;
-use abi::{self, ArgAttribute, FnType};
+use abi::{ArgAttribute, FnType};
 use type_of;
 
 use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span};
@@ -401,22 +401,10 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
             };
 
             let lvalue = LvalueRef::alloca(bcx, arg_ty, &name);
-            for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
-                let dst = lvalue.project_field(bcx, i);
+            for i in 0..tupled_arg_tys.len() {
                 let arg = &mircx.fn_ty.args[idx];
                 idx += 1;
-                if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) {
-                    // We pass fat pointers as two words, but inside the tuple
-                    // they are the two sub-fields of a single aggregate field.
-                    let meta = &mircx.fn_ty.args[idx];
-                    idx += 1;
-                    arg.store_fn_arg(bcx, &mut llarg_idx,
-                        dst.project_field(bcx, abi::FAT_PTR_ADDR));
-                    meta.store_fn_arg(bcx, &mut llarg_idx,
-                        dst.project_field(bcx, abi::FAT_PTR_EXTRA));
-                } else {
-                    arg.store_fn_arg(bcx, &mut llarg_idx, dst);
-                }
+                arg.store_fn_arg(bcx, &mut llarg_idx, lvalue.project_field(bcx, i));
             }
 
             // Now that we have one alloca that contains the aggregate value,
@@ -453,26 +441,19 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
             llarg_idx += 1;
             LvalueRef::new_sized(llarg, arg_ty, Alignment::AbiAligned)
         } else if !lvalue_locals.contains(local.index()) &&
-                  arg.cast.is_none() && arg_scope.is_none() {
-            if arg.is_ignore() {
-                return LocalRef::new_operand(bcx.ccx, arg_ty);
-            }
+                  !arg.nested.is_empty() {
+            assert_eq!(arg.nested.len(), 2);
+            let (a, b) = (&arg.nested[0], &arg.nested[1]);
+            assert!(!a.is_ignore() && a.cast.is_none() && a.pad.is_none());
+            assert!(!b.is_ignore() && b.cast.is_none() && b.pad.is_none());
 
-            // We don't have to cast or keep the argument in the alloca.
-            // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
-            // of putting everything in allocas just so we can use llvm.dbg.declare.
-            if arg.pad.is_some() {
-                llarg_idx += 1;
-            }
-            let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
+            let mut a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
             llarg_idx += 1;
-            let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
-                let meta = &mircx.fn_ty.args[idx];
-                idx += 1;
-                assert!(meta.cast.is_none() && meta.pad.is_none());
-                let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
-                llarg_idx += 1;
 
+            let mut b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
+            llarg_idx += 1;
+
+            if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
                 // FIXME(eddyb) As we can't perfectly represent the data and/or
                 // vtable pointer in a fat pointers in Rust's typesystem, and
                 // because we split fat pointers into two ArgType's, they're
@@ -486,36 +467,40 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
                 let data_llty = bcx.ccx.llvm_type_of(pointee);
                 let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee);
 
-                let llarg = bcx.pointercast(llarg, data_llty.ptr_to());
-                bcx.set_value_name(llarg, &(name.clone() + ".ptr"));
-                let llmeta = bcx.pointercast(llmeta, meta_llty);
-                bcx.set_value_name(llmeta, &(name + ".meta"));
+                a = bcx.pointercast(a, data_llty.ptr_to());
+                bcx.set_value_name(a, &(name.clone() + ".ptr"));
+                b = bcx.pointercast(b, meta_llty);
+                bcx.set_value_name(b, &(name + ".meta"));
+            }
 
-                OperandValue::Pair(llarg, llmeta)
-            } else {
-                bcx.set_value_name(llarg, &name);
-                OperandValue::Immediate(llarg)
-            };
+            return LocalRef::Operand(Some(OperandRef {
+                val: OperandValue::Pair(a, b),
+                ty: arg_ty
+            }));
+        } else if !lvalue_locals.contains(local.index()) &&
+                  !arg.is_indirect() && arg.cast.is_none() &&
+                  arg_scope.is_none() {
+            if arg.is_ignore() {
+                return LocalRef::new_operand(bcx.ccx, arg_ty);
+            }
+
+            // We don't have to cast or keep the argument in the alloca.
+            // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
+            // of putting everything in allocas just so we can use llvm.dbg.declare.
+            if arg.pad.is_some() {
+                llarg_idx += 1;
+            }
+            let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
+            bcx.set_value_name(llarg, &name);
+            llarg_idx += 1;
             let operand = OperandRef {
-                val,
+                val: OperandValue::Immediate(llarg),
                 ty: arg_ty
             };
             return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
         } else {
             let tmp = LvalueRef::alloca(bcx, arg_ty, &name);
-            if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
-                // we pass fat pointers as two words, but we want to
-                // represent them internally as a pointer to two words,
-                // so make an alloca to store them in.
-                let meta = &mircx.fn_ty.args[idx];
-                idx += 1;
-                arg.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_ADDR));
-                meta.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_EXTRA));
-            } else  {
-                // otherwise, arg is passed by value, so make a
-                // temporary and store it there
-                arg.store_fn_arg(bcx, &mut llarg_idx, tmp);
-            }
+            arg.store_fn_arg(bcx, &mut llarg_idx, tmp);
             tmp
         };
         arg_scope.map(|scope| {