about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbjorn3 <bjorn3@users.noreply.github.com>2021-01-26 21:41:20 +0100
committerbjorn3 <bjorn3@users.noreply.github.com>2021-01-26 21:41:20 +0100
commitfc595f1a555d7f43802679511e9fdf1f64f2c49a (patch)
treee4301fadd2021859eeda2cd2f024750dafb62e85
parent4555737152c0f68df5596b16d6e996d19caf2a6a (diff)
downloadrust-fc595f1a555d7f43802679511e9fdf1f64f2c49a.tar.gz
rust-fc595f1a555d7f43802679511e9fdf1f64f2c49a.zip
[WIP] Use FnAbi everywhere instead of our own abi calculations
-rw-r--r--src/abi/comments.rs9
-rw-r--r--src/abi/mod.rs227
-rw-r--r--src/abi/pass_mode.rs113
-rw-r--r--src/abi/returning.rs138
-rw-r--r--src/analyze.rs6
-rw-r--r--src/base.rs3
-rw-r--r--src/common.rs57
-rw-r--r--src/lib.rs2
-rw-r--r--src/value_and_place.rs117
9 files changed, 355 insertions, 317 deletions
diff --git a/src/abi/comments.rs b/src/abi/comments.rs
index 4847b007a36..41cb4c627f8 100644
--- a/src/abi/comments.rs
+++ b/src/abi/comments.rs
@@ -4,7 +4,7 @@
 use std::borrow::Cow;
 
 use rustc_middle::mir;
-use rustc_target::abi::call::ArgAbi;
+use rustc_target::abi::call::PassMode;
 
 use cranelift_codegen::entity::EntityRef;
 
@@ -23,7 +23,8 @@ pub(super) fn add_arg_comment<'tcx>(
     local: Option<mir::Local>,
     local_field: Option<usize>,
     params: EmptySinglePair<Value>,
-    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
+    arg_abi_mode: PassMode,
+    arg_layout: TyAndLayout<'tcx>,
 ) {
     let local = if let Some(local) = local {
         Cow::Owned(format!("{:?}", local))
@@ -42,7 +43,7 @@ pub(super) fn add_arg_comment<'tcx>(
         Pair(param_a, param_b) => Cow::Owned(format!("= {:?}, {:?}", param_a, param_b)),
     };
 
-    let pass_mode = format!("{:?}", arg_abi.mode);
+    let pass_mode = format!("{:?}", arg_abi_mode);
     fx.add_global_comment(format!(
         "{kind:5}{local:>3}{local_field:<5} {params:10} {pass_mode:36} {ty:?}",
         kind = kind,
@@ -50,7 +51,7 @@ pub(super) fn add_arg_comment<'tcx>(
         local_field = local_field,
         params = params,
         pass_mode = pass_mode,
-        ty = arg_abi.layout.ty,
+        ty = arg_layout.ty,
     ));
 }
 
diff --git a/src/abi/mod.rs b/src/abi/mod.rs
index bc2111726d2..55ebd39e3f1 100644
--- a/src/abi/mod.rs
+++ b/src/abi/mod.rs
@@ -6,7 +6,8 @@ mod pass_mode;
 mod returning;
 
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_target::abi::call::PassMode as RustcPassMode;
+use rustc_middle::ty::layout::FnAbiExt;
+use rustc_target::abi::call::{Conv, FnAbi};
 use rustc_target::spec::abi::Abi;
 
 use cranelift_codegen::ir::AbiParam;
@@ -16,6 +17,7 @@ use crate::prelude::*;
 
 pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return};
 
+// FIXME remove
 // Copied from https://github.com/rust-lang/rust/blob/f52c72948aa1dd718cc1f168d21c91c584c0a662/src/librustc_middle/ty/layout.rs#L2301
 #[rustfmt::skip]
 pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> {
@@ -93,84 +95,38 @@ pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx
     }
 }
 
-fn clif_sig_from_fn_sig<'tcx>(
+fn clif_sig_from_fn_abi<'tcx>(
     tcx: TyCtxt<'tcx>,
     triple: &target_lexicon::Triple,
