about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-02-26 01:10:40 +0200
committerEduard Burtescu <edy.burt@gmail.com>2016-03-17 21:51:51 +0200
commit77f34841489767804ffe3d0f20d5469ecc5cf417 (patch)
tree6afe306918b78493301741c465ba994cfcece4fe
parentde5f8244f20e434d2e0d351c7f4b55e604b4f3b3 (diff)
downloadrust-77f34841489767804ffe3d0f20d5469ecc5cf417.tar.gz
rust-77f34841489767804ffe3d0f20d5469ecc5cf417.zip
trans: Apply all attributes through FnType.
-rw-r--r--src/librustc_llvm/lib.rs55
-rw-r--r--src/librustc_trans/trans/abi.rs116
-rw-r--r--src/librustc_trans/trans/attributes.rs173
-rw-r--r--src/librustc_trans/trans/base.rs13
-rw-r--r--src/librustc_trans/trans/build.rs11
-rw-r--r--src/librustc_trans/trans/builder.rs46
-rw-r--r--src/librustc_trans/trans/callee.rs34
-rw-r--r--src/librustc_trans/trans/declare.rs9
-rw-r--r--src/librustc_trans/trans/expr.rs9
-rw-r--r--src/librustc_trans/trans/foreign.rs64
-rw-r--r--src/librustc_trans/trans/glue.rs7
-rw-r--r--src/librustc_trans/trans/intrinsic.rs39
-rw-r--r--src/librustc_trans/trans/mir/block.rs48
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs4
14 files changed, 240 insertions, 388 deletions
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index b8377916efe..a1dca796d9a 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -212,21 +212,21 @@ impl Attributes {
         self
     }
 
-    pub fn apply_llfn(&self, idx: c_uint, llfn: ValueRef) {
+    pub fn apply_llfn(&self, idx: usize, llfn: ValueRef) {
         unsafe {
-            LLVMAddFunctionAttribute(llfn, idx, self.regular.bits());
+            LLVMAddFunctionAttribute(llfn, idx as c_uint, self.regular.bits());
             if self.dereferenceable_bytes != 0 {
-                LLVMAddDereferenceableAttr(llfn, idx,
+                LLVMAddDereferenceableAttr(llfn, idx as c_uint,
                                            self.dereferenceable_bytes);
             }
         }
     }
 
-    pub fn apply_callsite(&self, idx: c_uint, callsite: ValueRef) {
+    pub fn apply_callsite(&self, idx: usize, callsite: ValueRef) {
         unsafe {
-            LLVMAddCallSiteAttribute(callsite, idx, self.regular.bits());
+            LLVMAddCallSiteAttribute(callsite, idx as c_uint, self.regular.bits());
             if self.dereferenceable_bytes != 0 {
-                LLVMAddDereferenceableCallSiteAttr(callsite, idx,
+                LLVMAddDereferenceableCallSiteAttr(callsite, idx as c_uint,
                                                    self.dereferenceable_bytes);
             }
         }
@@ -240,49 +240,6 @@ pub enum AttributeSet {
     FunctionIndex = !0
 }
 
-pub struct AttrBuilder {
-    attrs: Vec<(usize, Attributes)>
-}
-
-impl AttrBuilder {
-    pub fn new() -> AttrBuilder {
-        AttrBuilder {
-            attrs: Vec::new()
-        }
-    }
-
-    pub fn arg(&mut self, idx: usize) -> &mut Attributes {
-        let mut found = None;
-        for (i, &(idx2, _)) in self.attrs.iter().enumerate() {
-            if idx == idx2 {
-                found = Some(i);
-                break;
-            }
-        }
-        let i = found.unwrap_or_else(|| {
-            self.attrs.push((idx, Attributes::default()));
-            self.attrs.len() - 1
-        });
-        &mut self.attrs[i].1
-    }
-
-    pub fn ret(&mut self) -> &mut Attributes {
-        self.arg(ReturnIndex as usize)
-    }
-
-    pub fn apply_llfn(&self, llfn: ValueRef) {
-        for &(idx, ref attr) in &self.attrs {
-            attr.apply_llfn(idx as c_uint, llfn);
-        }
-    }
-
-    pub fn apply_callsite(&self, callsite: ValueRef) {
-        for &(idx, ref attr) in &self.attrs {
-            attr.apply_callsite(idx as c_uint, callsite);
-        }
-    }
-}
-
 // enum for the LLVM IntPredicate type
 #[derive(Copy, Clone)]
 pub enum IntPredicate {
diff --git a/src/librustc_trans/trans/abi.rs b/src/librustc_trans/trans/abi.rs
index ccd276457ce..440d5a8d816 100644
--- a/src/librustc_trans/trans/abi.rs
+++ b/src/librustc_trans/trans/abi.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm;
+use llvm::{self, ValueRef};
 use trans::common::{return_type_is_void, type_is_fat_ptr};
 use trans::context::CrateContext;
 use trans::cabi_x86;
@@ -24,6 +24,7 @@ use trans::machine::{llsize_of_alloc, llsize_of_real};
 use trans::type_::Type;
 use trans::type_of;
 
+use rustc_front::hir;
 use middle::ty::{self, Ty};
 
 pub use syntax::abi::Abi;
@@ -204,22 +205,102 @@ impl FnType {
             }
         };
 
-        let ret = match sig.output {
+        let mut ret = match sig.output {
             ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
                 arg_of(ret_ty)
             }
             _ => ArgType::new(Type::void(ccx), Type::void(ccx))
         };
 
+        if let ty::FnConverging(ret_ty) = sig.output {
+            if !type_is_fat_ptr(ccx.tcx(), ret_ty) {
+                // The `noalias` attribute on the return value is useful to a
+                // function ptr caller.
+                if let ty::TyBox(_) = ret_ty.sty {
+                    // `Box` pointer return values never alias because ownership
+                    // is transferred
+                    ret.attrs.set(llvm::Attribute::NoAlias);
+                }
+
+                // We can also mark the return value as `dereferenceable` in certain cases
+                match ret_ty.sty {
+                    // These are not really pointers but pairs, (pointer, len)
+                    ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
+                    ty::TyBox(ty) => {
+                        let llty = type_of::sizing_type_of(ccx, ty);
+                        let llsz = llsize_of_real(ccx, llty);
+                        ret.attrs.set_dereferenceable(llsz);
+                    }
+                    _ => {}
+                }
+            }
+        }
+
         let mut args = Vec::with_capacity(inputs.len() + extra_args.len());
+
+        // Handle safe Rust thin and fat pointers.
+        let rust_ptr_attrs = |ty: Ty<'tcx>, arg: &mut ArgType| match ty.sty {
+            // `Box` pointer parameters never alias because ownership is transferred
+            ty::TyBox(inner) => {
+                arg.attrs.set(llvm::Attribute::NoAlias);
+                Some(inner)
+            }
+
+            ty::TyRef(b, mt) => {
+                use middle::ty::{BrAnon, ReLateBound};
+
+                // `&mut` pointer parameters never alias other parameters, or mutable global data
+                //
+                // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
+                // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
+                // on memory dependencies rather than pointer equality
+                let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
+
+                if mt.mutbl != hir::MutMutable && !interior_unsafe {
+                    arg.attrs.set(llvm::Attribute::NoAlias);
+                }
+
+                if mt.mutbl == hir::MutImmutable && !interior_unsafe {
+                    arg.attrs.set(llvm::Attribute::ReadOnly);
+                }
+
+                // When a reference in an argument has no named lifetime, it's
+                // impossible for that reference to escape this function
+                // (returned or stored beyond the call by a closure).
+                if let ReLateBound(_, BrAnon(_)) = *b {
+                    arg.attrs.set(llvm::Attribute::NoCapture);
+                }
+
+                Some(mt.ty)
+            }
+            _ => None
+        };
+
         for ty in inputs.iter().chain(extra_args.iter()) {
-            let arg = arg_of(ty);
+            let mut arg = arg_of(ty);
+
             if type_is_fat_ptr(ccx.tcx(), ty) {
-                let original = arg.original_ty.field_types();
-                let sizing = arg.ty.field_types();
-                args.extend(original.into_iter().zip(sizing)
-                                    .map(|(o, s)| ArgType::new(o, s)));
+                let original_tys = arg.original_ty.field_types();
+                let sizing_tys = arg.ty.field_types();
+                assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2));
+
+                let mut data = ArgType::new(original_tys[0], sizing_tys[0]);
+                let mut info = ArgType::new(original_tys[1], sizing_tys[1]);
+
+                if let Some(inner) = rust_ptr_attrs(ty, &mut data) {
+                    data.attrs.set(llvm::Attribute::NonNull);
+                    if ccx.tcx().struct_tail(inner).is_trait() {
+                        info.attrs.set(llvm::Attribute::NonNull);
+                    }
+                }
+                args.push(data);
+                args.push(info);
             } else {
+                if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
+                    let llty = type_of::sizing_type_of(ccx, inner);
+                    let llsz = llsize_of_real(ccx, llty);
+                    arg.attrs.set_dereferenceable(llsz);
+                }
                 args.push(arg);
             }
         }
@@ -327,18 +408,29 @@ impl FnType {
         }
     }
 
