diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index 4fe82f03b036e..0b9dd4f6a78d0 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -10,7 +10,7 @@ use crate::ty::query::TyCtxtAt;
 use backtrace::Backtrace;
 use errors::DiagnosticBuilder;
 use rustc_macros::HashStable;
-use rustc_target::spec::abi::Abi;
+use rustc_target::abi;
 use syntax_pos::{Pos, Span};
 use syntax::symbol::Symbol;
 
@@ -396,7 +396,7 @@ pub enum UnsupportedOpInfo<'tcx> {
     Unsupported(String),
 
     // -- Everything below is not categorized yet --
-    FunctionAbiMismatch(Abi, Abi),
+    FunctionAbiMismatch(abi::call::Conv, abi::call::Conv),
     FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
     FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
     FunctionArgCountMismatch,
@@ -460,9 +460,9 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
                 write!(f, "type validation failed: {}", err)
             }
             NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
-            FunctionAbiMismatch(caller_abi, callee_abi) =>
+            FunctionAbiMismatch(caller_conv, callee_conv) =>
                 write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
-                    callee_abi, caller_abi),
+                    callee_conv, caller_conv),
             FunctionArgMismatch(caller_ty, callee_ty) =>
                 write!(f, "tried to call a function with argument of type {:?} \
                            passing data of type {:?}",
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 777db38850fec..801dfa81ef178 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -1,8 +1,7 @@
 use crate::hir::CodegenFnAttrFlags;
-use crate::hir::Unsafety;
 use crate::hir::def::Namespace;
 use crate::hir::def_id::DefId;
-use crate::ty::{self, Ty, PolyFnSig, TypeFoldable, SubstsRef, TyCtxt};
+use crate::ty::{self, Ty, TypeFoldable, SubstsRef, TyCtxt};
 use crate::ty::print::{FmtPrinter, Printer};
 use crate::traits;
 use crate::middle::lang_items::DropInPlaceFnLangItem;
@@ -10,7 +9,6 @@ use rustc_target::spec::abi::Abi;
 use rustc_macros::HashStable;
 
 use std::fmt;
-use std::iter;
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 #[derive(HashStable, Lift)]
@@ -29,17 +27,26 @@ pub enum InstanceDef<'tcx> {
 
     /// `fn()` pointer where the function itself cannot be turned into a pointer.
     ///
-    /// One example in the compiler today is functions annotated with `#[track_caller]`, which
-    /// must have their implicit caller location argument populated for a call. Because this is a
-    /// required part of the function's ABI but can't be tracked as a property of the function
-    /// pointer, we create a single "caller location" at the site where the function is reified.
+    /// One example is `<dyn Trait as Trait>::fn`, where the shim contains
+    /// a virtual call, which codegen supports only via a direct call to the
+    /// `<dyn Trait as Trait>::fn` instance (an `InstanceDef::Virtual`).
+    ///
+    /// Another example is functions annotated with `#[track_caller]`, which
+    /// must have their implicit caller location argument populated for a call.
+    /// Because this is a required part of the function's ABI but can't be tracked
+    /// as a property of the function pointer, we use a single "caller location"
+    /// (the definition of the function itself).
     ReifyShim(DefId),
 
     /// `<fn() as FnTrait>::call_*`
     /// `DefId` is `FnTrait::call_*`.
     FnPtrShim(DefId, Ty<'tcx>),
 
-    /// `<dyn Trait as Trait>::fn`
+    /// `<dyn Trait as Trait>::fn`, "direct calls" of which are implicitly
+    /// codegen'd as virtual calls.
+    ///
+    /// NB: if this is reified to a `fn` pointer, a `ReifyShim` is used
+    /// (see `ReifyShim` above for more details on that).
     Virtual(DefId, usize),
 
     /// `<[mut closure] as FnOnce>::call_once`
@@ -61,70 +68,6 @@ impl<'tcx> Instance<'tcx> {
             &ty,
         )
     }
-
-    fn fn_sig_noadjust(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
-        let ty = self.ty(tcx);
-        match ty.kind {
-            ty::FnDef(..) |
-            // Shims currently have type FnPtr. Not sure this should remain.
-            ty::FnPtr(_) => ty.fn_sig(tcx),
-            ty::Closure(def_id, substs) => {
-                let sig = substs.as_closure().sig(def_id, tcx);
-
-                let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
-                sig.map_bound(|sig| tcx.mk_fn_sig(
-                    iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
-                    sig.output(),
-                    sig.c_variadic,
-                    sig.unsafety,
-                    sig.abi
-                ))
-            }
-            ty::Generator(def_id, substs, _) => {
-                let sig = substs.as_generator().poly_sig(def_id, tcx);
-
-                let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
-                let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
-
-                let pin_did = tcx.lang_items().pin_type().unwrap();
-                let pin_adt_ref = tcx.adt_def(pin_did);
-                let pin_substs = tcx.intern_substs(&[env_ty.into()]);
-                let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
-
-                sig.map_bound(|sig| {
-                    let state_did = tcx.lang_items().gen_state().unwrap();
-                    let state_adt_ref = tcx.adt_def(state_did);
-                    let state_substs = tcx.intern_substs(&[
-                        sig.yield_ty.into(),
-                        sig.return_ty.into(),
-                    ]);
-                    let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
-
-                    tcx.mk_fn_sig(iter::once(env_ty),
-                        ret_ty,
-                        false,
-                        Unsafety::Normal,
-                        Abi::Rust
-                    )
-                })
-            }
-            _ => bug!("unexpected type {:?} in Instance::fn_sig_noadjust", ty)
-        }
-    }
-
-    pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
-        let mut fn_sig = self.fn_sig_noadjust(tcx);
-        if let InstanceDef::VtableShim(..) = self.def {
-            // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
-            fn_sig = fn_sig.map_bound(|mut fn_sig| {
-                let mut inputs_and_output = fn_sig.inputs_and_output.to_vec();
-                inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
-                fn_sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
-                fn_sig
-            });
-        }
-        fn_sig
-    }
 }
 
 impl<'tcx> InstanceDef<'tcx> {
@@ -196,7 +139,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
                 write!(f, " - intrinsic")
             }
             InstanceDef::Virtual(_, num) => {
-                write!(f, " - shim(#{})", num)
+                write!(f, " - virtual#{}", num)
             }
             InstanceDef::FnPtrShim(_, ty) => {
                 write!(f, " - shim({:?})", ty)
@@ -311,20 +254,23 @@ impl<'tcx> Instance<'tcx> {
         substs: SubstsRef<'tcx>,
     ) -> Option<Instance<'tcx>> {
         debug!("resolve(def_id={:?}, substs={:?})", def_id, substs);
-        Instance::resolve(tcx, param_env, def_id, substs).map(|resolved| {
+        Instance::resolve(tcx, param_env, def_id, substs).map(|mut resolved| {
             let has_track_caller = |def| tcx.codegen_fn_attrs(def).flags
                 .contains(CodegenFnAttrFlags::TRACK_CALLER);
 
             match resolved.def {
                 InstanceDef::Item(def_id) if has_track_caller(def_id) => {
                     debug!(" => fn pointer created for function with #[track_caller]");
-                    Instance {
-                        def: InstanceDef::ReifyShim(def_id),
-                        substs,
-                    }
-                },
-                _ => resolved,
+                    resolved.def = InstanceDef::ReifyShim(def_id);
+                }
+                InstanceDef::Virtual(def_id, _) => {
+                    debug!(" => fn pointer created for virtual call");
+                    resolved.def = InstanceDef::ReifyShim(def_id);
+                }
+                _ => {}
             }
+
+            resolved
         })
     }
 
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index b6050a5caf13e..0a1480664639f 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -2339,6 +2339,76 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for LayoutError<'tcx> {
     }
 }
 