-    sig: FnSig<'tcx>,
-    is_vtable_fn: bool,
-    requires_caller_location: bool,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
 ) -> Signature {
-    let abi = match sig.abi {
-        Abi::System => Abi::C,
-        abi => abi,
-    };
-    let (call_conv, inputs, output): (CallConv, Vec<Ty<'tcx>>, Ty<'tcx>) = match abi {
-        Abi::Rust => (
-            CallConv::triple_default(triple),
-            sig.inputs().to_vec(),
-            sig.output(),
-        ),
-        Abi::C | Abi::Unadjusted => (
-            CallConv::triple_default(triple),
-            sig.inputs().to_vec(),
-            sig.output(),
-        ),
-        Abi::SysV64 => (CallConv::SystemV, sig.inputs().to_vec(), sig.output()),
-        Abi::RustCall => {
-            assert_eq!(sig.inputs().len(), 2);
-            let extra_args = match sig.inputs().last().unwrap().kind() {
-                ty::Tuple(ref tupled_arguments) => tupled_arguments,
-                _ => bug!("argument to function with \"rust-call\" ABI is not a tuple"),
-            };
-            let mut inputs: Vec<Ty<'tcx>> = vec![sig.inputs()[0]];
-            inputs.extend(extra_args.types());
-            (CallConv::triple_default(triple), inputs, sig.output())
+    let call_conv = match fn_abi.conv {
+        Conv::Rust | Conv::C => CallConv::triple_default(triple),
+        Conv::X86_64SysV => CallConv::SystemV,
+        Conv::X86_64Win64 => CallConv::WindowsFastcall,
+        Conv::ArmAapcs
+        | Conv::Msp430Intr
+        | Conv::PtxKernel
+        | Conv::X86Fastcall
+        | Conv::X86Intr
+        | Conv::X86Stdcall
+        | Conv::X86ThisCall
+        | Conv::X86VectorCall
+        | Conv::AmdGpuKernel
+        | Conv::AvrInterrupt
+        | Conv::AvrNonBlockingInterrupt => {
+            todo!("{:?}", fn_abi.conv)
         }
-        Abi::System => unreachable!(),
-        Abi::RustIntrinsic => (
-            CallConv::triple_default(triple),
-            sig.inputs().to_vec(),
-            sig.output(),
-        ),
-        _ => unimplemented!("unsupported abi {:?}", sig.abi),
     };
-
-    let inputs = inputs
-        .into_iter()
-        .enumerate()
-        .map(|(i, ty)| {
-            let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
-            if i == 0 && is_vtable_fn {
-                // Virtual calls turn their self param into a thin pointer.
-                // See https://github.com/rust-lang/rust/blob/37b6a5e5e82497caf5353d9d856e4eb5d14cbe06/src/librustc/ty/layout.rs#L2519-L2572 for more info
-                layout = tcx
-                    .layout_of(ParamEnv::reveal_all().and(tcx.mk_mut_ptr(tcx.mk_unit())))
-                    .unwrap();
-            }
-            let mut arg_abi = get_arg_abi(tcx, layout);
-            if abi != Abi::Rust && abi != Abi::RustCall && abi != Abi::RustIntrinsic {
-                match arg_abi.mode {
-                    RustcPassMode::Indirect {
-                        ref mut on_stack, ..
-                    } => *on_stack = true,
-                    _ => {}
-                }
-            }
-            arg_abi.get_abi_param(tcx).into_iter()
-        })
+    let inputs = fn_abi
+        .args
+        .iter()
+        .map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter())
         .flatten();
 
-    let return_arg_abi = get_arg_abi(
-        tcx,
-        tcx.layout_of(ParamEnv::reveal_all().and(output)).unwrap(),
-    );
-    let (return_ptr, returns) = return_arg_abi.get_abi_return(tcx);
+    let (return_ptr, returns) = fn_abi.ret.get_abi_return(tcx);
     // Sometimes the first param is an pointer to the place where the return value needs to be stored.
-    let mut params: Vec<_> = return_ptr.into_iter().chain(inputs).collect();
-
-    if requires_caller_location {
-        params.push(AbiParam::new(pointer_ty(tcx)));
-    }
+    let params: Vec<_> = return_ptr.into_iter().chain(inputs).collect();
 
     Signature {
         params,
@@ -194,12 +150,11 @@ pub(crate) fn get_function_sig<'tcx>(
             "Variadic function definitions are not yet supported",
         );
     }
-    clif_sig_from_fn_sig(
+
+    clif_sig_from_fn_abi(
         tcx,
         triple,
-        fn_sig,
-        false,
-        inst.def.requires_caller_location(tcx),
+        &FnAbi::of_instance(&RevealAllLayoutCx(tcx), inst, &[]),
     )
 }
 
@@ -337,6 +292,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>(
         Spread(Vec<Option<CValue<'tcx>>>),
     }
 
+    let fn_abi = fx.fn_abi.take().unwrap();
+    let mut arg_abis_iter = fn_abi.args.iter();
+
     let func_params = fx
         .mir
         .args_iter()
@@ -356,14 +314,16 @@ pub(crate) fn codegen_fn_prelude<'tcx>(
                 };
 
                 let mut params = Vec::new();
-                for (i, arg_ty) in tupled_arg_tys.types().enumerate() {
-                    let param = cvalue_for_param(fx, start_block, Some(local), Some(i), arg_ty);
+                for (i, _arg_ty) in tupled_arg_tys.types().enumerate() {
+                    let arg_abi = arg_abis_iter.next().unwrap();
+                    let param = cvalue_for_param(fx, start_block, Some(local), Some(i), arg_abi);
                     params.push(param);
                 }
 
                 (local, ArgKind::Spread(params), arg_ty)
             } else {
-                let param = cvalue_for_param(fx, start_block, Some(local), None, arg_ty);
+                let arg_abi = arg_abis_iter.next().unwrap();
+                let param = cvalue_for_param(fx, start_block, Some(local), None, arg_abi);
                 (local, ArgKind::Normal(param), arg_ty)
             }
         })
@@ -372,11 +332,13 @@ pub(crate) fn codegen_fn_prelude<'tcx>(
     assert!(fx.caller_location.is_none());
     if fx.instance.def.requires_caller_location(fx.tcx) {
         // Store caller location for `#[track_caller]`.
-        fx.caller_location = Some(
-            cvalue_for_param(fx, start_block, None, None, fx.tcx.caller_location_ty()).unwrap(),
-        );
+        let arg_abi = arg_abis_iter.next().unwrap();
+        fx.caller_location = Some(cvalue_for_param(fx, start_block, None, None, arg_abi).unwrap());
     }
 
+    assert!(arg_abis_iter.next().is_none(), "ArgAbi left behind");
+    fx.fn_abi = Some(fn_abi);
+
     fx.bcx.switch_to_block(start_block);
     fx.bcx.ins().nop();
 
@@ -504,6 +466,21 @@ pub(crate) fn codegen_terminator_call<'tcx>(
         None
     };
 