-    pub fn llvm_attrs(&self) -> llvm::AttrBuilder {
-        let mut attrs = llvm::AttrBuilder::new();
+    pub fn apply_attrs_llfn(&self, llfn: ValueRef) {
+        let mut i = if self.ret.is_indirect() { 1 } else { 0 };
+        self.ret.attrs.apply_llfn(i, llfn);
+        i += 1;
+        for arg in &self.args {
+            if !arg.is_ignore() {
+                if arg.pad.is_some() { i += 1; }
+                arg.attrs.apply_llfn(i, llfn);
+                i += 1;
+            }
+        }
+    }
+
+    pub fn apply_attrs_callsite(&self, callsite: ValueRef) {
         let mut i = if self.ret.is_indirect() { 1 } else { 0 };
-        *attrs.arg(i) = self.ret.attrs;
+        self.ret.attrs.apply_callsite(i, callsite);
         i += 1;
         for arg in &self.args {
             if !arg.is_ignore() {
                 if arg.pad.is_some() { i += 1; }
-                *attrs.arg(i) = arg.attrs;
+                arg.attrs.apply_callsite(i, callsite);
                 i += 1;
             }
         }
-        attrs
     }
 }
diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs
index 5eb9560a43a..99dc3ade823 100644
--- a/src/librustc_trans/trans/attributes.rs
+++ b/src/librustc_trans/trans/attributes.rs
@@ -11,17 +11,10 @@
 
 use libc::{c_uint, c_ulonglong};
 use llvm::{self, ValueRef};
-use middle::ty;
-use middle::infer;
 use session::config::NoDebugInfo;
 pub use syntax::attr::InlineAttr;
 use syntax::ast;
-use rustc_front::hir;
-use trans::abi::Abi;
-use trans::common;
 use trans::context::CrateContext;
-use trans::machine;
-use trans::type_of;
 
 /// Mark LLVM function to use provided inline heuristic.
 #[inline]
@@ -111,174 +104,12 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
     for attr in attrs {
         if attr.check_name("cold") {
             llvm::Attributes::default().set(llvm::Attribute::Cold)
-                .apply_llfn(llvm::FunctionIndex as c_uint, llfn)
+                .apply_llfn(llvm::FunctionIndex as usize, llfn)
         } else if attr.check_name("allocator") {
             llvm::Attributes::default().set(llvm::Attribute::NoAlias)
-                .apply_llfn(llvm::ReturnIndex as c_uint, llfn)
+                .apply_llfn(llvm::ReturnIndex as usize, llfn)
         } else if attr.check_name("unwind") {
             unwind(llfn, true);
         }
     }
 }