+
+impl<'tcx> ty::Instance<'tcx> {
+    // NOTE(eddyb) this is private to avoid using it from outside of
+    // `FnAbi::of_instance` - any other uses are either too high-level
+    // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead),
+    // or should go through `FnAbi` instead, to avoid losing any
+    // adjustments `FnAbi::of_instance` might be performing.
+    fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
+        let ty = self.ty(tcx);
+        match ty.kind {
+            ty::FnDef(..) |
+            // Shims currently have type FnPtr. Not sure this should remain.
+            ty::FnPtr(_) => {
+                let mut sig = ty.fn_sig(tcx);
+                if let ty::InstanceDef::VtableShim(..) = self.def {
+                    // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
+                    sig = sig.map_bound(|mut sig| {
+                        let mut inputs_and_output = sig.inputs_and_output.to_vec();
+                        inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
+                        sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
+                        sig
+                    });
+                }
+                sig
+            }
+            ty::Closure(def_id, substs) => {
+                let sig = substs.as_closure().sig(def_id, tcx);
+
+                let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
+                sig.map_bound(|sig| tcx.mk_fn_sig(
+                    iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
+                    sig.output(),
+                    sig.c_variadic,
+                    sig.unsafety,
+                    sig.abi
+                ))
+            }
+            ty::Generator(def_id, substs, _) => {
+                let sig = substs.as_generator().poly_sig(def_id, tcx);
+
+                let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
+                let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
+
+                let pin_did = tcx.lang_items().pin_type().unwrap();
+                let pin_adt_ref = tcx.adt_def(pin_did);
+                let pin_substs = tcx.intern_substs(&[env_ty.into()]);
+                let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
+
+                sig.map_bound(|sig| {
+                    let state_did = tcx.lang_items().gen_state().unwrap();
+                    let state_adt_ref = tcx.adt_def(state_did);
+                    let state_substs = tcx.intern_substs(&[
+                        sig.yield_ty.into(),
+                        sig.return_ty.into(),
+                    ]);
+                    let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+
+                    tcx.mk_fn_sig(iter::once(env_ty),
+                        ret_ty,
+                        false,
+                        hir::Unsafety::Normal,
+                        rustc_target::spec::abi::Abi::Rust
+                    )
+                })
+            }
+            _ => bug!("unexpected type {:?} in Instance::fn_sig", ty)
+        }
+    }
+}
+
 pub trait FnAbiExt<'tcx, C>
 where
     C: LayoutOf<Ty = Ty<'tcx>, TyLayout = TyLayout<'tcx>>