+    let extra_args = &args[fn_sig.inputs().len()..];
+    let extra_args = extra_args
+        .iter()
+        .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx)))
+        .collect::<Vec<_>>();
+    let fn_abi = if let Some(instance) = instance {
+        FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args)
+    } else {
+        FnAbi::of_fn_ptr(
+            &RevealAllLayoutCx(fx.tcx),
+            fn_ty.fn_sig(fx.tcx),
+            &extra_args,
+        )
+    };
+
     let is_cold = instance
         .map(|inst| {
             fx.tcx
@@ -541,8 +518,8 @@ pub(crate) fn codegen_terminator_call<'tcx>(
 
     //   | indirect call target
     //   |         | the first argument to be passed
-    //   v         v          v virtual calls are special cased below
-    let (func_ref, first_arg, is_virtual_call) = match instance {
+    //   v         v
+    let (func_ref, first_arg) = match instance {
         // Trait object call
         Some(Instance {
             def: InstanceDef::Virtual(_, idx),
@@ -553,23 +530,19 @@ pub(crate) fn codegen_terminator_call<'tcx>(
                 let nop_inst = fx.bcx.ins().nop();
                 fx.add_comment(
                     nop_inst,
-                    format!(
-                        "virtual call; self arg pass mode: {:?}",
-                        get_arg_abi(fx.tcx, args[0].layout()).mode,
-                    ),
+                    format!("virtual call; self arg pass mode: {:?}", &fn_abi.args[0],),
                 );
             }
             let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx);
-            (Some(method), Single(ptr), true)
+            (Some(method), Single(ptr))
         }
 
         // Normal call
         Some(_) => (
             None,
             args.get(0)
-                .map(|arg| adjust_arg_for_abi(fx, *arg))
+                .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0]))
                 .unwrap_or(Empty),
-            false,
         ),
 
         // Indirect call
@@ -583,23 +556,27 @@ pub(crate) fn codegen_terminator_call<'tcx>(
             (
                 Some(func),
                 args.get(0)
-                    .map(|arg| adjust_arg_for_abi(fx, *arg))
+                    .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0]))
                     .unwrap_or(Empty),
-                false,
             )
         }
     };
 
     let ret_place = destination.map(|(place, _)| place);
-    let (call_inst, call_args) =
-        self::returning::codegen_with_call_return_arg(fx, fn_sig, ret_place, |fx, return_ptr| {
+    let (call_inst, call_args) = self::returning::codegen_with_call_return_arg(
+        fx,
+        &fn_abi.ret,
+        ret_place,
+        |fx, return_ptr| {
+            let regular_args_count = args.len();
             let mut call_args: Vec<Value> = return_ptr
                 .into_iter()
                 .chain(first_arg.into_iter())
                 .chain(
                     args.into_iter()
+                        .enumerate()
                         .skip(1)
-                        .map(|arg| adjust_arg_for_abi(fx, arg).into_iter())
+                        .map(|(i, arg)| adjust_arg_for_abi(fx, arg, &fn_abi.args[i]).into_iter())
                         .flatten(),
                 )
                 .collect::<Vec<_>>();
@@ -610,17 +587,17 @@ pub(crate) fn codegen_terminator_call<'tcx>(
             {
                 // Pass the caller location for `#[track_caller]`.
                 let caller_location = fx.get_caller_location(span);
-                call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter());
+                call_args.extend(
+                    adjust_arg_for_abi(fx, caller_location, &fn_abi.args[regular_args_count])
+                        .into_iter(),
+                );
+                assert_eq!(fn_abi.args.len(), regular_args_count + 1);
+            } else {
+                assert_eq!(fn_abi.args.len(), regular_args_count);
             }
 
             let call_inst = if let Some(func_ref) = func_ref {
-                let sig = clif_sig_from_fn_sig(
-                    fx.tcx,
-                    fx.triple(),
-                    fn_sig,
-                    is_virtual_call,
-                    false, // calls through function pointers never pass the caller location
-                );
+                let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi);
                 let sig = fx.bcx.import_signature(sig);
                 fx.bcx.ins().call_indirect(sig, func_ref, &call_args)
             } else {
@@ -630,7 +607,8 @@ pub(crate) fn codegen_terminator_call<'tcx>(
             };
 
             (call_inst, call_args)
-        });
+        },
+    );
 
     // FIXME find a cleaner way to support varargs
     if fn_sig.c_variadic {
@@ -671,36 +649,33 @@ pub(crate) fn codegen_drop<'tcx>(
     drop_place: CPlace<'tcx>,
 ) {
     let ty = drop_place.layout().ty;
-    let drop_fn = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx);
+    let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx);
 