-
-/// Composite function which converts function type into LLVM attributes for the function.
-pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx>)
-                              -> llvm::AttrBuilder {
-    use middle::ty::{BrAnon, ReLateBound};
-
-    let f = match fn_type.sty {
-        ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => f,
-        _ => unreachable!("expected fn type, found {:?}", fn_type)
-    };
-
-    let fn_sig = ccx.tcx().erase_late_bound_regions(&f.sig);
-    let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
-
-    let mut attrs = llvm::AttrBuilder::new();
-    let ret_ty = fn_sig.output;
-
-    // These have an odd calling convention, so we need to manually
-    // unpack the input ty's
-    let input_tys = match fn_type.sty {
-        ty::TyFnDef(..) | ty::TyFnPtr(_) if f.abi == Abi::RustCall => {
-            let first = Some(fn_sig.inputs[0]).into_iter();
-
-            match fn_sig.inputs[1].sty {
-                ty::TyTuple(ref t_in) => {
-                    first.chain(t_in.iter().cloned())
-                }
-                _ => ccx.sess().bug("expected tuple'd inputs")
-            }
-        }
-        _ => None.into_iter().chain(fn_sig.inputs.iter().cloned())
-    };
-
-    // Index 0 is the return value of the llvm func, so we start at 1
-    let mut idx = 1;
-    if let ty::FnConverging(ret_ty) = ret_ty {
-        // A function pointer is called without the declaration
-        // available, so we have to apply any attributes with ABI
-        // implications directly to the call instruction. Right now,
-        // the only attribute we need to worry about is `sret`.
-        if type_of::return_uses_outptr(ccx, ret_ty) {
-            let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
-
-            // The outptr can be noalias and nocapture because it's entirely
-            // invisible to the program. We also know it's nonnull as well
-            // as how many bytes we can dereference
-            attrs.arg(1).set(llvm::Attribute::StructRet)
-                        .set(llvm::Attribute::NoAlias)
-                        .set(llvm::Attribute::NoCapture)
-                        .set_dereferenceable(llret_sz);
-
-            // Add one more since there's an outptr
-            idx += 1;
-        } else {
-            // The `noalias` attribute on the return value is useful to a
-            // function ptr caller.
-            match ret_ty.sty {
-                // `Box` pointer return values never alias because ownership
-                // is transferred
-                ty::TyBox(it) if common::type_is_sized(ccx.tcx(), it) => {
-                    attrs.ret().set(llvm::Attribute::NoAlias);
-                }
-                _ => {}
-            }
-
-            // We can also mark the return value as `dereferenceable` in certain cases
-            match ret_ty.sty {
-                // These are not really pointers but pairs, (pointer, len)
-                ty::TyRef(_, ty::TypeAndMut { ty: inner, .. })
-                | ty::TyBox(inner) if common::type_is_sized(ccx.tcx(), inner) => {
-                    let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
-                    attrs.ret().set_dereferenceable(llret_sz);
-                }
-                _ => {}
-            }
-
-            if let ty::TyBool = ret_ty.sty {
-                attrs.ret().set(llvm::Attribute::ZExt);
-            }
-        }
-    }
-
-    for t in input_tys {
-        match t.sty {
-            _ if type_of::arg_is_indirect(ccx, t) => {
-                let llarg_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, t));
-
-                // For non-immediate arguments the callee gets its own copy of
-                // the value on the stack, so there are no aliases. It's also
-                // program-invisible so can't possibly capture
-                attrs.arg(idx).set(llvm::Attribute::NoAlias)
-                              .set(llvm::Attribute::NoCapture)
-                              .set_dereferenceable(llarg_sz);
-            }
-
-            ty::TyBool => {
-                attrs.arg(idx).set(llvm::Attribute::ZExt);
-            }
-
-            // `Box` pointer parameters never alias because ownership is transferred
-            ty::TyBox(inner) => {
-                attrs.arg(idx).set(llvm::Attribute::NoAlias);
-
-                if common::type_is_sized(ccx.tcx(), inner) {
-                    let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
-                    attrs.arg(idx).set_dereferenceable(llsz);
-                } else {
-                    attrs.arg(idx).set(llvm::Attribute::NonNull);
-                    if inner.is_trait() {
-                        attrs.arg(idx + 1).set(llvm::Attribute::NonNull);
-                    }
-                }
-            }
-
-            ty::TyRef(b, mt) => {
-                // `&mut` pointer parameters never alias other parameters, or mutable global data
-                //
-                // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
-                // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
-                // on memory dependencies rather than pointer equality
-                let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
-
-                if mt.mutbl != hir::MutMutable && !interior_unsafe {
-                    attrs.arg(idx).set(llvm::Attribute::NoAlias);
-                }
-
-                if mt.mutbl == hir::MutImmutable && !interior_unsafe {
-                    attrs.arg(idx).set(llvm::Attribute::ReadOnly);
-                }
-
-                // & pointer parameters are also never null and for sized types we also know
-                // exactly how many bytes we can dereference
-                if common::type_is_sized(ccx.tcx(), mt.ty) {
-                    let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
-                    attrs.arg(idx).set_dereferenceable(llsz);
-                } else {
-                    attrs.arg(idx).set(llvm::Attribute::NonNull);
-                    if mt.ty.is_trait() {
-                        attrs.arg(idx + 1).set(llvm::Attribute::NonNull);
-                    }
-                }
-
-                // When a reference in an argument has no named lifetime, it's
-                // impossible for that reference to escape this function
-                // (returned or stored beyond the call by a closure).
-                if let ReLateBound(_, BrAnon(_)) = *b {
-                    attrs.arg(idx).set(llvm::Attribute::NoCapture);
-                }
-            }
-
-            _ => ()
-        }
-
-        if common::type_is_fat_ptr(ccx.tcx(), t) {
-            idx += 2;
-        } else {
-            idx += 1;
-        }
-    }
-
-    attrs
-}
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 905ccda198c..4b268ba57ca 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -836,7 +836,6 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
 pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                           llfn: ValueRef,
                           llargs: &[ValueRef],
-                          fn_ty: Ty<'tcx>,
                           debug_loc: DebugLoc)
                           -> (ValueRef, Block<'blk, 'tcx>) {
     let _icx = push_ctxt("invoke_");
@@ -844,8 +843,6 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         return (C_null(Type::i8(bcx.ccx())), bcx);
     }
 