@@ -2347,12 +2417,22 @@ where
         + HasTyCtxt<'tcx>
         + HasParamEnv<'tcx>,
 {
-    fn of_instance(cx: &C, instance: ty::Instance<'tcx>) -> Self;
-    fn new(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
-    fn new_vtable(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
+    /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
+    ///
+    /// NB: this doesn't handle virtual calls - those should use `FnAbi::of_instance`
+    /// instead, where the instance is a `InstanceDef::Virtual`.
+    fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
+
+    /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for
+    /// direct calls to an `fn`.
+    ///
+    /// NB: that includes virtual calls, which are represented by "direct calls"
+    /// to a `InstanceDef::Virtual` instance (of `<dyn Trait as Trait>::fn`).
+    fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
+
     fn new_internal(
         cx: &C,
-        sig: ty::FnSig<'tcx>,
+        sig: ty::PolyFnSig<'tcx>,
         extra_args: &[Ty<'tcx>],
         mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
     ) -> Self;
@@ -2367,25 +2447,19 @@ where
         + HasTyCtxt<'tcx>
         + HasParamEnv<'tcx>,
 {
-    fn of_instance(cx: &C, instance: ty::Instance<'tcx>) -> Self {
-        let sig = instance.fn_sig(cx.tcx());
-        let sig = cx
-            .tcx()
-            .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
-        call::FnAbi::new(cx, sig, &[])
-    }
-
-    fn new(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
+    fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
         call::FnAbi::new_internal(cx, sig, extra_args, |ty, _| ArgAbi::new(cx.layout_of(ty)))
     }
 
-    fn new_vtable(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
-        FnAbiExt::new_internal(cx, sig, extra_args, |ty, arg_idx| {
+    fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
+        let sig = instance.fn_sig_for_fn_abi(cx.tcx());
+
+        call::FnAbi::new_internal(cx, sig, extra_args, |ty, arg_idx| {
             let mut layout = cx.layout_of(ty);
             // Don't pass the vtable, it's not an argument of the virtual fn.
             // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
             // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen
-            if arg_idx == Some(0) {
+            if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) {
                 let fat_pointer_ty = if layout.is_unsized() {
                     // unsized `self` is passed as a pointer to `self`
                     // FIXME (mikeyhew) change this to use &own if it is ever added to the language
@@ -2436,15 +2510,19 @@ where
 
     fn new_internal(
         cx: &C,
-        sig: ty::FnSig<'tcx>,
+        sig: ty::PolyFnSig<'tcx>,
         extra_args: &[Ty<'tcx>],
         mk_arg_type: impl Fn(Ty<'tcx>, Option<usize>) -> ArgAbi<'tcx, Ty<'tcx>>,
     ) -> Self {
         debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args);
 
+        let sig = cx
+            .tcx()
+            .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
+
         use rustc_target::spec::abi::Abi::*;
         let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) {
-            RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::C,
+            RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
 
             // It's the ABI's job to select this, not ours.
             System => bug!("system abi should be selected elsewhere"),
diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs
index 804a7af41f39a..1f3c8e1953e4f 100644
--- a/src/librustc_codegen_llvm/abi.rs
+++ b/src/librustc_codegen_llvm/abi.rs
@@ -372,7 +372,7 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
 
     fn llvm_cconv(&self) -> llvm::CallConv {
         match self.conv {
-            Conv::C => llvm::CCallConv,
+            Conv::C | Conv::Rust => llvm::CCallConv,
             Conv::AmdGpuKernel => llvm::AmdGpuKernel,
             Conv::ArmAapcs => llvm::ArmAapcsCallConv,
             Conv::Msp430Intr => llvm::Msp430Intr,
@@ -388,6 +388,11 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
     }
 
     fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) {
+        // FIXME(eddyb) can this also be applied to callsites?
+        if self.ret.layout.abi.is_uninhabited() {
+            llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn);
+        }
+
         let mut i = 0;
         let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| {
             attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn, ty);
diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs
index 6f4e7d0f0caf5..33dc2513de584 100644
--- a/src/librustc_codegen_llvm/attributes.rs
+++ b/src/librustc_codegen_llvm/attributes.rs
@@ -2,19 +2,20 @@
 
 use std::ffi::CString;
 
-use rustc::hir::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc::hir::CodegenFnAttrFlags;
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::session::Session;
 use rustc::session::config::{Sanitizer, OptLevel};
-use rustc::ty::{self, TyCtxt, PolyFnSig};
+use rustc::ty::{self, TyCtxt, Ty};
 use rustc::ty::layout::HasTyCtxt;
 use rustc::ty::query::Providers;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_data_structures::fx::FxHashMap;
+use rustc_target::abi::call::Conv;
 use rustc_target::spec::PanicStrategy;
 use rustc_codegen_ssa::traits::*;
 
-use crate::abi::Abi;
+use crate::abi::FnAbi;
 use crate::attributes;
 use crate::llvm::{self, Attribute};
 use crate::llvm::AttributePlace::Function;
@@ -26,7 +27,7 @@ use crate::value::Value;
 
 /// Mark LLVM function to use provided inline heuristic.
 #[inline]
-pub fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
+fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
     use self::InlineAttr::*;
     match inline {
         Hint   => Attribute::InlineHint.apply_llfn(Function, val),
@@ -58,7 +59,7 @@ fn unwind(val: &'ll Value, can_unwind: bool) {
 
 /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
 #[inline]
-pub fn naked(val: &'ll Value, is_naked: bool) {
+fn naked(val: &'ll Value, is_naked: bool) {
     Attribute::Naked.toggle_llfn(Function, val, is_naked);
 }
 
@@ -72,7 +73,7 @@ pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value)
 
 /// Tell LLVM what instrument function to insert.
 #[inline]
-pub fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
+fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
     if cx.sess().instrument_mcount() {
         // Similar to `clang -pg` behavior. Handled by the
         // `post-inline-ee-instrument` LLVM pass.
@@ -88,7 +89,7 @@ pub fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
     }
 }
 
-pub fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
+fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
     // Only use stack probes if the target specification indicates that we
     // should be using stack probes
     if !cx.sess().target.target.options.stack_probes {
@@ -202,11 +203,10 @@ pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
 pub fn from_fn_attrs(
     cx: &CodegenCx<'ll, 'tcx>,
     llfn: &'ll Value,
-    id: Option<DefId>,
-    sig: PolyFnSig<'tcx>,
+    instance: ty::Instance<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
 ) {
-    let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id))
-        .unwrap_or_else(|| CodegenFnAttrs::new());
+    let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
 
     match codegen_fn_attrs.optimize {
         OptimizeAttr::None => {
@@ -224,6 +224,11 @@ pub fn from_fn_attrs(
         }
     }
 
+    // FIXME(eddyb) consolidate these two `inline` calls (and avoid overwrites).
+    if instance.def.is_inline(cx.tcx) {
+        inline(cx, llfn, attributes::InlineAttr::Hint);
+    }
+
     inline(cx, llfn, codegen_fn_attrs.inline);
 
     // The `uwtable` attribute according to LLVM is:
@@ -276,8 +281,7 @@ pub fn from_fn_attrs(
         // Special attribute for allocator functions, which can't unwind.
         false
     } else {
-        let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
-        if sig.abi == Abi::Rust || sig.abi == Abi::RustCall {
+        if fn_abi.conv == Conv::Rust {
             // Any Rust method (or `extern "Rust" fn` or `extern
             // "rust-call" fn`) is explicitly allowed to unwind
             // (unless it has no-unwind attribute, handled above).
@@ -331,16 +335,14 @@ pub fn from_fn_attrs(
     // Note that currently the `wasm-import-module` doesn't do anything, but
     // eventually LLVM 7 should read this and ferry the appropriate import
     // module to the output file.
-    if let Some(id) = id {
-        if cx.tcx.sess.target.target.arch == "wasm32" {
-            if let Some(module) = wasm_import_module(cx.tcx, id) {
-                llvm::AddFunctionAttrStringValue(
-                    llfn,
-                    llvm::AttributePlace::Function,
-                    const_cstr!("wasm-import-module"),
-                    &module,
-                );
-            }
+    if cx.tcx.sess.target.target.arch == "wasm32" {
+        if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
+            llvm::AddFunctionAttrStringValue(
+                llfn,
+                llvm::AttributePlace::Function,
+                const_cstr!("wasm-import-module"),
+                &module,
+            );
         }
     }
 }
diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs
index 08fa23f2a7c9e..e0db7cae99e1a 100644
--- a/src/librustc_codegen_llvm/callee.rs
+++ b/src/librustc_codegen_llvm/callee.rs
@@ -4,6 +4,7 @@
 //! and methods are represented as just a fn ptr and not a full
 //! closure.
 
+use crate::abi::{FnAbi, FnAbiLlvmExt};
 use crate::attributes;
 use crate::llvm;
 use crate::context::CodegenCx;
@@ -11,7 +12,7 @@ use crate::value::Value;
 use rustc_codegen_ssa::traits::*;
 
 use rustc::ty::{TypeFoldable, Instance};
-use rustc::ty::layout::{LayoutOf, HasTyCtxt};
+use rustc::ty::layout::{FnAbiExt, HasTyCtxt};
 
 /// Codegens a reference to a fn/method item, monomorphizing and
 /// inlining as it goes.
@@ -32,19 +33,19 @@ pub fn get_fn(
     assert!(!instance.substs.has_escaping_bound_vars());
     assert!(!instance.substs.has_param_types());
 
-    let sig = instance.fn_sig(cx.tcx());
     if let Some(&llfn) = cx.instances.borrow().get(&instance) {
         return llfn;
     }
 
     let sym = tcx.symbol_name(instance).name.as_str();
-    debug!("get_fn({:?}: {:?}) => {}", instance, sig, sym);
+    debug!("get_fn({:?}: {:?}) => {}", instance, instance.ty(cx.tcx()), sym);
 
-    // Create a fn pointer with the substituted signature.
-    let fn_ptr_ty = tcx.mk_fn_ptr(sig);
-    let llptrty = cx.backend_type(cx.layout_of(fn_ptr_ty));
+    let fn_abi = FnAbi::of_instance(cx, instance, &[]);
 
     let llfn = if let Some(llfn) = cx.get_declared_value(&sym) {
+        // Create a fn pointer with the new signature.
+        let llptrty = fn_abi.ptr_to_llvm_type(cx);
+
         // This is subtle and surprising, but sometimes we have to bitcast
         // the resulting fn pointer.  The reason has to do with external
         // functions.  If you have two crates that both bind the same C
@@ -76,14 +77,10 @@ pub fn get_fn(
             llfn
         }
     } else {
-        let llfn = cx.declare_fn(&sym, sig);
-        assert_eq!(cx.val_ty(llfn), llptrty);
+        let llfn = cx.declare_fn(&sym, &fn_abi);
         debug!("get_fn: not casting pointer!");
 
-        if instance.def.is_inline(tcx) {
-            attributes::inline(cx, llfn, attributes::InlineAttr::Hint);
-        }
-        attributes::from_fn_attrs(cx, llfn, Some(instance.def.def_id()), sig);
+        attributes::from_fn_attrs(cx, llfn, instance, &fn_abi);
 
         let instance_def_id = instance.def_id();
 
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index 4a40349cb73e8..39ea1f6f5dccf 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -1,3 +1,4 @@
+use crate::abi::FnAbi;
 use crate::attributes;
 use crate::llvm;
 use crate::llvm_util;
@@ -15,7 +16,7 @@ use rustc::mir::mono::CodegenUnit;
 use rustc::session::config::{self, DebugInfo};
 use rustc::session::Session;
 use rustc::ty::layout::{
-    LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx, HasParamEnv
+    FnAbiExt, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx, HasParamEnv
 };
 use rustc::ty::{self, Ty, TyCtxt, Instance};
 use rustc::util::nodemap::FxHashMap;
@@ -420,7 +421,8 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             Abi::C
         ));
 
-        let llfn = self.declare_fn("rust_eh_unwind_resume", sig);
+        let fn_abi = FnAbi::of_fn_ptr(self, sig, &[]);
+        let llfn = self.declare_fn("rust_eh_unwind_resume", &fn_abi);
         attributes::apply_target_cpu_attr(self, llfn);
         unwresume.set(Some(llfn));
         llfn
diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs
index 7713fe47004b9..c2359a2fe6d94 100644
--- a/src/librustc_codegen_llvm/debuginfo/mod.rs
+++ b/src/librustc_codegen_llvm/debuginfo/mod.rs
@@ -16,7 +16,7 @@ use rustc::hir::CodegenFnAttrFlags;
 use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
 use rustc::ty::subst::{SubstsRef, GenericArgKind};
 
-use crate::abi::Abi;
+use crate::abi::FnAbi;
 use crate::common::CodegenCx;
 use crate::builder::Builder;
 use crate::value::Value;
@@ -280,7 +280,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn create_function_debug_context(
         &self,
         instance: Instance<'tcx>,
-        sig: ty::FnSig<'tcx>,
+        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         llfn: &'ll Value,
         mir: &mir::Body<'_>,
     ) -> Option<FunctionDebugContext<&'ll DIScope>> {
@@ -308,7 +308,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         let file_metadata = file_metadata(self, &loc.file.name, def_id.krate);
 
         let function_type_metadata = unsafe {
-            let fn_signature = get_function_signature(self, sig);
+            let fn_signature = get_function_signature(self, fn_abi);
             llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), file_metadata, fn_signature)
         };
 
@@ -338,7 +338,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 
         let mut flags = DIFlags::FlagPrototyped;
 
-        if self.layout_of(sig.output()).abi.is_uninhabited() {
+        if fn_abi.ret.layout.abi.is_uninhabited() {
             flags |= DIFlags::FlagNoReturn;
         }
 
@@ -392,25 +392,20 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 
         fn get_function_signature<'ll, 'tcx>(
             cx: &CodegenCx<'ll, 'tcx>,
-            sig: ty::FnSig<'tcx>,
+            fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         ) -> &'ll DIArray {
             if cx.sess().opts.debuginfo == DebugInfo::Limited {
                 return create_DIArray(DIB(cx), &[]);
             }
 
-            let mut signature = Vec::with_capacity(sig.inputs().len() + 1);
+            let mut signature = Vec::with_capacity(fn_abi.args.len() + 1);
 
             // Return type -- llvm::DIBuilder wants this at index 0
-            signature.push(match sig.output().kind {
-                ty::Tuple(ref tys) if tys.is_empty() => None,
-                _ => Some(type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP))
-            });
-
-            let inputs = if sig.abi == Abi::RustCall {
-                &sig.inputs()[..sig.inputs().len() - 1]
+            signature.push(if fn_abi.ret.is_ignore() {
+                None
             } else {
-                sig.inputs()
-            };
+                Some(type_metadata(cx, fn_abi.ret.layout.ty, syntax_pos::DUMMY_SP))
+            });
 
             // Arguments types
             if cx.sess().target.target.options.is_like_msvc {
@@ -424,7 +419,8 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                 // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`.
                 // This transformed type is wrong, but these function types are
                 // already inaccurate due to ABI adjustments (see #42800).
-                signature.extend(inputs.iter().map(|&t| {
+                signature.extend(fn_abi.args.iter().map(|arg| {
+                    let t = arg.layout.ty;
                     let t = match t.kind {
                         ty::Array(ct, _)
                             if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => {
@@ -435,21 +431,11 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                     Some(type_metadata(cx, t, syntax_pos::DUMMY_SP))
                 }));
             } else {
-                signature.extend(inputs.iter().map(|t| {
-                    Some(type_metadata(cx, t, syntax_pos::DUMMY_SP))
+                signature.extend(fn_abi.args.iter().map(|arg| {
+                    Some(type_metadata(cx, arg.layout.ty, syntax_pos::DUMMY_SP))
                 }));
             }
 
-            if sig.abi == Abi::RustCall && !sig.inputs().is_empty() {
-                if let ty::Tuple(args) = sig.inputs()[sig.inputs().len() - 1].kind {
-                    signature.extend(
-                        args.iter().map(|argument_type| {
-                            Some(type_metadata(cx, argument_type.expect_ty(), syntax_pos::DUMMY_SP))
-                        })
-                    );
-                }
-            }
-
             create_DIArray(DIB(cx), &signature[..])
         }
 
diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs
index 46cdd2aaa9614..8b6fedc87db96 100644
--- a/src/librustc_codegen_llvm/declare.rs
+++ b/src/librustc_codegen_llvm/declare.rs
@@ -18,8 +18,7 @@ use crate::attributes;
 use crate::context::CodegenCx;
 use crate::type_::Type;
 use crate::value::Value;
-use rustc::ty::{self, PolyFnSig};
-use rustc::ty::layout::{FnAbiExt, LayoutOf};
+use rustc::ty::Ty;
 use rustc::session::config::Sanitizer;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_codegen_ssa::traits::*;
@@ -94,21 +93,12 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn declare_fn(
         &self,
         name: &str,
-        sig: PolyFnSig<'tcx>,
+        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
     ) -> &'ll Value {
-        debug!("declare_rust_fn(name={:?}, sig={:?})", name, sig);
-        let sig = self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
-        debug!("declare_rust_fn (after region erasure) sig={:?}", sig);
+        debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
 
-        let fn_abi = FnAbi::new(self, sig, &[]);
         let llfn = declare_raw_fn(self, name, fn_abi.llvm_cconv(), fn_abi.llvm_type(self));
-
-        if self.layout_of(sig.output()).abi.is_uninhabited() {
-            llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
-        }
-
         fn_abi.apply_attrs_llfn(self, llfn);
-
         llfn
     }
 
@@ -130,28 +120,6 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         }
     }
 
-    fn define_fn(
-        &self,
-        name: &str,
-        fn_sig: PolyFnSig<'tcx>,
-    ) -> &'ll Value {
-        if self.get_defined_value(name).is_some() {
-            self.sess().fatal(&format!("symbol `{}` already defined", name))
-        } else {
-            self.declare_fn(name, fn_sig)
-        }
-    }
-
-    fn define_internal_fn(
-        &self,
-        name: &str,
-        fn_sig: PolyFnSig<'tcx>,
-    ) -> &'ll Value {
-        let llfn = self.define_fn(name, fn_sig);
-        unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
-        llfn
-    }
-
     fn get_declared_value(&self, name: &str) -> Option<&'ll Value> {
         debug!("get_declared_value(name={:?})", name);
         let namebuf = SmallCStr::new(name);
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index aa55f3a19e2be..3201767c86015 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -1,4 +1,3 @@
-use crate::attributes;
 use crate::llvm;
 use crate::llvm_util;
 use crate::abi::{Abi, FnAbi, LlvmType, PassMode};
@@ -14,7 +13,7 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
 use rustc_codegen_ssa::glue;
 use rustc_codegen_ssa::base::{to_immediate, wants_msvc_seh, compare_simd_types};
 use rustc::ty::{self, Ty};
-use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Primitive};
+use rustc::ty::layout::{self, FnAbiExt, LayoutOf, HasTyCtxt, Primitive};
 use rustc::mir::interpret::GlobalId;
 use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
 use rustc::hir;
@@ -1013,8 +1012,10 @@ fn gen_fn<'ll, 'tcx>(
         hir::Unsafety::Unsafe,
         Abi::Rust
     ));
-    let llfn = cx.define_internal_fn(name, rust_fn_sig);
-    attributes::from_fn_attrs(cx, llfn, None, rust_fn_sig);
+    let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]);
+    let llfn = cx.declare_fn(name, &fn_abi);
+    // FIXME(eddyb) find a nicer way to do this.
+    unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
     let bx = Builder::new_block(cx, llfn, "entry-block");
     codegen(bx);
     llfn
diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs
index c1703ffd0c753..cbc8af4fd2710 100644
--- a/src/librustc_codegen_llvm/mono_item.rs
+++ b/src/librustc_codegen_llvm/mono_item.rs
@@ -1,3 +1,4 @@
+use crate::abi::FnAbi;
 use crate::attributes;
 use crate::base;
 use crate::context::CodegenCx;
@@ -6,7 +7,7 @@ use crate::type_of::LayoutLlvmExt;
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::mir::mono::{Linkage, Visibility};
 use rustc::ty::{TypeFoldable, Instance};
-use rustc::ty::layout::{LayoutOf, HasTyCtxt};
+use rustc::ty::layout::{FnAbiExt, LayoutOf};
 use rustc_codegen_ssa::traits::*;
 
 pub use rustc::mir::mono::MonoItem;
@@ -42,10 +43,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         assert!(!instance.substs.needs_infer() &&
                 !instance.substs.has_param_types());
 
-        let mono_sig = instance.fn_sig(self.tcx());
-        let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
-        let lldecl = self.declare_fn(symbol_name, mono_sig);
+        let fn_abi = FnAbi::of_instance(self, instance, &[]);
+        let lldecl = self.declare_fn(symbol_name, &fn_abi);
         unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) };
+        let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
         base::set_link_section(lldecl, &attrs);
         if linkage == Linkage::LinkOnceODR ||
             linkage == Linkage::WeakODR {
@@ -67,16 +68,9 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             }
         }
 
-        debug!("predefine_fn: mono_sig = {:?} instance = {:?}", mono_sig, instance);
-        if instance.def.is_inline(self.tcx) {
-            attributes::inline(self, lldecl, attributes::InlineAttr::Hint);
-        }
-        attributes::from_fn_attrs(
-            self,
-            lldecl,
-            Some(instance.def.def_id()),
-            mono_sig,
-        );
+        debug!("predefine_fn: instance = {:?}", instance);
+
+        attributes::from_fn_attrs(self, lldecl, instance, &fn_abi);
 
         self.instances.borrow_mut().insert(instance, lldecl);
     }
diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs
index c21e62e7562e3..d77bbb279216a 100644
--- a/src/librustc_codegen_llvm/type_of.rs
+++ b/src/librustc_codegen_llvm/type_of.rs
@@ -235,11 +235,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
                     cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).llvm_type(cx))
                 }
                 ty::FnPtr(sig) => {
-                    let sig = cx.tcx.normalize_erasing_late_bound_regions(
-                        ty::ParamEnv::reveal_all(),
-                        &sig,
-                    );
-                    cx.fn_ptr_backend_type(&FnAbi::new(cx, sig, &[]))
+                    cx.fn_ptr_backend_type(&FnAbi::of_fn_ptr(cx, sig, &[]))
                 }
                 _ => self.scalar_llvm_type_at(cx, scalar, Size::ZERO)
             };
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs
index 22693beb855d2..af340008cd9c6 100644
--- a/src/librustc_codegen_ssa/base.rs
+++ b/src/librustc_codegen_ssa/base.rs
@@ -368,13 +368,7 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
     // release builds.
     info!("codegen_instance({})", instance);
 
-    let sig = instance.fn_sig(cx.tcx());
-    let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
-
-    let lldecl = cx.get_fn(instance);
-
-    let mir = cx.tcx().instance_mir(instance.def);
-    mir::codegen_mir::<Bx>(cx, lldecl, &mir, instance, sig);
+    mir::codegen_mir::<Bx>(cx, instance);
 }
 
 /// Creates the `main` function which will initialize the rust runtime and call
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index 14be0e80fb482..6ac26bfc68eef 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -24,28 +24,28 @@ use super::operand::OperandValue::{Pair, Ref, Immediate};
 
 /// Used by `FunctionCx::codegen_terminator` for emitting common patterns
 /// e.g., creating a basic block, calling a function, etc.
-struct TerminatorCodegenHelper<'a, 'tcx> {
-    bb: &'a mir::BasicBlock,
-    terminator: &'a mir::Terminator<'tcx>,
+struct TerminatorCodegenHelper<'tcx> {
+    bb: mir::BasicBlock,
+    terminator: &'tcx mir::Terminator<'tcx>,
     funclet_bb: Option<mir::BasicBlock>,
 }
 
-impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
+impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
     /// Returns the associated funclet from `FunctionCx::funclets` for the
     /// `funclet_bb` member if it is not `None`.
-    fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn funclet<'b, Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
-    ) -> Option<&'c Bx::Funclet> {
+        fx: &'b mut FunctionCx<'a, 'tcx, Bx>,
+    ) -> Option<&'b Bx::Funclet> {
         match self.funclet_bb {
             Some(funcl) => fx.funclets[funcl].as_ref(),
             None => None,
         }
     }
 
-    fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
+    fn lltarget<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        fx: &mut FunctionCx<'a, 'tcx, Bx>,
         target: mir::BasicBlock,
     ) -> (Bx::BasicBlock, bool) {
         let span = self.terminator.source_info.span;
@@ -63,9 +63,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
     }
 
     /// Create a basic block.
-    fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn llblock<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        fx: &mut FunctionCx<'a, 'tcx, Bx>,
         target: mir::BasicBlock,
     ) -> Bx::BasicBlock {
         let (lltarget, is_cleanupret) = self.lltarget(fx, target);
@@ -83,9 +83,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
         }
     }
 
-    fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn funclet_br<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        fx: &mut FunctionCx<'a, 'tcx, Bx>,
         bx: &mut Bx,
         target: mir::BasicBlock,
     ) {
@@ -101,9 +101,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
 
     /// Call `fn_ptr` of `fn_abi` with the arguments `llargs`, the optional
     /// return destination `destination` and the cleanup function `cleanup`.
-    fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn do_call<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+        fx: &mut FunctionCx<'a, 'tcx, Bx>,
         bx: &mut Bx,
         fn_abi: FnAbi<'tcx, Ty<'tcx>>,
         fn_ptr: Bx::Value,
@@ -132,7 +132,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
         } else {
             let llret = bx.call(fn_ptr, &llargs, self.funclet(fx));
             bx.apply_attrs_callsite(&fn_abi, llret);
-            if fx.mir[*self.bb].is_cleanup {
+            if fx.mir[self.bb].is_cleanup {
                 // Cleanup is always the cold path. Don't inline
                 // drop glue. Also, when there is a deeply-nested
                 // struct, there are "symmetry" issues that cause
@@ -151,15 +151,15 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
 
     // Generate sideeffect intrinsic if jumping to any of the targets can form
     // a loop.
-    fn maybe_sideeffect<'b, 'tcx2: 'b, Bx: BuilderMethods<'b, 'tcx2>>(
+    fn maybe_sideeffect<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
-        mir: &'b mir::Body<'tcx>,
+        mir: &'tcx mir::Body<'tcx>,
         bx: &mut Bx,
         targets: &[mir::BasicBlock],
     ) {
         if bx.tcx().sess.opts.debugging_opts.insert_sideeffect {
-            if targets.iter().any(|target| {
-                *target <= *self.bb
+            if targets.iter().any(|&target| {
+                target <= self.bb
                     && target
                         .start_location()
                         .is_predecessor_of(self.bb.start_location(), mir)
@@ -173,9 +173,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
 /// Codegen implementations for some terminator variants.
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// Generates code for a `Resume` terminator.
-    fn codegen_resume_terminator<'b>(
+    fn codegen_resume_terminator(
         &mut self,
-        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        helper: TerminatorCodegenHelper<'tcx>,
         mut bx: Bx,
     ) {
         if let Some(funclet) = helper.funclet(self) {
@@ -201,9 +201,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         }
     }
 
-    fn codegen_switchint_terminator<'b>(
+    fn codegen_switchint_terminator(
         &mut self,
-        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        helper: TerminatorCodegenHelper<'tcx>,
         mut bx: Bx,
         discr: &mir::Operand<'tcx>,
         switch_ty: Ty<'tcx>,
@@ -316,9 +316,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     }
 
 
-    fn codegen_drop_terminator<'b>(
+    fn codegen_drop_terminator(
         &mut self,
-        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        helper: TerminatorCodegenHelper<'tcx>,
         mut bx: Bx,
         location: &mir::Place<'tcx>,
         target: mir::BasicBlock,
@@ -345,20 +345,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             &args1[..]
         };
         let (drop_fn, fn_abi) = match ty.kind {
+            // FIXME(eddyb) perhaps move some of this logic into
+            // `Instance::resolve_drop_in_place`?
             ty::Dynamic(..) => {
-                let sig = drop_fn.fn_sig(self.cx.tcx());
-                let sig = self.cx.tcx().normalize_erasing_late_bound_regions(
-                    ty::ParamEnv::reveal_all(),
-                    &sig,
-                );
-                let fn_abi = FnAbi::new_vtable(&bx, sig, &[]);
+                let virtual_drop = Instance {
+                    def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0),
+                    substs: drop_fn.substs,
+                };
+                let fn_abi = FnAbi::of_instance(&bx, virtual_drop, &[]);
                 let vtable = args[1];
                 args = &args[..1];
                 (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_abi), fn_abi)
             }
             _ => {
                 (bx.get_fn_addr(drop_fn),
-                 FnAbi::of_instance(&bx, drop_fn))
+                 FnAbi::of_instance(&bx, drop_fn, &[]))
             }
         };
         helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
@@ -367,9 +368,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                        unwind);
     }
 
-    fn codegen_assert_terminator<'b>(
+    fn codegen_assert_terminator(
         &mut self,
-        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        helper: TerminatorCodegenHelper<'tcx>,
         mut bx: Bx,
         terminator: &mir::Terminator<'tcx>,
         cond: &mir::Operand<'tcx>,
@@ -439,16 +440,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Obtain the panic entry point.
         let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
         let instance = ty::Instance::mono(bx.tcx(), def_id);
-        let fn_abi = FnAbi::of_instance(&bx, instance);
+        let fn_abi = FnAbi::of_instance(&bx, instance, &[]);
         let llfn = bx.get_fn_addr(instance);
 
         // Codegen the actual panic invoke/call.
         helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup);
     }
 
-    fn codegen_call_terminator<'b>(
+    fn codegen_call_terminator(
         &mut self,
-        helper: TerminatorCodegenHelper<'b, 'tcx>,
+        helper: TerminatorCodegenHelper<'tcx>,
         mut bx: Bx,
         terminator: &mir::Terminator<'tcx>,
         func: &mir::Operand<'tcx>,
@@ -474,12 +475,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             _ => bug!("{} is not callable", callee.layout.ty),
         };
         let def = instance.map(|i| i.def);
+
+        if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
+            // Empty drop glue; a no-op.
+            let &(_, target) = destination.as_ref().unwrap();
+            helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
+            helper.funclet_br(self, &mut bx, target);
+            return;
+        }
+
+        // FIXME(eddyb) avoid computing this if possible, when `instance` is
+        // available - right now `sig` is only needed for getting the `abi`
+        // and figuring out how many extra args were passed to a C-variadic `fn`.
         let sig = callee.layout.ty.fn_sig(bx.tcx());
-        let sig = bx.tcx().normalize_erasing_late_bound_regions(
-            ty::ParamEnv::reveal_all(),
-            &sig,
-        );
-        let abi = sig.abi;
+        let abi = sig.abi();
 
         // Handle intrinsics old codegen wants Expr's for, ourselves.
         let intrinsic = match def {
@@ -489,6 +498,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         };
         let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
 
+        let extra_args = &args[sig.inputs().skip_binder().len()..];
+        let extra_args = extra_args.iter().map(|op_arg| {
+            let op_ty = op_arg.ty(self.mir, bx.tcx());
+            self.monomorphize(&op_ty)
+        }).collect::<Vec<_>>();
+
+        let fn_abi = match instance {
+            Some(instance) => FnAbi::of_instance(&bx, instance, &extra_args),
+            None => FnAbi::of_fn_ptr(&bx, sig, &extra_args)
+        };
+
         if intrinsic == Some("transmute") {
             if let Some(destination_ref) = destination.as_ref() {
                 let &(ref dest, target) = destination_ref;
@@ -502,32 +522,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 // we can do what we like. Here, we declare that transmuting
                 // into an uninhabited type is impossible, so anything following
                 // it must be unreachable.
-                assert_eq!(bx.layout_of(sig.output()).abi, layout::Abi::Uninhabited);
+                assert_eq!(fn_abi.ret.layout.abi, layout::Abi::Uninhabited);
                 bx.unreachable();
             }
             return;
         }
 
-        let extra_args = &args[sig.inputs().len()..];
-        let extra_args = extra_args.iter().map(|op_arg| {
-            let op_ty = op_arg.ty(self.mir, bx.tcx());
-            self.monomorphize(&op_ty)
-        }).collect::<Vec<_>>();
-
-        let fn_abi = match def {
-            Some(ty::InstanceDef::Virtual(..)) => {
-                FnAbi::new_vtable(&bx, sig, &extra_args)
-            }
-            Some(ty::InstanceDef::DropGlue(_, None)) => {
-                // Empty drop glue; a no-op.
-                let &(_, target) = destination.as_ref().unwrap();
-                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
-                helper.funclet_br(self, &mut bx, target);
-                return;
-            }
-            _ => FnAbi::new(&bx, sig, &extra_args)
-        };
-
         // This should never be reachable at runtime:
         // We should only emit a call to this intrinsic in #[cfg(miri)] mode,
         // which means that we will never actually use the generate object files
@@ -556,7 +556,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let def_id =
                     common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
                 let instance = ty::Instance::mono(bx.tcx(), def_id);
-                let fn_abi = FnAbi::of_instance(&bx, instance);
+                let fn_abi = FnAbi::of_instance(&bx, instance, &[]);
                 let llfn = bx.get_fn_addr(instance);
 
                 if let Some((_, target)) = destination.as_ref() {
@@ -813,14 +813,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         &mut self,
         mut bx: Bx,
         bb: mir::BasicBlock,
-        terminator: &mir::Terminator<'tcx>
+        terminator: &'tcx mir::Terminator<'tcx>
     ) {
         debug!("codegen_terminator: {:?}", terminator);
 
         // Create the cleanup bundle, if needed.
         let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
         let helper = TerminatorCodegenHelper {
-            bb: &bb, terminator, funclet_bb
+            bb, terminator, funclet_bb
         };
 
         self.set_debug_loc(&mut bx, terminator.source_info);
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index fec31f07a349b..32af151d313c0 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -1,6 +1,6 @@
 use rustc::ty::{self, Ty, TypeFoldable, Instance};
 use rustc::ty::layout::{TyLayout, HasTyCtxt, FnAbiExt};
-use rustc::mir::{self, Body};
+use rustc::mir;
 use rustc_target::abi::call::{FnAbi, PassMode};
 use crate::base;
 use crate::traits::*;
@@ -21,7 +21,7 @@ use self::operand::{OperandRef, OperandValue};
 pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     instance: Instance<'tcx>,
 
-    mir: &'a mir::Body<'tcx>,
+    mir: &'tcx mir::Body<'tcx>,
 
     debug_context: Option<FunctionDebugContext<Bx::DIScope>>,
 
@@ -119,18 +119,18 @@ impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
 
 pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     cx: &'a Bx::CodegenCx,
-    llfn: Bx::Function,
-    mir: &'a Body<'tcx>,
     instance: Instance<'tcx>,
-    sig: ty::FnSig<'tcx>,
 ) {
     assert!(!instance.substs.needs_infer());
 
-    let fn_abi = FnAbi::new(cx, sig, &[]);
+    let llfn = cx.get_fn(instance);
+
+    let mir = cx.tcx().instance_mir(instance.def);
+
+    let fn_abi = FnAbi::of_instance(cx, instance, &[]);
     debug!("fn_abi: {:?}", fn_abi);
 
-    let debug_context =
-        cx.create_function_debug_context(instance, sig, llfn, mir);
+    let debug_context = cx.create_function_debug_context(instance, &fn_abi, llfn, mir);
 
     let mut bx = Bx::new_block(cx, llfn, "start");
 
@@ -247,7 +247,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 }
 
 fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    mir: &'a Body<'tcx>,
+    mir: &'tcx mir::Body<'tcx>,
     bx: &mut Bx,
     cleanup_kinds: &IndexVec<mir::BasicBlock, CleanupKind>,
     block_bxs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/src/librustc_codegen_ssa/traits/debuginfo.rs
index 802eaaa357ac0..e67201b710698 100644
--- a/src/librustc_codegen_ssa/traits/debuginfo.rs
+++ b/src/librustc_codegen_ssa/traits/debuginfo.rs
@@ -2,8 +2,9 @@ use super::BackendTypes;
 use crate::mir::debuginfo::{FunctionDebugContext, VariableKind};
 use rustc::hir::def_id::CrateNum;
 use rustc::mir;
-use rustc::ty::{self, Ty, Instance};
+use rustc::ty::{Ty, Instance};
 use rustc::ty::layout::Size;
+use rustc_target::abi::call::FnAbi;
 use syntax::ast::Name;
 use syntax_pos::{SourceFile, Span};
 
@@ -17,7 +18,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
     fn create_function_debug_context(
         &self,
         instance: Instance<'tcx>,
-        sig: ty::FnSig<'tcx>,
+        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         llfn: Self::Function,
         mir: &mir::Body<'_>,
     ) -> Option<FunctionDebugContext<Self::DIScope>>;
diff --git a/src/librustc_codegen_ssa/traits/declare.rs b/src/librustc_codegen_ssa/traits/declare.rs
index cd42044e48df8..1dd2c74dd4fa2 100644
--- a/src/librustc_codegen_ssa/traits/declare.rs
+++ b/src/librustc_codegen_ssa/traits/declare.rs
@@ -1,7 +1,8 @@
 use super::BackendTypes;
 use rustc::hir::def_id::DefId;
 use rustc::mir::mono::{Linkage, Visibility};
-use rustc::ty::{self, Instance};
+use rustc::ty::{Instance, Ty};
+use rustc_target::abi::call::FnAbi;
 
 pub trait DeclareMethods<'tcx>: BackendTypes {
     /// Declare a global value.
@@ -23,7 +24,7 @@ pub trait DeclareMethods<'tcx>: BackendTypes {
     ///
     /// If there’s a value with the same name already declared, the function will
     /// update the declaration and return existing Value instead.
-    fn declare_fn(&self, name: &str, sig: ty::PolyFnSig<'tcx>) -> Self::Function;
+    fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Function;
 
     /// Declare a global with an intention to define it.
     ///
@@ -38,20 +39,6 @@ pub trait DeclareMethods<'tcx>: BackendTypes {
     /// Use this function when you intend to define a global without a name.
     fn define_private_global(&self, ty: Self::Type) -> Self::Value;
 
-    /// Declare a Rust function with an intention to define it.
-    ///
-    /// Use this function when you intend to define a function. This function will
-    /// return panic if the name already has a definition associated with it. This
-    /// can happen with #[no_mangle] or #[export_name], for example.
-    fn define_fn(&self, name: &str, fn_sig: ty::PolyFnSig<'tcx>) -> Self::Value;
-
-    /// Declare a Rust function with an intention to define it.
-    ///
-    /// Use this function when you intend to define a function. This function will
-    /// return panic if the name already has a definition associated with it. This
-    /// can happen with #[no_mangle] or #[export_name], for example.
-    fn define_internal_fn(&self, name: &str, fn_sig: ty::PolyFnSig<'tcx>) -> Self::Value;
-
     /// Gets declared value by name.
     fn get_declared_value(&self, name: &str) -> Option<Self::Value>;
 
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index daa0a5e1bc4dd..eb17fc9a42f54 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -1,9 +1,10 @@
 use std::borrow::Cow;
 
-use rustc::{mir, ty};
-use rustc::ty::Instance;
-use rustc::ty::layout::{self, TyLayout, LayoutOf};
+use rustc::mir;
+use rustc::ty::{self, Instance, Ty};
+use rustc::ty::layout::{self, FnAbiExt, TyLayout, LayoutOf};
 use syntax::source_map::Span;
+use rustc_target::abi::call::FnAbi;
 use rustc_target::spec::abi::Abi;
 
 use super::{
@@ -60,16 +61,22 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 ..
             } => {
                 let func = self.eval_operand(func, None)?;
-                let (fn_val, abi) = match func.layout.ty.kind {
+                let (fn_val, abi, fn_abi) = match func.layout.ty.kind {
                     ty::FnPtr(sig) => {
                         let caller_abi = sig.abi();
                         let fn_ptr = self.read_scalar(func)?.not_undef()?;
                         let fn_val = self.memory.get_fn(fn_ptr)?;
-                        (fn_val, caller_abi)
+                        // FIXME(eddyb) compute `extra_args` to pass to
+                        // `FnAbi::of_fn_ptr` to support C variadics.
+                        (fn_val, caller_abi, FnAbi::of_fn_ptr(self, sig, &[]))
                     }
                     ty::FnDef(def_id, substs) => {
                         let sig = func.layout.ty.fn_sig(*self.tcx);
-                        (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi())
+                        let instance = self.resolve(def_id, substs)?;
+                        // FIXME(eddyb) compute `extra_args` to pass to
+                        // `FnAbi::of_instance` to support C variadics.
+                        let fn_abi = FnAbi::of_instance(self, instance, &[]);
+                        (FnVal::Instance(instance), sig.abi(), fn_abi)
                     },
                     _ => {
                         bug!("invalid callee of type {:?}", func.layout.ty)
@@ -84,6 +91,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     fn_val,
                     terminator.source_info.span,
                     abi,
+                    &fn_abi,
                     &args[..],
                     ret,
                     *cleanup
@@ -234,7 +242,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         fn_val: FnVal<'tcx, M::ExtraFnVal>,
         span: Span,
+        // FIXME(eddyb) this shouldn't be needed, its main purpose
+        // right now is special-casing `RustCall` and intrinsics.
         caller_abi: Abi,
+        caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         args: &[OpTy<'tcx, M::PointerTag>],
         ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
         unwind: Option<mir::BasicBlock>
@@ -249,26 +260,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
 
         // ABI check
+        // FIXME(eddyb) check every aspect of `FnAbi` and use it below
+        // for adjusting various details (such as ignoring ZSTs).
         {
-            let callee_abi = {
-                let instance_ty = instance.ty(*self.tcx);
-                match instance_ty.kind {
-                    ty::FnDef(..) =>
-                        instance_ty.fn_sig(*self.tcx).abi(),
-                    ty::Closure(..) => Abi::RustCall,
-                    ty::Generator(..) => Abi::Rust,
-                    _ => bug!("unexpected callee ty: {:?}", instance_ty),
-                }
-            };
-            let normalize_abi = |abi| match abi {
-                Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
-                    // These are all the same ABI, really.
-                    Abi::Rust,
-                abi =>
-                    abi,
-            };
-            if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
-                throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi))
+            // FIXME(eddyb) compute `extra_args` to pass to
+            // `FnAbi::of_instance` to support C variadics.
+            let callee_fn_abi = FnAbi::of_instance(self, instance, &[]);
+            if caller_fn_abi.conv != callee_fn_abi.conv {
+                throw_unsup!(FunctionAbiMismatch(caller_fn_abi.conv, callee_fn_abi.conv))
             }
         }
 
@@ -444,7 +443,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 });
                 trace!("Patched self operand to {:#?}", args[0]);
                 // recurse with concrete function
-                self.eval_fn_call(drop_fn, span, caller_abi, &args, ret, unwind)
+                self.eval_fn_call(drop_fn, span, caller_abi, caller_fn_abi, &args, ret, unwind)
             }
         }
     }
@@ -479,10 +478,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let ty = self.tcx.mk_unit(); // return type is ()
         let dest = MPlaceTy::dangling(self.layout_of(ty)?, self);
 
+        // FIXME(eddyb) perhaps compute this more like the way it's done in
+        // `rustc_codegen_ssa::mir::block`?
+        let fn_abi = FnAbi::of_instance(self, instance, &[]);
+
         self.eval_fn_call(
             FnVal::Instance(instance),
             span,
             Abi::Rust,
+            &fn_abi,
             &[arg.into()],
             Some((dest.into(), target)),
             unwind
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index a4c4c7ff616d9..1bebac321c259 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -741,23 +741,21 @@ fn visit_instance_use<'tcx>(
     }
 
     match instance.def {
-        ty::InstanceDef::Intrinsic(def_id) => {
+        ty::InstanceDef::Virtual(..) |
+        ty::InstanceDef::Intrinsic(_) => {
             if !is_direct_call {
-                bug!("intrinsic {:?} being reified", def_id);
+                bug!("{:?} being reified", instance);
             }
         }
-        ty::InstanceDef::VtableShim(..) |
-        ty::InstanceDef::ReifyShim(..) |
-        ty::InstanceDef::Virtual(..) |
         ty::InstanceDef::DropGlue(_, None) => {
-            // Don't need to emit shim if we are calling directly.
+            // Don't need to emit noop drop glue if we are calling directly.
             if !is_direct_call {
                 output.push(create_fn_mono_item(instance));
             }
         }
-        ty::InstanceDef::DropGlue(_, Some(_)) => {
-            output.push(create_fn_mono_item(instance));
-        }
+        ty::InstanceDef::DropGlue(_, Some(_)) |
+        ty::InstanceDef::VtableShim(..) |
+        ty::InstanceDef::ReifyShim(..) |
         ty::InstanceDef::ClosureOnceShim { .. } |
         ty::InstanceDef::Item(..) |
         ty::InstanceDef::FnPtrShim(..) |
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 17f5e3d4e47a9..a2b0f85fdac1a 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -35,7 +35,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
         ty::InstanceDef::VtableShim(def_id) => {
             build_call_shim(
                 tcx,
-                def_id,
+                instance,
                 Adjustment::DerefMove,
                 CallKind::Direct(def_id),
                 None,
@@ -60,27 +60,27 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
 
             build_call_shim(
                 tcx,
-                def_id,
+                instance,
                 adjustment,
                 CallKind::Indirect,
                 Some(arg_tys)
             )
         }
         // We are generating a call back to our def-id, which the
-        // codegen backend knows to turn to an actual virtual call.
-        ty::InstanceDef::Virtual(def_id, _) |
-        // ...or we are generating a direct call to a function for which indirect calls must be
-        // codegen'd differently than direct ones (example: #[track_caller])
+        // codegen backend knows to turn to an actual call, be it
+        // a virtual call, or a direct call to a function for which
+        // indirect calls must be codegen'd differently than direct ones
+        // (such as `#[track_caller]`).
         ty::InstanceDef::ReifyShim(def_id) => {
             build_call_shim(
                 tcx,
-                def_id,
+                instance,
                 Adjustment::Identity,
                 CallKind::Direct(def_id),
                 None
             )
         }
-        ty::InstanceDef::ClosureOnceShim { call_once } => {
+        ty::InstanceDef::ClosureOnceShim { call_once: _ } => {
             let fn_mut = tcx.lang_items().fn_mut_trait().unwrap();
             let call_mut = tcx
                 .associated_items(fn_mut)
@@ -89,7 +89,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
 
             build_call_shim(
                 tcx,
-                call_once,
+                instance,
                 Adjustment::RefMut,
                 CallKind::Direct(call_mut),
                 None
@@ -109,6 +109,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
                 bug!("builtin clone shim {:?} not supported", instance)
             }
         }
+        ty::InstanceDef::Virtual(..) => {
+            bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
+        }
         ty::InstanceDef::Intrinsic(_) => {
             bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
         }
@@ -689,7 +692,7 @@ impl CloneShimBuilder<'tcx> {
     }
 }
 
-/// Builds a "call" shim for `def_id`. The shim calls the
+/// Builds a "call" shim for `instance`. The shim calls the
 /// function specified by `call_kind`, first adjusting its first
 /// argument according to `rcvr_adjustment`.
 ///
@@ -697,17 +700,30 @@ impl CloneShimBuilder<'tcx> {
 /// function will be untupled as these types.
 fn build_call_shim<'tcx>(
     tcx: TyCtxt<'tcx>,
-    def_id: DefId,
+    instance: ty::InstanceDef<'tcx>,
     rcvr_adjustment: Adjustment,
     call_kind: CallKind,
     untuple_args: Option<&[Ty<'tcx>]>,
 ) -> Body<'tcx> {
-    debug!("build_call_shim(def_id={:?}, rcvr_adjustment={:?}, \
+    debug!("build_call_shim(instance={:?}, rcvr_adjustment={:?}, \
             call_kind={:?}, untuple_args={:?})",
-           def_id, rcvr_adjustment, call_kind, untuple_args);
+           instance, rcvr_adjustment, call_kind, untuple_args);
 
+    let def_id = instance.def_id();
     let sig = tcx.fn_sig(def_id);
-    let sig = tcx.erase_late_bound_regions(&sig);
+    let mut sig = tcx.erase_late_bound_regions(&sig);
+
+    // FIXME(eddyb) avoid having this snippet both here and in
+    // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?).
+    if let ty::InstanceDef::VtableShim(..) = instance {
+        // Modify fn(self, ...) to fn(self: *mut Self, ...)
+        let mut inputs_and_output = sig.inputs_and_output.to_vec();
+        let self_arg = &mut inputs_and_output[0];
+        debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
+        *self_arg = tcx.mk_mut_ptr(*self_arg);
+        sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
+    }
+
     let span = tcx.def_span(def_id);
 
     debug!("build_call_shim: sig={:?}", sig);
@@ -722,14 +738,7 @@ fn build_call_shim<'tcx>(
     let rcvr = match rcvr_adjustment {
         Adjustment::Identity => Operand::Move(rcvr_l),
         Adjustment::Deref => Operand::Copy(tcx.mk_place_deref(rcvr_l)),
-        Adjustment::DerefMove => {
-            // fn(Self, ...) -> fn(*mut Self, ...)
-            let arg_ty = local_decls[rcvr_arg].ty;
-            debug_assert!(tcx.generics_of(def_id).has_self && arg_ty == tcx.types.self_param);
-            local_decls[rcvr_arg].ty = tcx.mk_mut_ptr(arg_ty);
-
-            Operand::Move(tcx.mk_place_deref(rcvr_l))
-        }
+        Adjustment::DerefMove => Operand::Move(tcx.mk_place_deref(rcvr_l)),
         Adjustment::RefMut => {
             // let rcvr = &mut rcvr;
             let ref_rcvr = local_decls.push(temp_decl(
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index aced12aa32acb..f7c55a6fcfe4c 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -2,6 +2,8 @@ use crate::abi::{self, Abi, Align, FieldPlacement, Size};
 use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods};
 use crate::spec::{self, HasTargetSpec};
 
+use rustc_macros::HashStable_Generic;
+
 mod aarch64;
 mod amdgpu;
 mod arm;
@@ -488,9 +490,15 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)]
+#[derive(HashStable_Generic)]
 pub enum Conv {
+    // General language calling conventions, for which every target
+    // should have its own backend (e.g. LLVM) support.
     C,
+    Rust,
+
+    // Target-specific calling conventions.
 
     ArmAapcs,