-    if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
+    if let ty::InstanceDef::DropGlue(_, None) = drop_instance.def {
         // we don't actually need to drop anything
     } else {
-        let drop_fn_ty = drop_fn.ty(fx.tcx, ParamEnv::reveal_all());
-        let fn_sig = fx.tcx.normalize_erasing_late_bound_regions(
-            ParamEnv::reveal_all(),
-            drop_fn_ty.fn_sig(fx.tcx),
-        );
-        assert_eq!(fn_sig.output(), fx.tcx.mk_unit());
-
         match ty.kind() {
             ty::Dynamic(..) => {
                 let (ptr, vtable) = drop_place.to_ptr_maybe_unsized();
                 let ptr = ptr.get_addr(fx);
                 let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable.unwrap());
 
-                let sig = clif_sig_from_fn_sig(
-                    fx.tcx,
-                    fx.triple(),
-                    fn_sig,
-                    true,
-                    false, // `drop_in_place` is never `#[track_caller]`
-                );
+                // FIXME(eddyb) perhaps move some of this logic into
+                // `Instance::resolve_drop_in_place`?
+                let virtual_drop = Instance {
+                    def: ty::InstanceDef::Virtual(drop_instance.def_id(), 0),
+                    substs: drop_instance.substs,
+                };
+                let fn_abi = FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), virtual_drop, &[]);
+
+                let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi);
                 let sig = fx.bcx.import_signature(sig);
                 fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]);
             }
             _ => {
-                assert!(!matches!(drop_fn.def, InstanceDef::Virtual(_, _)));
+                assert!(!matches!(drop_instance.def, InstanceDef::Virtual(_, _)));
+
+                let fn_abi = FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), drop_instance, &[]);
 
                 let arg_value = drop_place.place_ref(
                     fx,
@@ -712,17 +687,19 @@ pub(crate) fn codegen_drop<'tcx>(
                         },
                     )),
                 );
-                let arg_value = adjust_arg_for_abi(fx, arg_value);
+                let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0]);
 
                 let mut call_args: Vec<Value> = arg_value.into_iter().collect::<Vec<_>>();
 
-                if drop_fn.def.requires_caller_location(fx.tcx) {
+                if drop_instance.def.requires_caller_location(fx.tcx) {
                     // Pass the caller location for `#[track_caller]`.
                     let caller_location = fx.get_caller_location(span);
-                    call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter());
+                    call_args.extend(
+                        adjust_arg_for_abi(fx, caller_location, &fn_abi.args[1]).into_iter(),
+                    );
                 }
 
-                let func_ref = fx.get_function_ref(drop_fn);
+                let func_ref = fx.get_function_ref(drop_instance);
                 fx.bcx.ins().call(func_ref, &call_args);
             }
         }
diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs
index aec321bd4a0..e2b78bfeac0 100644
--- a/src/abi/pass_mode.rs
+++ b/src/abi/pass_mode.rs
@@ -1,9 +1,10 @@
 //! Argument passing
 
 use crate::prelude::*;
+use crate::value_and_place::assert_assignable;
 
 use cranelift_codegen::ir::ArgumentPurpose;
-use rustc_target::abi::call::{ArgAbi, ArgAttributes, PassMode as RustcPassMode};
+use rustc_target::abi::call::{ArgAbi, PassMode};
 pub(super) use EmptySinglePair::*;
 
 #[derive(Copy, Clone, Debug)]