-    let attributes = attributes::from_fn_type(bcx.ccx(), fn_ty);
-
     match bcx.opt_node_id {
         None => {
             debug!("invoke at ???");
@@ -868,7 +865,6 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               &llargs[..],
                               normal_bcx.llbb,
                               landing_pad,
-                              Some(attributes),
                               debug_loc);
         return (llresult, normal_bcx);
     } else {
@@ -877,7 +873,7 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             debug!("arg: {:?}", Value(llarg));
         }
 
-        let llresult = Call(bcx, llfn, &llargs[..], Some(attributes), debug_loc);
+        let llresult = Call(bcx, llfn, &llargs[..], debug_loc);
         return (llresult, bcx);
     }
 }
@@ -1105,7 +1101,6 @@ pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
         Call(cx,
              lifetime_start,
              &[C_u64(ccx, size), ptr],
-             None,
              DebugLoc::None);
     })
 }
@@ -1116,7 +1111,6 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
         Call(cx,
              lifetime_end,
              &[C_u64(ccx, size), ptr],
-             None,
              DebugLoc::None);
     })
 }
@@ -1147,7 +1141,6 @@ pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, a
     Call(cx,
          memcpy,
          &[dst_ptr, src_ptr, size, align, volatile],
-         None,
          DebugLoc::None);
 }
 
@@ -1217,7 +1210,7 @@ pub fn call_memset<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>,
     let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width);
     let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key);
     let volatile = C_bool(ccx, volatile);
-    b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None, None);
+    b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None);
 }
 
 
@@ -1292,7 +1285,7 @@ pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &st
     // Block, which we do not have for `alloca_insert_pt`).
     core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
         let ptr = b.pointercast(p, Type::i8p(ccx));
-        b.call(lifetime_start, &[C_u64(ccx, size), ptr], None, None);
+        b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
     });
     memfill(&b, p, ty, adt::DTOR_DONE);
     p
diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs
index 2289cdcc8a3..ac7e3e48fb5 100644
--- a/src/librustc_trans/trans/build.rs
+++ b/src/librustc_trans/trans/build.rs
@@ -12,7 +12,7 @@
 #![allow(non_snake_case)]
 
 use llvm;
-use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder};
+use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
 use llvm::{Opcode, IntPredicate, RealPredicate};
 use llvm::{ValueRef, BasicBlockRef};
 use trans::common::*;
@@ -139,7 +139,6 @@ pub fn Invoke(cx: Block,
               args: &[ValueRef],
               then: BasicBlockRef,
               catch: BasicBlockRef,
-              attributes: Option<AttrBuilder>,
               debug_loc: DebugLoc)
               -> ValueRef {
     if cx.unreachable.get() {
@@ -154,7 +153,7 @@ pub fn Invoke(cx: Block,
            }).collect::<Vec<String>>().join(", "));
     debug_loc.apply(cx.fcx);
     let bundle = cx.lpad().and_then(|b| b.bundle());
-    B(cx).invoke(fn_, args, then, catch, bundle, attributes)
+    B(cx).invoke(fn_, args, then, catch, bundle)
 }
 
 pub fn Unreachable(cx: Block) {
@@ -911,7 +910,6 @@ pub fn InlineAsmCall(cx: Block, asm: *const c_char, cons: *const c_char,
 pub fn Call(cx: Block,
             fn_: ValueRef,
             args: &[ValueRef],
-            attributes: Option<AttrBuilder>,
             debug_loc: DebugLoc)
             -> ValueRef {
     if cx.unreachable.get() {
@@ -919,14 +917,13 @@ pub fn Call(cx: Block,
     }
     debug_loc.apply(cx.fcx);
     let bundle = cx.lpad.get().and_then(|b| b.bundle());
-    B(cx).call(fn_, args, bundle, attributes)
+    B(cx).call(fn_, args, bundle)
 }
 
 pub fn CallWithConv(cx: Block,
                     fn_: ValueRef,
                     args: &[ValueRef],
                     conv: CallConv,
-                    attributes: Option<AttrBuilder>,
                     debug_loc: DebugLoc)
                     -> ValueRef {
     if cx.unreachable.get() {
@@ -934,7 +931,7 @@ pub fn CallWithConv(cx: Block,
     }
     debug_loc.apply(cx.fcx);
     let bundle = cx.lpad.get().and_then(|b| b.bundle());
-    B(cx).call_with_conv(fn_, args, conv, bundle, attributes)
+    B(cx).call_with_conv(fn_, args, conv, bundle)
 }
 
 pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) {
diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs
index fad797f14ed..da5042b0caf 100644
--- a/src/librustc_trans/trans/builder.rs
+++ b/src/librustc_trans/trans/builder.rs
@@ -11,7 +11,7 @@
 #![allow(dead_code)] // FFI wrappers
 
 use llvm;
-use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder};
+use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
 use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef};
 use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef};
 use trans::base;
@@ -165,8 +165,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                   args: &[ValueRef],
                   then: BasicBlockRef,
                   catch: BasicBlockRef,
-                  bundle: Option<&OperandBundleDef>,
-                  attributes: Option<AttrBuilder>)
+                  bundle: Option<&OperandBundleDef>)
                   -> ValueRef {
         self.count_insn("invoke");
 
@@ -180,18 +179,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);
 
         unsafe {
-            let v = llvm::LLVMRustBuildInvoke(self.llbuilder,
-                                              llfn,
-                                              args.as_ptr(),
-                                              args.len() as c_uint,
-                                              then,
-                                              catch,
-                                              bundle,
-                                              noname());
-            if let Some(a) = attributes {
-                a.apply_callsite(v);
-            }
-            v
+            llvm::LLVMRustBuildInvoke(self.llbuilder,
+                                      llfn,
+                                      args.as_ptr(),
+                                      args.len() as c_uint,
+                                      then,
+                                      catch,
+                                      bundle,
+                                      noname())
         }
     }
 
@@ -775,7 +770,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                          comment_text.as_ptr(), noname(), False,
                                          False)
             };
-            self.call(asm, &[], None, None);
+            self.call(asm, &[], None);
         }
     }
 
@@ -800,13 +795,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         unsafe {
             let v = llvm::LLVMInlineAsm(
                 fty.to_ref(), asm, cons, volatile, alignstack, dia as c_uint);
-            self.call(v, inputs, None, None)
+            self.call(v, inputs, None)
         }
     }
 
     pub fn call(&self, llfn: ValueRef, args: &[ValueRef],
-                bundle: Option<&OperandBundleDef>,
-                attributes: Option<AttrBuilder>) -> ValueRef {
+                bundle: Option<&OperandBundleDef>) -> ValueRef {
         self.count_insn("call");
 
         debug!("Call {:?} with args ({})",
@@ -844,22 +838,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);
 
         unsafe {
-            let v = llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(),
-                                            args.len() as c_uint, bundle,
-                                            noname());
-            if let Some(a) = attributes {
-                a.apply_callsite(v);
-            }
-            v
+            llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(),
+                                    args.len() as c_uint, bundle, noname())
         }
     }
 
     pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef],
                           conv: CallConv,
-                          bundle: Option<&OperandBundleDef>,
-                          attributes: Option<AttrBuilder>) -> ValueRef {
+                          bundle: Option<&OperandBundleDef>) -> ValueRef {
         self.count_insn("callwithconv");
-        let v = self.call(llfn, args, bundle, attributes);
+        let v = self.call(llfn, args, bundle);
         llvm::SetInstructionCallConv(v, conv);
         v
     }
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 4c5386799e9..9236d29ca72 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -27,7 +27,7 @@ use middle::subst;
 use middle::subst::{Substs};
 use middle::traits;
 use rustc::front::map as hir_map;
-use trans::abi::Abi;
+use trans::abi::{Abi, FnType};
 use trans::adt;
 use trans::attributes;
 use trans::base;
@@ -700,25 +700,31 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         };
 
         // Invoke the actual rust fn and update bcx/llresult.
-        let (llret, b) = base::invoke(bcx,
-                                      datum.val,
-                                      &llargs[..],
-                                      datum.ty,
-                                      debug_loc);
+        let (llret, b) = base::invoke(bcx, datum.val, &llargs, debug_loc);
+
+        let fn_ty = match datum.ty.sty {
+            ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => {
+                let sig = bcx.tcx().erase_late_bound_regions(&f.sig);
+                let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
+                FnType::new(bcx.ccx(), f.abi, &sig, &[])
+            }
+            _ => unreachable!("expected fn type")
+        };
+
+        if !bcx.unreachable.get() {
+            fn_ty.apply_attrs_callsite(llret);
+        }
+
         bcx = b;
         llresult = llret;
 
         // If the Rust convention for this type is return via
         // the return value, copy it into llretslot.
-        match (opt_llretslot, ret_ty) {
-            (Some(llretslot), ty::FnConverging(ret_ty)) => {
-                if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) &&
-                    !common::type_is_zero_size(bcx.ccx(), ret_ty)
-                {
-                    store_ty(bcx, llret, llretslot, ret_ty)
-                }
+        if let Some(llretslot) = opt_llretslot {
+            let llty = fn_ty.ret.original_ty;
+            if !fn_ty.ret.is_indirect() && llty != Type::void(bcx.ccx()) {
+                store_ty(bcx, llret, llretslot, ret_ty.unwrap())
             }
-            (_, _) => {}
         }
     } else {
         // Lang items are the only case where dest is None, and
diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs
index 6d21a90bb0d..307c511b2a8 100644
--- a/src/librustc_trans/trans/declare.rs
+++ b/src/librustc_trans/trans/declare.rs
@@ -109,14 +109,11 @@ pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
         llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturn);
     }
 
-    let attrs = if f.abi == Abi::Rust || f.abi == Abi::RustCall {
-        attributes::from_fn_type(ccx, fn_type)
-    } else {
+    if f.abi != Abi::Rust && f.abi != Abi::RustCall {
         attributes::unwind(llfn, false);
-        fty.llvm_attrs()
-    };
+    }
 