@@ -68,8 +69,8 @@ pub(super) trait ArgAbiExt<'tcx> {
 impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
     fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> EmptySinglePair<AbiParam> {
         match self.mode {
-            RustcPassMode::Ignore => EmptySinglePair::Empty,
-            RustcPassMode::Direct(_) => match &self.layout.abi {
+            PassMode::Ignore => EmptySinglePair::Empty,
+            PassMode::Direct(_) => match &self.layout.abi {
                 Abi::Scalar(scalar) => {
                     EmptySinglePair::Single(AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())))
                 }
@@ -79,7 +80,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            RustcPassMode::Pair(_, _) => match &self.layout.abi {
+            PassMode::Pair(_, _) => match &self.layout.abi {
                 Abi::ScalarPair(a, b) => {
                     let a = scalar_to_clif_type(tcx, a.clone());
                     let b = scalar_to_clif_type(tcx, b.clone());
@@ -87,8 +88,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            RustcPassMode::Cast(_) => EmptySinglePair::Single(AbiParam::new(pointer_ty(tcx))),
-            RustcPassMode::Indirect {
+            PassMode::Cast(_) => EmptySinglePair::Single(AbiParam::new(pointer_ty(tcx))),
+            PassMode::Indirect {
                 attrs: _,
                 extra_attrs: None,
                 on_stack,
@@ -103,7 +104,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                     EmptySinglePair::Single(AbiParam::new(pointer_ty(tcx)))
                 }
             }
-            RustcPassMode::Indirect {
+            PassMode::Indirect {
                 attrs: _,
                 extra_attrs: Some(_),
                 on_stack,
@@ -119,8 +120,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
 
     fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) {
         match self.mode {
-            RustcPassMode::Ignore => (None, vec![]),
-            RustcPassMode::Direct(_) => match &self.layout.abi {
+            PassMode::Ignore => (None, vec![]),
+            PassMode::Direct(_) => match &self.layout.abi {
                 Abi::Scalar(scalar) => (
                     None,
                     vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))],
@@ -132,7 +133,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            RustcPassMode::Pair(_, _) => match &self.layout.abi {
+            PassMode::Pair(_, _) => match &self.layout.abi {
                 Abi::ScalarPair(a, b) => {
                     let a = scalar_to_clif_type(tcx, a.clone());
                     let b = scalar_to_clif_type(tcx, b.clone());
@@ -140,14 +141,14 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            RustcPassMode::Cast(_) => (
+            PassMode::Cast(_) => (
                 Some(AbiParam::special(
                     pointer_ty(tcx),
                     ArgumentPurpose::StructReturn,
                 )),
                 vec![],
             ),
-            RustcPassMode::Indirect {
+            PassMode::Indirect {
                 attrs: _,
                 extra_attrs: None,
                 on_stack,
@@ -161,7 +162,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                     vec![],
                 )
             }
-            RustcPassMode::Indirect {
+            PassMode::Indirect {
                 attrs: _,
                 extra_attrs: Some(_),
                 on_stack: _,
@@ -170,56 +171,21 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
     }
 }
 
-pub(super) fn get_arg_abi<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    layout: TyAndLayout<'tcx>,
-) -> ArgAbi<'tcx, Ty<'tcx>> {
-    let mut arg_abi = ArgAbi::new(&tcx, layout, |_, _, _| ArgAttributes::new());
-    if layout.is_zst() {
-        // WARNING zst arguments must never be passed, as that will break CastKind::ClosureFnPointer
-        arg_abi.mode = RustcPassMode::Ignore;
-    }
-    match arg_abi.mode {
-        RustcPassMode::Ignore => {}
-        RustcPassMode::Direct(_) => match &arg_abi.layout.abi {
-            Abi::Scalar(_) => {}
-            // FIXME implement Vector Abi in a cg_llvm compatible way
-            Abi::Vector { .. } => {
-                if crate::intrinsics::clif_vector_type(tcx, arg_abi.layout).is_none() {
-                    arg_abi.make_indirect();
-                }
-            }
-            _ => unreachable!("{:?}", arg_abi.layout.abi),
-        },
-        RustcPassMode::Pair(_, _) => match &arg_abi.layout.abi {
-            Abi::ScalarPair(a, b) => {
-                let a = scalar_to_clif_type(tcx, a.clone());
-                let b = scalar_to_clif_type(tcx, b.clone());
-                if a == types::I128 && b == types::I128 {
-                    arg_abi.make_indirect();
-                }
-            }
-            _ => unreachable!("{:?}", arg_abi.layout.abi),
-        },
-        _ => {}
-    }
-    arg_abi
-}
-
 /// Get a set of values to be passed as function arguments.
 pub(super) fn adjust_arg_for_abi<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
     arg: CValue<'tcx>,
+    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
 ) -> EmptySinglePair<Value> {
-    let arg_abi = get_arg_abi(fx.tcx, arg.layout());
+    assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty);
     match arg_abi.mode {
-        RustcPassMode::Ignore => Empty,
-        RustcPassMode::Direct(_) => Single(arg.load_scalar(fx)),
-        RustcPassMode::Pair(_, _) => {
+        PassMode::Ignore => Empty,
+        PassMode::Direct(_) => Single(arg.load_scalar(fx)),
+        PassMode::Pair(_, _) => {
             let (a, b) = arg.load_scalar_pair(fx);
             Pair(a, b)
         }
-        RustcPassMode::Cast(_) | RustcPassMode::Indirect { .. } => match arg.force_stack(fx) {
+        PassMode::Cast(_) | PassMode::Indirect { .. } => match arg.force_stack(fx) {
             (ptr, None) => Single(ptr.get_addr(fx)),
             (ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta),
         },
@@ -233,41 +199,52 @@ pub(super) fn cvalue_for_param<'tcx>(
     start_block: Block,
     #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option<mir::Local>,
     #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option<usize>,
-    arg_ty: Ty<'tcx>,
+    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
 ) -> Option<CValue<'tcx>> {
-    let layout = fx.layout_of(arg_ty);
-    let arg_abi = get_arg_abi(fx.tcx, layout);
-
     let clif_types = arg_abi.get_abi_param(fx.tcx);
     let block_params =
         clif_types.map(|abi_param| fx.bcx.append_block_param(start_block, abi_param.value_type));
 
     #[cfg(debug_assertions)]
-    crate::abi::comments::add_arg_comment(fx, "arg", local, local_field, block_params, &arg_abi);
+    crate::abi::comments::add_arg_comment(
+        fx,
+        "arg",
+        local,
+        local_field,
+        block_params,
+        arg_abi.mode,
+        arg_abi.layout,
+    );
 
     match arg_abi.mode {
-        RustcPassMode::Ignore => None,
-        RustcPassMode::Direct(_) => Some(CValue::by_val(block_params.assert_single(), layout)),
-        RustcPassMode::Pair(_, _) => {
+        PassMode::Ignore => None,
+        PassMode::Direct(_) => {
+            Some(CValue::by_val(block_params.assert_single(), arg_abi.layout))
+        }
+        PassMode::Pair(_, _) => {
             let (a, b) = block_params.assert_pair();
-            Some(CValue::by_val_pair(a, b, layout))
+            Some(CValue::by_val_pair(a, b, arg_abi.layout))
         }
-        RustcPassMode::Cast(_)
-        | RustcPassMode::Indirect {
+        PassMode::Cast(_)
+        | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
         } => Some(CValue::by_ref(
             Pointer::new(block_params.assert_single()),
-            layout,
+            arg_abi.layout,
         )),
-        RustcPassMode::Indirect {
+        PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
         } => {
             let (ptr, meta) = block_params.assert_pair();
-            Some(CValue::by_ref_unsized(Pointer::new(ptr), meta, layout))
+            Some(CValue::by_ref_unsized(
+                Pointer::new(ptr),
+                meta,
+                arg_abi.layout,
+            ))
         }
     }
 }
diff --git a/src/abi/returning.rs b/src/abi/returning.rs
index 3a5f61315f8..d7a82e0c377 100644
--- a/src/abi/returning.rs
+++ b/src/abi/returning.rs
@@ -3,21 +3,55 @@
 use crate::abi::pass_mode::*;
 use crate::prelude::*;
 
-use rustc_target::abi::call::PassMode as RustcPassMode;
-
-fn return_layout<'a, 'tcx>(fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> TyAndLayout<'tcx> {
-    fx.layout_of(fx.monomorphize(&fx.mir.local_decls[RETURN_PLACE].ty))
-}
+use rustc_middle::ty::layout::FnAbiExt;
+use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
 
 /// Can the given type be returned into an ssa var or does it need to be returned on the stack.
 pub(crate) fn can_return_to_ssa_var<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    dest_layout: TyAndLayout<'tcx>,
+    fx: &FunctionCx<'_, 'tcx, impl Module>,
+    func: &mir::Operand<'tcx>,
+    args: &[mir::Operand<'tcx>],
 ) -> bool {
-    match get_arg_abi(tcx, dest_layout).mode {
-        RustcPassMode::Ignore | RustcPassMode::Direct(_) | RustcPassMode::Pair(_, _) => true,
+    let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx));
+    let fn_sig = fx
+        .tcx
+        .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx));
+
+    // Handle special calls like instrinsics and empty drop glue.
+    let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() {
+        let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs)
+            .unwrap()
+            .unwrap()
+            .polymorphize(fx.tcx);
+
+        match instance.def {
+            InstanceDef::Intrinsic(_) | InstanceDef::DropGlue(_, _) => {
+                return true;
+            }
+            _ => Some(instance),
+        }
+    } else {
+        None
+    };
+
+    let extra_args = &args[fn_sig.inputs().len()..];
+    let extra_args = extra_args
+        .iter()
+        .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx)))
+        .collect::<Vec<_>>();
+    let fn_abi = if let Some(instance) = instance {
+        FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args)
+    } else {
+        FnAbi::of_fn_ptr(
+            &RevealAllLayoutCx(fx.tcx),
+            fn_ty.fn_sig(fx.tcx),
+            &extra_args,
+        )
+    };
+    match fn_abi.ret.mode {
+        PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => true,
         // FIXME Make it possible to return Cast and Indirect to an ssa var.
-        RustcPassMode::Cast(_) | RustcPassMode::Indirect { .. } => false,
+        PassMode::Cast(_) | PassMode::Indirect { .. } => false,
     }
 }
 
@@ -28,30 +62,39 @@ pub(super) fn codegen_return_param<'tcx>(
     ssa_analyzed: &rustc_index::vec::IndexVec<Local, crate::analyze::SsaKind>,
     start_block: Block,
 ) -> CPlace<'tcx> {
-    let ret_layout = return_layout(fx);
-    let ret_arg_abi = get_arg_abi(fx.tcx, ret_layout);
-    let (ret_place, ret_param) = match ret_arg_abi.mode {
-        RustcPassMode::Ignore => (CPlace::no_place(ret_layout), Empty),
-        RustcPassMode::Direct(_) | RustcPassMode::Pair(_, _) => {
+    let (ret_place, ret_param) = match fx.fn_abi.as_ref().unwrap().ret.mode {
+        PassMode::Ignore => (
+            CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout),
+            Empty,
+        ),
+        PassMode::Direct(_) | PassMode::Pair(_, _) => {
             let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa;
             (
-                super::make_local_place(fx, RETURN_PLACE, ret_layout, is_ssa),
+                super::make_local_place(
+                    fx,
+                    RETURN_PLACE,
+                    fx.fn_abi.as_ref().unwrap().ret.layout,
+                    is_ssa,
+                ),
                 Empty,
             )
         }
-        RustcPassMode::Cast(_)
-        | RustcPassMode::Indirect {
+        PassMode::Cast(_)
+        | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
         } => {
             let ret_param = fx.bcx.append_block_param(start_block, fx.pointer_type);
             (
-                CPlace::for_ptr(Pointer::new(ret_param), ret_layout),
+                CPlace::for_ptr(
+                    Pointer::new(ret_param),
+                    fx.fn_abi.as_ref().unwrap().ret.layout,
+                ),
                 Single(ret_param),
             )
         }
-        RustcPassMode::Indirect {
+        PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
@@ -68,7 +111,8 @@ pub(super) fn codegen_return_param<'tcx>(
         Some(RETURN_PLACE),
         None,
         ret_param,
-        &ret_arg_abi,
+        fx.fn_abi.as_ref().unwrap().ret.mode,
+        fx.fn_abi.as_ref().unwrap().ret.layout,
     );
 
     ret_place
@@ -78,17 +122,14 @@ pub(super) fn codegen_return_param<'tcx>(
 /// returns the call return value(s) if any are written to the correct place.
 pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>(
     fx: &mut FunctionCx<'_, 'tcx, M>,
-    fn_sig: FnSig<'tcx>,
+    ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
     ret_place: Option<CPlace<'tcx>>,
     f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option<Value>) -> (Inst, T),
 ) -> (Inst, T) {
-    let ret_layout = fx.layout_of(fn_sig.output());
-
-    let output_arg_abi = get_arg_abi(fx.tcx, ret_layout);
-    let return_ptr = match output_arg_abi.mode {
-        RustcPassMode::Ignore => None,
-        RustcPassMode::Cast(_)
-        | RustcPassMode::Indirect {
+    let return_ptr = match ret_arg_abi.mode {
+        PassMode::Ignore => None,
+        PassMode::Cast(_)
+        | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
@@ -96,38 +137,41 @@ pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>(
             Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)),
             None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot
         },
-        RustcPassMode::Indirect {
+        PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
         } => unreachable!("unsized return value"),
-        RustcPassMode::Direct(_) | RustcPassMode::Pair(_, _) => None,
+        PassMode::Direct(_) | PassMode::Pair(_, _) => None,
     };
 
     let (call_inst, meta) = f(fx, return_ptr);
 
-    match output_arg_abi.mode {
-        RustcPassMode::Ignore => {}
-        RustcPassMode::Direct(_) => {
+    match ret_arg_abi.mode {
+        PassMode::Ignore => {}
+        PassMode::Direct(_) => {
             if let Some(ret_place) = ret_place {
                 let ret_val = fx.bcx.inst_results(call_inst)[0];
-                ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_layout));
+                ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_arg_abi.layout));
             }
         }
-        RustcPassMode::Pair(_, _) => {
+        PassMode::Pair(_, _) => {
             if let Some(ret_place) = ret_place {
                 let ret_val_a = fx.bcx.inst_results(call_inst)[0];
                 let ret_val_b = fx.bcx.inst_results(call_inst)[1];
-                ret_place.write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_layout));
+                ret_place.write_cvalue(
+                    fx,
+                    CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout),
+                );
             }
         }
-        RustcPassMode::Cast(_)
-        | RustcPassMode::Indirect {
+        PassMode::Cast(_)
+        | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
         } => {}
-        RustcPassMode::Indirect {
+        PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
@@ -139,27 +183,27 @@ pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>(
 
 /// Codegen a return instruction with the right return value(s) if any.
 pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) {
-    match get_arg_abi(fx.tcx, return_layout(fx)).mode {
-        RustcPassMode::Ignore
-        | RustcPassMode::Cast(_)
-        | RustcPassMode::Indirect {
+    match fx.fn_abi.as_ref().unwrap().ret.mode {
+        PassMode::Ignore
+        | PassMode::Cast(_)
+        | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
         } => {
             fx.bcx.ins().return_(&[]);
         }
-        RustcPassMode::Indirect {
+        PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
         } => unreachable!("unsized return value"),
-        RustcPassMode::Direct(_) => {
+        PassMode::Direct(_) => {
             let place = fx.get_local_place(RETURN_PLACE);
             let ret_val = place.to_cvalue(fx).load_scalar(fx);
             fx.bcx.ins().return_(&[ret_val]);
         }
-        RustcPassMode::Pair(_, _) => {
+        PassMode::Pair(_, _) => {
             let place = fx.get_local_place(RETURN_PLACE);
             let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx);
             fx.bcx.ins().return_(&[ret_val_a, ret_val_b]);
diff --git a/src/analyze.rs b/src/analyze.rs
index adf5c7ac4fe..dc5e8a7e304 100644
--- a/src/analyze.rs
+++ b/src/analyze.rs
@@ -40,11 +40,9 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec<Local, S
         }
 
         match &bb.terminator().kind {
-            TerminatorKind::Call { destination, .. } => {
+            TerminatorKind::Call { destination, func, args, .. } => {
                 if let Some((dest_place, _dest_bb)) = destination {
-                    let dest_layout = fx
-                        .layout_of(fx.monomorphize(&dest_place.ty(&fx.mir.local_decls, fx.tcx).ty));
-                    if !crate::abi::can_return_to_ssa_var(fx.tcx, dest_layout) {
+                    if !crate::abi::can_return_to_ssa_var(fx, func, args) {
                         not_ssa(&mut flag_map, dest_place.local)
                     }
                 }
diff --git a/src/base.rs b/src/base.rs
index 1fafc121597..1eff0d4f516 100644
--- a/src/base.rs
+++ b/src/base.rs
@@ -2,6 +2,8 @@
 
 use rustc_index::vec::IndexVec;
 use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::layout::FnAbiExt;
+use rustc_target::abi::call::FnAbi;
 
 use crate::prelude::*;
 
@@ -51,6 +53,7 @@ pub(crate) fn codegen_fn<'tcx>(
 
         instance,
         mir,
+        fn_abi: Some(FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[])),
 
         bcx,
         block_map,
diff --git a/src/common.rs b/src/common.rs
index 1485d4451b8..fbee84e09f7 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -1,4 +1,5 @@
 use rustc_index::vec::IndexVec;
+use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::{Integer, Primitive};
 use rustc_target::spec::{HasTargetSpec, Target};
 
@@ -294,6 +295,7 @@ pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> {
 
     pub(crate) instance: Instance<'tcx>,
     pub(crate) mir: &'tcx Body<'tcx>,
+    pub(crate) fn_abi: Option<FnAbi<'tcx, Ty<'tcx>>>,
 
     pub(crate) bcx: FunctionBuilder<'clif>,
     pub(crate) block_map: IndexVec<BasicBlock, Block>,
@@ -319,16 +321,7 @@ impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> {
     type TyAndLayout = TyAndLayout<'tcx>;
 
     fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> {
-        assert!(!ty.still_further_specializable());
-        self.tcx
-            .layout_of(ParamEnv::reveal_all().and(&ty))
-            .unwrap_or_else(|e| {
-                if let layout::LayoutError::SizeOverflow(_) = e {
-                    self.tcx.sess.fatal(&e.to_string())
-                } else {
-                    bug!("failed to get layout for `{}`: {}", ty, e)
-                }
-            })
+        RevealAllLayoutCx(self.tcx).layout_of(ty)
     }
 }
 
@@ -442,3 +435,47 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> {
         self.bcx.ins().global_value(self.pointer_type, local_msg_id)
     }
 }
+
+pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>);
+
+impl<'tcx> LayoutOf for RevealAllLayoutCx<'tcx> {
+    type Ty = Ty<'tcx>;
+    type TyAndLayout = TyAndLayout<'tcx>;
+
+    fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> {
+        assert!(!ty.still_further_specializable());
+        self.0
+            .layout_of(ParamEnv::reveal_all().and(&ty))
+            .unwrap_or_else(|e| {
+                if let layout::LayoutError::SizeOverflow(_) = e {
+                    self.0.sess.fatal(&e.to_string())
+                } else {
+                    bug!("failed to get layout for `{}`: {}", ty, e)
+                }
+            })
+    }
+}
+
+impl<'tcx> layout::HasTyCtxt<'tcx> for RevealAllLayoutCx<'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.0
+    }
+}
+
+impl<'tcx> rustc_target::abi::HasDataLayout for RevealAllLayoutCx<'tcx> {
+    fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout {
+        &self.0.data_layout
+    }
+}
+
+impl<'tcx> layout::HasParamEnv<'tcx> for RevealAllLayoutCx<'tcx> {
+    fn param_env(&self) -> ParamEnv<'tcx> {
+        ParamEnv::reveal_all()
+    }
+}
+
+impl<'tcx> HasTargetSpec for RevealAllLayoutCx<'tcx> {
+    fn target_spec(&self) -> &Target {
+        &self.0.sess.target
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 9b5b7d8051c..ed7ee3b5365 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -90,7 +90,7 @@ mod prelude {
     pub(crate) use rustc_middle::mir::{self, *};
     pub(crate) use rustc_middle::ty::layout::{self, TyAndLayout};
     pub(crate) use rustc_middle::ty::{
-        self, FnSig, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeAndMut, TypeFoldable,
+        self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeAndMut, TypeFoldable,
     };
     pub(crate) use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx};
 
diff --git a/src/value_and_place.rs b/src/value_and_place.rs
index 5bcb11fd515..17cb09d5587 100644
--- a/src/value_and_place.rs
+++ b/src/value_and_place.rs
@@ -450,64 +450,6 @@ impl<'tcx> CPlace<'tcx> {
         fx: &mut FunctionCx<'_, 'tcx, impl Module>,
         from: CValue<'tcx>,
     ) {
-        fn assert_assignable<'tcx>(
-            fx: &FunctionCx<'_, 'tcx, impl Module>,
-            from_ty: Ty<'tcx>,
-            to_ty: Ty<'tcx>,
-        ) {
-            match (from_ty.kind(), to_ty.kind()) {
-                (ty::Ref(_, a, _), ty::Ref(_, b, _))
-                | (
-                    ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }),
-                    ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }),
-                ) => {
-                    assert_assignable(fx, a, b);
-                }
-                (ty::FnPtr(_), ty::FnPtr(_)) => {
-                    let from_sig = fx.tcx.normalize_erasing_late_bound_regions(
-                        ParamEnv::reveal_all(),
-                        from_ty.fn_sig(fx.tcx),
-                    );
-                    let to_sig = fx.tcx.normalize_erasing_late_bound_regions(
-                        ParamEnv::reveal_all(),
-                        to_ty.fn_sig(fx.tcx),
-                    );
-                    assert_eq!(
-                        from_sig, to_sig,
-                        "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
-                        from_sig, to_sig, fx,
-                    );
-                    // fn(&T) -> for<'l> fn(&'l T) is allowed
-                }
-                (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => {
-                    for (from, to) in from_traits.iter().zip(to_traits) {
-                        let from = fx
-                            .tcx
-                            .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from);
-                        let to = fx
-                            .tcx
-                            .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to);
-                        assert_eq!(
-                            from, to,
-                            "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}",
-                            from_traits, to_traits, fx,
-                        );
-                    }
-                    // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed
-                }
-                _ => {
-                    assert_eq!(
-                        from_ty,
-                        to_ty,
-                        "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}",
-                        from_ty,
-                        to_ty,
-                        fx,
-                    );
-                }
-            }
-        }
-
         assert_assignable(fx, from.layout().ty, self.layout().ty);
 
         self.write_cvalue_maybe_transmute(fx, from, "write_cvalue");
@@ -794,3 +736,62 @@ impl<'tcx> CPlace<'tcx> {
         }
     }
 }