-    attrs.apply_llfn(llfn);
+    fty.apply_attrs_llfn(llfn);
 
     llfn
 }
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 6ea407e7b44..d03f4a3013c 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -864,7 +864,6 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let expected = Call(bcx,
                                 expect,
                                 &[bounds_check, C_bool(ccx, false)],
-                                None,
                                 index_expr_debug_loc);
             bcx = with_cond(bcx, expected, |bcx| {
                 controlflow::trans_fail_bounds_check(bcx,
@@ -1681,10 +1680,10 @@ fn trans_scalar_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 if lhs_t == tcx.types.f32 {
                     let lhs = FPExt(bcx, lhs, f64t);
                     let rhs = FPExt(bcx, rhs, f64t);
-                    let res = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
+                    let res = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
                     FPTrunc(bcx, res, Type::f32(bcx.ccx()))
                 } else {
-                    Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc)
+                    Call(bcx, llfn, &[lhs, rhs], binop_debug_loc)
                 }
             } else {
                 FRem(bcx, lhs, rhs, binop_debug_loc)
@@ -2255,7 +2254,7 @@ impl OverflowOpViaIntrinsic {
                                         -> (Block<'blk, 'tcx>, ValueRef) {
         let llfn = self.to_intrinsic(bcx, lhs_t);
 
-        let val = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
+        let val = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
         let result = ExtractValue(bcx, val, 0); // iN operation result
         let overflow = ExtractValue(bcx, val, 1); // i1 "did it overflow?"
 
@@ -2264,7 +2263,7 @@ impl OverflowOpViaIntrinsic {
 
         let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
         Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
-             None, binop_debug_loc);
+             binop_debug_loc);
 
         let bcx =
             base::with_cond(bcx, cond, |bcx|
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index ac316d18940..50b11515eff 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -253,8 +253,10 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         llfn,
                                         &llargs_foreign[..],
                                         fn_type.cconv,
-                                        Some(fn_type.llvm_attrs()),
                                         call_debug_loc);
+    if !bcx.unreachable.get() {
+        fn_type.apply_attrs_callsite(llforeign_retval);
+    }
 
     // If the function we just called does not use an outpointer,
     // store the result into the rust outpointer. Cast the outpointer
@@ -347,20 +349,31 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     let fnty = ccx.tcx().node_id_to_type(id);
     let mty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fnty);
-    let (fn_abi, fn_sig) = match mty.sty {
-        ty::TyFnDef(_, _, ref fn_ty) => (fn_ty.abi, &fn_ty.sig),
+    let f = match mty.sty {
+        ty::TyFnDef(_, _, f) => f,
         _ => ccx.sess().bug("trans_rust_fn_with_foreign_abi called on non-function type")
     };
-    let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig);
+    assert!(f.abi != Abi::Rust);
+    assert!(f.abi != Abi::RustIntrinsic);
+    assert!(f.abi != Abi::PlatformIntrinsic);
+
+    let fn_sig = ccx.tcx().erase_late_bound_regions(&f.sig);
     let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig);
-    let fn_ty = FnType::new(ccx, fn_abi, &fn_sig, &[]);
+    let rust_fn_ty = ccx.tcx().mk_fn_ptr(ty::BareFnTy {
+        unsafety: f.unsafety,
+        abi: Abi::Rust,
+        sig: ty::Binder(fn_sig.clone())
+    });
+    let fty = FnType::new(ccx, f.abi, &fn_sig, &[]);
+    let rust_fty = FnType::new(ccx, Abi::Rust, &fn_sig, &[]);
 
     unsafe { // unsafe because we call LLVM operations
         // Build up the Rust function (`foo0` above).
-        let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
+        let llrustfn = build_rust_fn(ccx, decl, body, param_substs,
+                                     attrs, id, rust_fn_ty, hash);
 
         // Build up the foreign wrapper (`foo` above).
-        return build_wrap_fn(ccx, llrustfn, llwrapfn, &fn_sig, &fn_ty, mty);
+        return build_wrap_fn(ccx, llrustfn, llwrapfn, &fn_sig, &fty, &rust_fty);
     }
 
     fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -369,13 +382,12 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                param_substs: &'tcx Substs<'tcx>,
                                attrs: &[ast::Attribute],
                                id: ast::NodeId,
+                               rust_fn_ty: Ty<'tcx>,
                                hash: Option<&str>)
                                -> ValueRef
     {
         let _icx = push_ctxt("foreign::foreign::build_rust_fn");
         let tcx = ccx.tcx();
-        let t = tcx.node_id_to_type(id);
-        let t = monomorphize::apply_param_substs(tcx, param_substs, &t);
 
         let path =
             tcx.map.def_path(tcx.map.local_def_id(id))
@@ -384,25 +396,6 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                    .chain(once(special_idents::clownshoe_abi.name.as_str()));
         let ps = link::mangle(path, hash);
 
-        // Compute the type that the function would have if it were just a
-        // normal Rust function. This will be the type of the wrappee fn.
-        let rust_fn_ty = match t.sty {
-            ty::TyFnDef(_, _, ref f) => {
-                assert!(f.abi != Abi::Rust);
-                assert!(f.abi != Abi::RustIntrinsic);
-                assert!(f.abi != Abi::PlatformIntrinsic);
-                tcx.mk_fn_ptr(ty::BareFnTy {
-                    unsafety: f.unsafety,
-                    abi: Abi::Rust,
-                    sig: f.sig.clone()
-                })
-            }
-            _ => {
-                unreachable!("build_rust_fn: extern fn {} has ty {:?}, \
-                              expected a fn item type",
-                              tcx.map.path_to_string(id), t);
-            }
-        };
 
         debug!("build_rust_fn: path={} id={} ty={:?}",
                ccx.tcx().map.path_to_string(id),
@@ -419,14 +412,13 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                       llwrapfn: ValueRef,
                                       fn_sig: &ty::FnSig<'tcx>,
                                       fn_ty: &FnType,
-                                      t: Ty<'tcx>) {
+                                      rust_fty: &FnType) {
         let _icx = push_ctxt(
             "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
 
-        debug!("build_wrap_fn(llrustfn={:?}, llwrapfn={:?}, t={:?})",
+        debug!("build_wrap_fn(llrustfn={:?}, llwrapfn={:?})",
                Value(llrustfn),
-               Value(llwrapfn),
-               t);
+               Value(llwrapfn));
 
         // Avoid all the Rust generation stuff and just generate raw
         // LLVM here.
@@ -615,11 +607,9 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
 
         // Perform the call itself
-        debug!("calling llrustfn = {:?}, t = {:?}",
-               Value(llrustfn), t);
-        let attributes = attributes::from_fn_type(ccx, t);
-        let llrust_ret_val = builder.call(llrustfn, &llrust_args,
-                                          None, Some(attributes));
+        debug!("calling llrustfn = {:?}", Value(llrustfn));
+        let llrust_ret_val = builder.call(llrustfn, &llrust_args, None);
+        rust_fty.apply_attrs_callsite(llrust_ret_val);
 
         // Get the return value where the foreign fn expects it.
         let llforeign_ret_ty = fn_ty.ret.cast.unwrap_or(fn_ty.ret.original_ty);
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index 2408d676947..6e31e7c571a 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -170,13 +170,13 @@ pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 let may_need_drop =
                     ICmp(bcx, llvm::IntNE, hint_val, moved_val, DebugLoc::None);
                 bcx = with_cond(bcx, may_need_drop, |cx| {
-                    Call(cx, glue, &[ptr], None, debug_loc);
+                    Call(cx, glue, &[ptr], debug_loc);
                     cx
                 })
             }
             None => {
                 // No drop-hint ==> call standard drop glue
-                Call(bcx, glue, &[ptr], None, debug_loc);
+                Call(bcx, glue, &[ptr], debug_loc);
             }
         }
     }
@@ -313,7 +313,7 @@ fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             And(bcx, not_init, not_done, DebugLoc::None);
         with_cond(bcx, drop_flag_neither_initialized_nor_cleared, |cx| {
             let llfn = cx.ccx().get_intrinsic(&("llvm.debugtrap"));
-            Call(cx, llfn, &[], None, DebugLoc::None);
+            Call(cx, llfn, &[], DebugLoc::None);
             cx
         })
     };
@@ -583,7 +583,6 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
             Call(bcx,
                  dtor,
                  &[PointerCast(bcx, Load(bcx, data_ptr), Type::i8p(bcx.ccx()))],
-                 None,
                  DebugLoc::None);
             bcx
         }
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index edca908e7ba..171a1fe977d 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -407,7 +407,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     // These are the only intrinsic functions that diverge.
     if name == "abort" {
         let llfn = ccx.get_intrinsic(&("llvm.trap"));
-        Call(bcx, llfn, &[], None, call_debug_location);
+        Call(bcx, llfn, &[], call_debug_location);
         fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
         Unreachable(bcx);
         return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to()));
@@ -442,11 +442,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let simple = get_simple_intrinsic(ccx, &name);
     let llval = match (simple, &name[..]) {
         (Some(llfn), _) => {
-            Call(bcx, llfn, &llargs, None, call_debug_location)
+            Call(bcx, llfn, &llargs, call_debug_location)
         }
         (_, "breakpoint") => {
             let llfn = ccx.get_intrinsic(&("llvm.debugtrap"));
-            Call(bcx, llfn, &[], None, call_debug_location)
+            Call(bcx, llfn, &[], call_debug_location)
         }
         (_, "size_of") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
@@ -636,13 +636,13 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                         "cttz" => count_zeros_intrinsic(bcx, &format!("llvm.cttz.i{}", width),
                                                         llargs[0], call_debug_location),
                         "ctpop" => Call(bcx, ccx.get_intrinsic(&format!("llvm.ctpop.i{}", width)),
-                                        &llargs, None, call_debug_location),
+                                        &llargs, call_debug_location),
                         "bswap" => {
                             if width == 8 {
                                 llargs[0] // byte swap a u8/i8 is just a no-op
                             } else {
                                 Call(bcx, ccx.get_intrinsic(&format!("llvm.bswap.i{}", width)),
-                                        &llargs, None, call_debug_location)
+                                        &llargs, call_debug_location)
                             }
                         }
                         "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
@@ -951,7 +951,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                     let f = declare::declare_cfn(ccx,
                                                  name,
                                                  Type::func(&inputs, &outputs));
-                    Call(bcx, f, &llargs, None, call_debug_location)
+                    Call(bcx, f, &llargs, call_debug_location)
                 }
             };
 
@@ -1024,7 +1024,6 @@ fn copy_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            Mul(bcx, size, count, DebugLoc::None),
            align,
            C_bool(ccx, volatile)],
-         None,
          call_debug_location)
 }
 
@@ -1054,7 +1053,6 @@ fn memset_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
            Mul(bcx, size, count, DebugLoc::None),
            align,
            C_bool(ccx, volatile)],
-         None,
          call_debug_location)
 }
 
@@ -1065,7 +1063,7 @@ fn count_zeros_intrinsic(bcx: Block,
                          -> ValueRef {
     let y = C_bool(bcx.ccx(), false);
     let llfn = bcx.ccx().get_intrinsic(&name);
-    Call(bcx, llfn, &[val, y], None, call_debug_location)
+    Call(bcx, llfn, &[val, y], call_debug_location)
 }
 
 fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
@@ -1078,7 +1076,7 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let llfn = bcx.ccx().get_intrinsic(&name);
 
     // Convert `i1` to a `bool`, and write it to the out parameter
-    let val = Call(bcx, llfn, &[a, b], None, call_debug_location);
+    let val = Call(bcx, llfn, &[a, b], call_debug_location);
     let result = ExtractValue(bcx, val, 0);
     let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx()));
     Store(bcx, result, StructGEP(bcx, out, 0));
@@ -1094,7 +1092,7 @@ fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                              dest: ValueRef,
                              dloc: DebugLoc) -> Block<'blk, 'tcx> {
     if bcx.sess().no_landing_pads() {
-        Call(bcx, func, &[data], None, dloc);
+        Call(bcx, func, &[data], dloc);
         Store(bcx, C_null(Type::i8p(bcx.ccx())), dest);
         bcx
     } else if wants_msvc_seh(bcx.sess()) {
@@ -1165,9 +1163,9 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // More information can be found in libstd's seh.rs implementation.
         let slot = Alloca(bcx, Type::i8p(ccx), "slot");
         let localescape = ccx.get_intrinsic(&"llvm.localescape");
-        Call(bcx, localescape, &[slot], None, dloc);
+        Call(bcx, localescape, &[slot], dloc);
         Store(bcx, local_ptr, slot);
-        Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, None, dloc);
+        Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, dloc);
 
         Ret(normal, C_i32(ccx, 0), dloc);
 
@@ -1184,7 +1182,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Note that no invoke is used here because by definition this function
     // can't panic (that's what it's catching).
-    let ret = Call(bcx, llfn, &[func, data, local_ptr], None, dloc);
+    let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc);
     Store(bcx, ret, dest);
     return bcx
 }
@@ -1242,7 +1240,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         let func = llvm::get_param(bcx.fcx.llfn, 0);
         let data = llvm::get_param(bcx.fcx.llfn, 1);
         let local_ptr = llvm::get_param(bcx.fcx.llfn, 2);
-        Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
+        Invoke(bcx, func, &[data], then.llbb, catch.llbb, dloc);
         Ret(then, C_i32(ccx, 0), dloc);
 
         // Type indicator for the exception being thrown.
@@ -1262,7 +1260,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Note that no invoke is used here because by definition this function
     // can't panic (that's what it's catching).
-    let ret = Call(bcx, llfn, &[func, data, local_ptr], None, dloc);
+    let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc);
     Store(bcx, ret, dest);
     return bcx;
 }