+
+#[track_caller]
+pub(crate) fn assert_assignable<'tcx>(
+    fx: &FunctionCx<'_, 'tcx, impl Module>,
+    from_ty: Ty<'tcx>,
+    to_ty: Ty<'tcx>,
+) {
+    match (from_ty.kind(), to_ty.kind()) {
+        (ty::Ref(_, a, _), ty::Ref(_, b, _))
+        | (
+            ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }),
+            ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }),
+        ) => {
+            assert_assignable(fx, a, b);
+        }
+        (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }))
+        | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => {
+            assert_assignable(fx, a, b);
+        }
+        (ty::FnPtr(_), ty::FnPtr(_)) => {
+            let from_sig = fx.tcx.normalize_erasing_late_bound_regions(
+                ParamEnv::reveal_all(),
+                from_ty.fn_sig(fx.tcx),
+            );
+            let to_sig = fx
+                .tcx
+                .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_ty.fn_sig(fx.tcx));
+            assert_eq!(
+                from_sig, to_sig,
+                "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
+                from_sig, to_sig, fx,
+            );
+            // fn(&T) -> for<'l> fn(&'l T) is allowed
+        }
+        (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => {
+            for (from, to) in from_traits.iter().zip(to_traits) {
+                let from = fx
+                    .tcx
+                    .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from);
+                let to = fx
+                    .tcx
+                    .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to);
+                assert_eq!(
+                    from, to,
+                    "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}",
+                    from_traits, to_traits, fx,
+                );
+            }
+            // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed
+        }
+        _ => {
+            assert_eq!(
+                from_ty, to_ty,
+                "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}",
+                from_ty, to_ty, fx,
+            );
+        }
+    }
+}