@@ -1376,11 +1374,10 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
     // For more info, see seh.rs in the standard library.
     let do_trans = |bcx: Block, ehptrs, base_pointer| {
         let rust_try_fn = BitCast(bcx, rust_try_fn, Type::i8p(ccx));
-        let parentfp = Call(bcx, recoverfp, &[rust_try_fn, base_pointer],
-                            None, dloc);
+        let parentfp = Call(bcx, recoverfp, &[rust_try_fn, base_pointer], dloc);
         let arg = Call(bcx, localrecover,
-                       &[rust_try_fn, parentfp, C_i32(ccx, 0)], None, dloc);
-        let ret = Call(bcx, rust_try_filter, &[ehptrs, arg], None, dloc);
+                       &[rust_try_fn, parentfp, C_i32(ccx, 0)], dloc);
+        let ret = Call(bcx, rust_try_filter, &[ehptrs, arg], dloc);
         Ret(bcx, ret, dloc);
     };
 
@@ -1402,7 +1399,7 @@ fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
             }),
         });
         gen_fn(fcx, "__rustc_try_filter", filter_fn_ty, output, &mut |bcx| {
-            let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], None, dloc);
+            let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], dloc);
             let exn = InBoundsGEP(bcx, ebp, &[C_i32(ccx, -20)]);
             let exn = Load(bcx, BitCast(bcx, exn, Type::i8p(ccx).ptr_to()));
             do_trans(bcx, exn, ebp);
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index 00babf6792f..a91b2dfcf92 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -9,11 +9,10 @@
 // except according to those terms.
 
 use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
-use rustc::middle::ty;
+use rustc::middle::{infer, ty};
 use rustc::mir::repr as mir;
-use trans::abi::Abi;
+use trans::abi::{Abi, FnType};
 use trans::adt;
-use trans::attributes;
 use trans::base;
 use trans::build;
 use trans::callee::{Callee, Fn, Virtual};
@@ -141,11 +140,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                &[llvalue],
                                self.llblock(target),
                                unwind.llbb(),
-                               cleanup_bundle.as_ref(),
-                               None);
+                               cleanup_bundle.as_ref());
                     self.bcx(target).at_start(|bcx| drop::drop_fill(bcx, lvalue.llval, ty));
                 } else {
-                    bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref(), None);
+                    bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref());
                     drop::drop_fill(&bcx, lvalue.llval, ty);
                     funclet_br(bcx, self.llblock(target));
                 }
@@ -244,7 +242,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
                     datum
                 };
-                let attrs = attributes::from_fn_type(bcx.ccx(), datum.ty);
+
+                let fn_ty = match datum.ty.sty {
+                    ty::TyFnDef(_, _, f) | ty::TyFnPtr(f) => {
+                        let sig = bcx.tcx().erase_late_bound_regions(&f.sig);
+                        let sig = infer::normalize_associated_type(bcx.tcx(), &sig);
+                        FnType::new(bcx.ccx(), f.abi, &sig, &[])
+                    }
+                    _ => unreachable!("expected fn type")
+                };
 
                 // Many different ways to call a function handled here
                 match (is_foreign, cleanup, destination) {
@@ -253,12 +259,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let cleanup = self.bcx(cleanup);
                         let landingpad = self.make_landing_pad(cleanup);
                         let unreachable_blk = self.unreachable_block();
-                        bcx.invoke(datum.val,
-                                   &llargs[..],
-                                   unreachable_blk.llbb,
-                                   landingpad.llbb(),
-                                   cleanup_bundle.as_ref(),
-                                   Some(attrs));
+                        let cs = bcx.invoke(datum.val,
+                                            &llargs[..],
+                                            unreachable_blk.llbb,
+                                            landingpad.llbb(),
+                                            cleanup_bundle.as_ref());
+                        fn_ty.apply_attrs_callsite(cs);
                         landingpad.at_start(|bcx| for op in args {
                             self.set_operand_dropped(bcx, op);
                         });
@@ -270,8 +276,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                    &llargs[..],
                                                    self.llblock(success),
                                                    landingpad.llbb(),
-                                                   cleanup_bundle.as_ref(),
-                                                   Some(attrs));
+                                                   cleanup_bundle.as_ref());
+                        fn_ty.apply_attrs_callsite(invokeret);
                         if must_copy_dest {
                             let (ret_dest, ret_ty) = ret_dest_ty
                                 .expect("return destination and type not set");
@@ -289,18 +295,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         });
                     },
                     (false, _, &None) => {
-                        bcx.call(datum.val,
-                                 &llargs[..],
-                                 cleanup_bundle.as_ref(),
-                                 Some(attrs));
+                        let cs = bcx.call(datum.val,
+                                          &llargs[..],
+                                          cleanup_bundle.as_ref());
+                        fn_ty.apply_attrs_callsite(cs);
                         // no need to drop args, because the call never returns
                         bcx.unreachable();
                     }
                     (false, _, &Some((_, target))) => {
                         let llret = bcx.call(datum.val,
                                              &llargs[..],
-                                             cleanup_bundle.as_ref(),
-                                             Some(attrs));
+                                             cleanup_bundle.as_ref());
+                        fn_ty.apply_attrs_callsite(llret);
                         if must_copy_dest {
                             let (ret_dest, ret_ty) = ret_dest_ty
                                 .expect("return destination and type not set");
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs
index 99c388f604b..cf4c2dfd99b 100644
--- a/src/librustc_trans/trans/mir/rvalue.rs
+++ b/src/librustc_trans/trans/mir/rvalue.rs
@@ -515,10 +515,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     if input_ty == tcx.types.f32 {
                         let lllhs = bcx.fpext(lhs, f64t);
                         let llrhs = bcx.fpext(rhs, f64t);
-                        let llres = bcx.call(llfn, &[lllhs, llrhs], None, None);
+                        let llres = bcx.call(llfn, &[lllhs, llrhs], None);
                         bcx.fptrunc(llres, Type::f32(bcx.ccx()))
                     } else {
-                        bcx.call(llfn, &[lhs, rhs], None, None)
+                        bcx.call(llfn, &[lhs, rhs], None)
                     }
                 } else {
                     bcx.frem(lhs, rhs)