about summary refs log tree commit diff
path: root/tests/ui/abi
diff options
context:
space:
mode:
authorAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-05 09:13:28 +0100
committerAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-11 09:32:08 +0000
commitcf2dff2b1e3fa55fa5415d524200070d0d7aacfe (patch)
tree40a88d9a46aaf3e8870676eb2538378b75a263eb /tests/ui/abi
parentca855e6e42787ecd062d81d53336fe6788ef51a9 (diff)
downloadrust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.tar.gz
rust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.zip
Move /src/test to /tests
Diffstat (limited to 'tests/ui/abi')
-rw-r--r--tests/ui/abi/abi-sysv64-arg-passing.rs448
-rw-r--r--tests/ui/abi/abi-sysv64-register-usage.rs107
-rw-r--r--tests/ui/abi/abi-typo-unstable.rs6
-rw-r--r--tests/ui/abi/abi-typo-unstable.stderr11
-rw-r--r--tests/ui/abi/anon-extern-mod.rs18
-rw-r--r--tests/ui/abi/c-stack-as-value.rs18
-rw-r--r--tests/ui/abi/c-stack-returning-int64.rs36
-rw-r--r--tests/ui/abi/cabi-int-widening.rs15
-rw-r--r--tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs14
-rw-r--r--tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs9
-rw-r--r--tests/ui/abi/cross-crate/duplicated-external-mods.rs9
-rw-r--r--tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs28
-rw-r--r--tests/ui/abi/extern/extern-call-deep.rs36
-rw-r--r--tests/ui/abi/extern/extern-call-deep2.rs41
-rw-r--r--tests/ui/abi/extern/extern-call-direct.rs10
-rw-r--r--tests/ui/abi/extern/extern-call-indirect.rs35
-rw-r--r--tests/ui/abi/extern/extern-call-scrub.rs45
-rw-r--r--tests/ui/abi/extern/extern-crosscrate.rs21
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU16s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU32s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU64s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-TwoU8s.rs26
-rw-r--r--tests/ui/abi/extern/extern-pass-char.rs15
-rw-r--r--tests/ui/abi/extern/extern-pass-double.rs13
-rw-r--r--tests/ui/abi/extern/extern-pass-empty.rs55
-rw-r--r--tests/ui/abi/extern/extern-pass-u32.rs15
-rw-r--r--tests/ui/abi/extern/extern-pass-u64.rs15
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU16s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU32s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU64s.rs22
-rw-r--r--tests/ui/abi/extern/extern-return-TwoU8s.rs22
-rw-r--r--tests/ui/abi/foreign/auxiliary/foreign_lib.rs37
-rw-r--r--tests/ui/abi/foreign/foreign-call-no-runtime.rs60
-rw-r--r--tests/ui/abi/foreign/foreign-dupe.rs17
-rw-r--r--tests/ui/abi/foreign/foreign-fn-with-byval.rs30
-rw-r--r--tests/ui/abi/foreign/foreign-no-abi.rs22
-rw-r--r--tests/ui/abi/foreign/invoke-external-foreign.rs17
-rw-r--r--tests/ui/abi/homogenous-floats-target-feature-mixup.rs192
-rw-r--r--tests/ui/abi/issue-28676.rs40
-rw-r--r--tests/ui/abi/issues/issue-22565-rust-call.rs31
-rw-r--r--tests/ui/abi/issues/issue-22565-rust-call.stderr33
-rw-r--r--tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs46
-rw-r--r--tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs39
-rw-r--r--tests/ui/abi/lib-defaults.rs17
-rw-r--r--tests/ui/abi/mir/mir_codegen_calls_variadic.rs19
-rw-r--r--tests/ui/abi/nullable-pointer-ffi-compat.rs28
-rw-r--r--tests/ui/abi/numbers-arithmetic/i128-ffi.rs31
-rw-r--r--tests/ui/abi/rustcall-generic.rs12
-rw-r--r--tests/ui/abi/segfault-no-out-of-stack.rs48
-rw-r--r--tests/ui/abi/stack-probes-lto.rs17
-rw-r--r--tests/ui/abi/stack-probes.rs85
-rw-r--r--tests/ui/abi/stack-protector.rs99
-rw-r--r--tests/ui/abi/statics/static-mut-foreign.rs41
-rw-r--r--tests/ui/abi/struct-enums/struct-return.rs122
-rw-r--r--tests/ui/abi/union/union-c-interop.rs37
-rw-r--r--tests/ui/abi/unsupported.aarch64.stderr61
-rw-r--r--tests/ui/abi/unsupported.arm.stderr55
-rw-r--r--tests/ui/abi/unsupported.i686.stderr39
-rw-r--r--tests/ui/abi/unsupported.rs53
-rw-r--r--tests/ui/abi/unsupported.x64.stderr55
-rw-r--r--tests/ui/abi/variadic-ffi.rs84
-rw-r--r--tests/ui/abi/x86stdcall.rs22
-rw-r--r--tests/ui/abi/x86stdcall2.rs27
63 files changed, 2728 insertions, 0 deletions
diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs
new file mode 100644
index 00000000000..c87353b93a7
--- /dev/null
+++ b/tests/ui/abi/abi-sysv64-arg-passing.rs
@@ -0,0 +1,448 @@
+// run-pass
+// Checks if the "sysv64" calling convention behaves the same as the
+// "C" calling convention on platforms where both should be the same
+
+// This file contains versions of the following run-pass tests with
+// the calling convention changed to "sysv64"
+
+// cabi-int-widening
+// extern-pass-char
+// extern-pass-u32
+// extern-pass-u64
+// extern-pass-double
+// extern-pass-empty
+// extern-pass-TwoU8s
+// extern-pass-TwoU16s
+// extern-pass-TwoU32s
+// extern-pass-TwoU64s
+// extern-return-TwoU8s
+// extern-return-TwoU16s
+// extern-return-TwoU32s
+// extern-return-TwoU64s
+// foreign-fn-with-byval
+// issue-28676
+// issue-62350-sysv-neg-reg-counts
+// struct-return
+
+// ignore-android
+// ignore-arm
+// ignore-aarch64
+// ignore-windows
+
+// note: windows is ignored as rust_test_helpers does not have the sysv64 abi on windows
+
+#[allow(dead_code)]
+#[allow(improper_ctypes)]
+
+#[cfg(target_arch = "x86_64")]
+mod tests {
+    #[repr(C)]
+    #[derive(Copy, Clone, PartialEq, Debug)]
+    pub struct TwoU8s {
+        one: u8, two: u8
+    }
+
+    #[repr(C)]
+    #[derive(Copy, Clone, PartialEq, Debug)]
+    pub struct TwoU16s {
+        one: u16, two: u16
+    }
+
+    #[repr(C)]
+    #[derive(Copy, Clone, PartialEq, Debug)]
+    pub struct TwoU32s {
+        one: u32, two: u32
+    }
+
+    #[repr(C)]
+    #[derive(Copy, Clone, PartialEq, Debug)]
+    pub struct TwoU64s {
+        one: u64, two: u64
+    }
+
+    #[repr(C)]
+    pub struct ManyInts {
+        arg1: i8,
+        arg2: i16,
+        arg3: i32,
+        arg4: i16,
+        arg5: i8,
+        arg6: TwoU8s,
+    }
+
+    #[repr(C)]
+    pub struct Empty;
+
+    #[repr(C)]
+    #[derive(Copy, Clone)]
+    pub struct S {
+        x: u64,
+        y: u64,
+        z: u64,
+    }
+
+    #[repr(C)]
+    #[derive(Copy, Clone)]
+    pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
+
+    #[derive(Copy, Clone)]
+    pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
+
+    #[repr(C)]
+    #[derive(Copy, Clone)]
+    pub struct Floats { a: f64, b: u8, c: f64 }
+
+    #[repr(C, u8)]
+    pub enum U8TaggedEnumOptionU64U64 {
+        None,
+        Some(u64,u64),
+    }
+
+    #[repr(C, u8)]
+    pub enum U8TaggedEnumOptionU64 {
+        None,
+        Some(u64),
+    }
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "sysv64" {
+        pub fn rust_int8_to_int32(_: i8) -> i32;
+        pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
+        pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
+        pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
+        pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
+        pub fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts);
+        pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s;
+        pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
+        pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s;
+        pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
+        pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s;
+        pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s;
+        pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
+        pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s;
+        pub fn get_x(x: S) -> u64;
+        pub fn get_y(x: S) -> u64;
+        pub fn get_z(x: S) -> u64;
+        pub fn get_c_many_params(_: *const (), _: *const (),
+                                 _: *const (), _: *const (), f: Quad) -> u64;
+        pub fn get_c_exhaust_sysv64_ints(
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            h: QuadFloats,
+        ) -> f32;
+        pub fn rust_dbg_abi_1(q: Quad) -> Quad;
+        pub fn rust_dbg_abi_2(f: Floats) -> Floats;
+        pub fn rust_dbg_new_some_u64u64(a: u64, b: u64) -> U8TaggedEnumOptionU64U64;
+        pub fn rust_dbg_new_none_u64u64() -> U8TaggedEnumOptionU64U64;
+        pub fn rust_dbg_unpack_option_u64u64(
+            o: U8TaggedEnumOptionU64U64,
+            a: *mut u64,
+            b: *mut u64,
+        ) -> i32;
+        pub fn rust_dbg_new_some_u64(some: u64) -> U8TaggedEnumOptionU64;
+        pub fn rust_dbg_new_none_u64() -> U8TaggedEnumOptionU64;
+        pub fn rust_dbg_unpack_option_u64(o: U8TaggedEnumOptionU64, v: *mut u64) -> i32;
+    }
+
+    pub fn cabi_int_widening() {
+        let x = unsafe {
+            rust_int8_to_int32(-1)
+        };
+
+        assert!(x == -1);
+    }
+
+    pub fn extern_pass_char() {
+        unsafe {
+            assert_eq!(22, rust_dbg_extern_identity_u8(22));
+        }
+    }
+
+    pub fn extern_pass_u32() {
+        unsafe {
+            assert_eq!(22, rust_dbg_extern_identity_u32(22));
+        }
+    }
+
+    pub fn extern_pass_u64() {
+        unsafe {
+            assert_eq!(22, rust_dbg_extern_identity_u64(22));
+        }
+    }
+
+    pub fn extern_pass_double() {
+        unsafe {
+            assert_eq!(22.0_f64, rust_dbg_extern_identity_double(22.0_f64));
+        }
+    }
+
+    pub fn extern_pass_empty() {
+        unsafe {
+            let x = ManyInts {
+                arg1: 2,
+                arg2: 3,
+                arg3: 4,
+                arg4: 5,
+                arg5: 6,
+                arg6: TwoU8s { one: 7, two: 8, }
+            };
+            let y = ManyInts {
+                arg1: 1,
+                arg2: 2,
+                arg3: 3,
+                arg4: 4,
+                arg5: 5,
+                arg6: TwoU8s { one: 6, two: 7, }
+            };
+            let empty = Empty;
+            rust_dbg_extern_empty_struct(x, empty, y);
+        }
+    }
+
+    pub fn extern_pass_twou8s() {
+        unsafe {
+            let x = TwoU8s {one: 22, two: 23};
+            let y = rust_dbg_extern_identity_TwoU8s(x);
+            assert_eq!(x, y);
+        }
+    }
+
+    pub fn extern_pass_twou16s() {
+        unsafe {
+            let x = TwoU16s {one: 22, two: 23};
+            let y = rust_dbg_extern_identity_TwoU16s(x);
+            assert_eq!(x, y);
+        }
+    }
+
+    pub fn extern_pass_twou32s() {
+        unsafe {
+            let x = TwoU32s {one: 22, two: 23};
+            let y = rust_dbg_extern_identity_TwoU32s(x);
+            assert_eq!(x, y);
+        }
+    }
+
+    pub fn extern_pass_twou64s() {
+        unsafe {
+            let x = TwoU64s {one: 22, two: 23};
+            let y = rust_dbg_extern_identity_TwoU64s(x);
+            assert_eq!(x, y);
+        }
+    }
+
+    pub fn extern_return_twou8s() {
+        unsafe {
+            let y = rust_dbg_extern_return_TwoU8s();
+            assert_eq!(y.one, 10);
+            assert_eq!(y.two, 20);
+        }
+    }
+
+    pub fn extern_return_twou16s() {
+        unsafe {
+            let y = rust_dbg_extern_return_TwoU16s();
+            assert_eq!(y.one, 10);
+            assert_eq!(y.two, 20);
+        }
+    }
+
+    pub fn extern_return_twou32s() {
+        unsafe {
+            let y = rust_dbg_extern_return_TwoU32s();
+            assert_eq!(y.one, 10);
+            assert_eq!(y.two, 20);
+        }
+    }
+
+    pub fn extern_return_twou64s() {
+        unsafe {
+            let y = rust_dbg_extern_return_TwoU64s();
+            assert_eq!(y.one, 10);
+            assert_eq!(y.two, 20);
+        }
+    }
+
+    #[inline(never)]
+    fn indirect_call(func: unsafe extern "sysv64" fn(s: S) -> u64, s: S) -> u64 {
+        unsafe {
+            func(s)
+        }
+    }
+
+    pub fn foreign_fn_with_byval() {
+        let s = S { x: 1, y: 2, z: 3 };
+        assert_eq!(s.x, indirect_call(get_x, s));
+        assert_eq!(s.y, indirect_call(get_y, s));
+        assert_eq!(s.z, indirect_call(get_z, s));
+    }
+
+    fn test() {
+        use std::ptr;
+        unsafe {
+            let null = ptr::null();
+            let q = Quad {
+                a: 1,
+                b: 2,
+                c: 3,
+                d: 4
+            };
+            assert_eq!(get_c_many_params(null, null, null, null, q), q.c);
+        }
+    }
+
+    pub fn issue_28676() {
+        test();
+    }
+
+    fn test_62350() {
+        use std::ptr;
+        unsafe {
+            let null = ptr::null();
+            let q = QuadFloats {
+                a: 10.2,
+                b: 20.3,
+                c: 30.4,
+                d: 40.5
+            };
+            assert_eq!(
+                get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+                q.c,
+            );
+        }
+    }
+
+    pub fn issue_62350() {
+        test_62350();
+    }
+
+    fn test1() {
+        unsafe {
+            let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
+                     b: 0xbbbb_bbbb_bbbb_bbbb,
+                     c: 0xcccc_cccc_cccc_cccc,
+                     d: 0xdddd_dddd_dddd_dddd };
+            let qq = rust_dbg_abi_1(q);
+            println!("a: {:x}", qq.a as usize);
+            println!("b: {:x}", qq.b as usize);
+            println!("c: {:x}", qq.c as usize);
+            println!("d: {:x}", qq.d as usize);
+            assert_eq!(qq.a, q.c + 1);
+            assert_eq!(qq.b, q.d - 1);
+            assert_eq!(qq.c, q.a + 1);
+            assert_eq!(qq.d, q.b - 1);
+        }
+    }
+
+    fn test2() {
+        unsafe {
+            let f = Floats { a: 1.234567890e-15_f64,
+                     b: 0b_1010_1010,
+                     c: 1.0987654321e-15_f64 };
+            let ff = rust_dbg_abi_2(f);
+            println!("a: {}", ff.a as f64);
+            println!("b: {}", ff.b as usize);
+            println!("c: {}", ff.c as f64);
+            assert_eq!(ff.a, f.c + 1.0f64);
+            assert_eq!(ff.b, 0xff);
+            assert_eq!(ff.c, f.a - 1.0f64);
+        }
+    }
+
+    pub fn struct_return() {
+        test1();
+        test2();
+    }
+
+    pub fn enum_passing_and_return_pair() {
+        let some_u64u64 = unsafe { rust_dbg_new_some_u64u64(10, 20) };
+        if let U8TaggedEnumOptionU64U64::Some(a, b) = some_u64u64 {
+            assert_eq!(10, a);
+            assert_eq!(20, b);
+        } else {
+            panic!("unexpected none");
+        }
+
+        let none_u64u64 = unsafe { rust_dbg_new_none_u64u64() };
+        if let U8TaggedEnumOptionU64U64::Some(_,_) = none_u64u64 {
+            panic!("unexpected some");
+        }
+
+        let mut a: u64 = 0;
+        let mut b: u64 = 0;
+        let r = unsafe {
+            rust_dbg_unpack_option_u64u64(some_u64u64, &mut a as *mut _, &mut b as *mut _)
+        };
+        assert_eq!(1, r);
+        assert_eq!(10, a);
+        assert_eq!(20, b);
+
+        let mut a: u64 = 0;
+        let mut b: u64 = 0;
+        let r = unsafe {
+            rust_dbg_unpack_option_u64u64(none_u64u64, &mut a as *mut _, &mut b as *mut _)
+        };
+        assert_eq!(0, r);
+        assert_eq!(0, a);
+        assert_eq!(0, b);
+    }
+
+    pub fn enum_passing_and_return() {
+        let some_u64 = unsafe { rust_dbg_new_some_u64(10) };
+        if let U8TaggedEnumOptionU64::Some(v) = some_u64 {
+            assert_eq!(10, v);
+        } else {
+            panic!("unexpected none");
+        }
+
+        let none_u64 = unsafe { rust_dbg_new_none_u64() };
+        if let U8TaggedEnumOptionU64::Some(_) = none_u64 {
+            panic!("unexpected some");
+        }
+
+        let mut target: u64 = 0;
+        let r = unsafe { rust_dbg_unpack_option_u64(some_u64, &mut target as *mut _) };
+        assert_eq!(1, r);
+        assert_eq!(10, target);
+
+        let mut target: u64 = 0;
+        let r = unsafe { rust_dbg_unpack_option_u64(none_u64, &mut target as *mut _) };
+        assert_eq!(0, r);
+        assert_eq!(0, target);
+    }
+}
+
+#[cfg(target_arch = "x86_64")]
+fn main() {
+    use tests::*;
+    cabi_int_widening();
+    extern_pass_char();
+    extern_pass_u32();
+    extern_pass_u64();
+    extern_pass_double();
+    extern_pass_empty();
+    extern_pass_twou8s();
+    extern_pass_twou16s();
+    extern_pass_twou32s();
+    extern_pass_twou64s();
+    extern_return_twou8s();
+    extern_return_twou16s();
+    extern_return_twou32s();
+    extern_return_twou64s();
+    foreign_fn_with_byval();
+    issue_28676();
+    issue_62350();
+    struct_return();
+    enum_passing_and_return_pair();
+    enum_passing_and_return();
+}
+
+#[cfg(not(target_arch = "x86_64"))]
+fn main() {
+
+}
diff --git a/tests/ui/abi/abi-sysv64-register-usage.rs b/tests/ui/abi/abi-sysv64-register-usage.rs
new file mode 100644
index 00000000000..39330693677
--- /dev/null
+++ b/tests/ui/abi/abi-sysv64-register-usage.rs
@@ -0,0 +1,107 @@
+// run-pass
+// Checks if the correct registers are being used to pass arguments
+// when the sysv64 ABI is specified.
+
+// ignore-android
+// ignore-arm
+// ignore-aarch64
+// needs-asm-support
+
+#[cfg(target_arch = "x86_64")]
+pub extern "sysv64" fn all_the_registers(
+    rdi: i64,
+    rsi: i64,
+    rdx: i64,
+    rcx: i64,
+    r8: i64,
+    r9: i64,
+    xmm0: f32,
+    xmm1: f32,
+    xmm2: f32,
+    xmm3: f32,
+    xmm4: f32,
+    xmm5: f32,
+    xmm6: f32,
+    xmm7: f32,
+) -> i64 {
+    assert_eq!(rdi, 1);
+    assert_eq!(rsi, 2);
+    assert_eq!(rdx, 3);
+    assert_eq!(rcx, 4);
+    assert_eq!(r8, 5);
+    assert_eq!(r9, 6);
+    assert_eq!(xmm0, 1.0f32);
+    assert_eq!(xmm1, 2.0f32);
+    assert_eq!(xmm2, 4.0f32);
+    assert_eq!(xmm3, 8.0f32);
+    assert_eq!(xmm4, 16.0f32);
+    assert_eq!(xmm5, 32.0f32);
+    assert_eq!(xmm6, 64.0f32);
+    assert_eq!(xmm7, 128.0f32);
+    42
+}
+
+// this struct contains 8 i64's, while only 6 can be passed in registers.
+#[cfg(target_arch = "x86_64")]
+#[derive(PartialEq, Eq, Debug)]
+pub struct LargeStruct(i64, i64, i64, i64, i64, i64, i64, i64);
+
+#[cfg(target_arch = "x86_64")]
+#[inline(never)]
+#[allow(improper_ctypes_definitions)]
+pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct {
+    foo.0 *= 1;
+    foo.1 *= 2;
+    foo.2 *= 3;
+    foo.3 *= 4;
+    foo.4 *= 5;
+    foo.5 *= 6;
+    foo.6 *= 7;
+    foo.7 *= 8;
+    foo
+}
+
+#[cfg(target_arch = "x86_64")]
+pub fn main() {
+    use std::arch::asm;
+
+    let result: i64;
+    unsafe {
+        asm!("mov rdi, 1",
+             "mov rsi, 2",
+             "mov rdx, 3",
+             "mov rcx, 4",
+             "mov r8,  5",
+             "mov r9,  6",
+             "mov eax, 0x3F800000",
+             "movd xmm0, eax",
+             "mov eax, 0x40000000",
+             "movd xmm1, eax",
+             "mov eax, 0x40800000",
+             "movd xmm2, eax",
+             "mov eax, 0x41000000",
+             "movd xmm3, eax",
+             "mov eax, 0x41800000",
+             "movd xmm4, eax",
+             "mov eax, 0x42000000",
+             "movd xmm5, eax",
+             "mov eax, 0x42800000",
+             "movd xmm6, eax",
+             "mov eax, 0x43000000",
+             "movd xmm7, eax",
+             "call {0}",
+             sym all_the_registers,
+             out("rax") result,
+             clobber_abi("sysv64"),
+        );
+    }
+    assert_eq!(result, 42);
+
+    assert_eq!(
+        large_struct_by_val(LargeStruct(1, 2, 3, 4, 5, 6, 7, 8)),
+        LargeStruct(1, 4, 9, 16, 25, 36, 49, 64)
+    );
+}
+
+#[cfg(not(target_arch = "x86_64"))]
+pub fn main() {}
diff --git a/tests/ui/abi/abi-typo-unstable.rs b/tests/ui/abi/abi-typo-unstable.rs
new file mode 100644
index 00000000000..94991a5eb17
--- /dev/null
+++ b/tests/ui/abi/abi-typo-unstable.rs
@@ -0,0 +1,6 @@
+// rust-intrinsic is unstable and not enabled, so it should not be suggested as a fix
+extern "rust-intrinsec" fn rust_intrinsic() {} //~ ERROR invalid ABI
+
+fn main() {
+    rust_intrinsic();
+}
diff --git a/tests/ui/abi/abi-typo-unstable.stderr b/tests/ui/abi/abi-typo-unstable.stderr
new file mode 100644
index 00000000000..3b346e00227
--- /dev/null
+++ b/tests/ui/abi/abi-typo-unstable.stderr
@@ -0,0 +1,11 @@
+error[E0703]: invalid ABI: found `rust-intrinsec`
+  --> $DIR/abi-typo-unstable.rs:2:8
+   |
+LL | extern "rust-intrinsec" fn rust_intrinsic() {}
+   |        ^^^^^^^^^^^^^^^^ invalid ABI
+   |
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0703`.
diff --git a/tests/ui/abi/anon-extern-mod.rs b/tests/ui/abi/anon-extern-mod.rs
new file mode 100644
index 00000000000..6c7d60d4cb0
--- /dev/null
+++ b/tests/ui/abi/anon-extern-mod.rs
@@ -0,0 +1,18 @@
+// run-pass
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_get_test_int() -> libc::intptr_t;
+}
+
+pub fn main() {
+    unsafe {
+        let _ = rust_get_test_int();
+    }
+}
diff --git a/tests/ui/abi/c-stack-as-value.rs b/tests/ui/abi/c-stack-as-value.rs
new file mode 100644
index 00000000000..5bece0ba2d1
--- /dev/null
+++ b/tests/ui/abi/c-stack-as-value.rs
@@ -0,0 +1,18 @@
+// run-pass
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_get_test_int() -> libc::intptr_t;
+    }
+}
+
+pub fn main() {
+    let _foo = rustrt::rust_get_test_int;
+}
diff --git a/tests/ui/abi/c-stack-returning-int64.rs b/tests/ui/abi/c-stack-returning-int64.rs
new file mode 100644
index 00000000000..fb3cb2083e4
--- /dev/null
+++ b/tests/ui/abi/c-stack-returning-int64.rs
@@ -0,0 +1,36 @@
+// run-pass
+// ignore-wasm32-bare no libc to test with
+// ignore-sgx no libc
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::ffi::CString;
+
+mod mlibc {
+    use libc::{c_char, c_long, c_longlong};
+
+    extern "C" {
+        pub fn atol(x: *const c_char) -> c_long;
+        pub fn atoll(x: *const c_char) -> c_longlong;
+    }
+}
+
+fn atol(s: String) -> isize {
+    let c = CString::new(s).unwrap();
+    unsafe { mlibc::atol(c.as_ptr()) as isize }
+}
+
+fn atoll(s: String) -> i64 {
+    let c = CString::new(s).unwrap();
+    unsafe { mlibc::atoll(c.as_ptr()) as i64 }
+}
+
+pub fn main() {
+    assert_eq!(atol("1024".to_string()) * 10, atol("10240".to_string()));
+    assert_eq!(
+        (atoll("11111111111111111".to_string()) * 10),
+        atoll("111111111111111110".to_string())
+    );
+}
diff --git a/tests/ui/abi/cabi-int-widening.rs b/tests/ui/abi/cabi-int-widening.rs
new file mode 100644
index 00000000000..1dbab275225
--- /dev/null
+++ b/tests/ui/abi/cabi-int-widening.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_int8_to_int32(_: i8) -> i32;
+}
+
+fn main() {
+    let x = unsafe {
+        rust_int8_to_int32(-1)
+    };
+
+    assert!(x == -1);
+}
diff --git a/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs b/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs
new file mode 100644
index 00000000000..77168be5374
--- /dev/null
+++ b/tests/ui/abi/cross-crate/anon-extern-mod-cross-crate-2.rs
@@ -0,0 +1,14 @@
+// run-pass
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+extern crate anonexternmod;
+
+use anonexternmod::rust_get_test_int;
+
+pub fn main() {
+    unsafe {
+        rust_get_test_int();
+    }
+}
diff --git a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs
new file mode 100644
index 00000000000..5cbf8093c5c
--- /dev/null
+++ b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs
@@ -0,0 +1,9 @@
+#![crate_name = "anonexternmod"]
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_get_test_int() -> libc::intptr_t;
+}
diff --git a/tests/ui/abi/cross-crate/duplicated-external-mods.rs b/tests/ui/abi/cross-crate/duplicated-external-mods.rs
new file mode 100644
index 00000000000..05a279a3014
--- /dev/null
+++ b/tests/ui/abi/cross-crate/duplicated-external-mods.rs
@@ -0,0 +1,9 @@
+// run-pass
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// aux-build:anon-extern-mod-cross-crate-1.rs
+// pretty-expanded FIXME #23616
+// ignore-wasm32-bare no libc to test ffi with
+
+extern crate anonexternmod;
+
+pub fn main() { }
diff --git a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
new file mode 100644
index 00000000000..9c61518b941
--- /dev/null
+++ b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs
@@ -0,0 +1,28 @@
+#![crate_name = "externcallback"]
+#![crate_type = "lib"]
+#![feature(rustc_private)]
+
+extern crate libc;
+
+pub mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_call(
+            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+            data: libc::uintptr_t,
+        ) -> libc::uintptr_t;
+    }
+}
+
+pub fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        rustrt::rust_dbg_call(cb, n)
+    }
+}
+
+pub extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+    if data == 1 { data } else { fact(data - 1) * data }
+}
diff --git a/tests/ui/abi/extern/extern-call-deep.rs b/tests/ui/abi/extern/extern-call-deep.rs
new file mode 100644
index 00000000000..db5f2ca652f
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-deep.rs
@@ -0,0 +1,36 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+// ignore-emscripten blows the JS stack
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_call(
+            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+            data: libc::uintptr_t,
+        ) -> libc::uintptr_t;
+    }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+    if data == 1 { data } else { count(data - 1) + 1 }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        rustrt::rust_dbg_call(cb, n)
+    }
+}
+
+pub fn main() {
+    let result = count(1000);
+    println!("result = {}", result);
+    assert_eq!(result, 1000);
+}
diff --git a/tests/ui/abi/extern/extern-call-deep2.rs b/tests/ui/abi/extern/extern-call-deep2.rs
new file mode 100644
index 00000000000..60e8db1592e
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-deep2.rs
@@ -0,0 +1,41 @@
+// run-pass
+#![allow(unused_must_use)]
+// ignore-emscripten no threads support
+#![feature(rustc_private)]
+
+extern crate libc;
+use std::thread;
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_call(
+            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+            data: libc::uintptr_t,
+        ) -> libc::uintptr_t;
+    }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+    if data == 1 { data } else { count(data - 1) + 1 }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        rustrt::rust_dbg_call(cb, n)
+    }
+}
+
+pub fn main() {
+    // Make sure we're on a thread with small Rust stacks (main currently
+    // has a large stack)
+    thread::spawn(move || {
+        let result = count(1000);
+        println!("result = {}", result);
+        assert_eq!(result, 1000);
+    })
+    .join();
+}
diff --git a/tests/ui/abi/extern/extern-call-direct.rs b/tests/ui/abi/extern/extern-call-direct.rs
new file mode 100644
index 00000000000..19b901d49a4
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-direct.rs
@@ -0,0 +1,10 @@
+// run-pass
+// Test direct calls to extern fns.
+
+
+extern "C" fn f(x: usize) -> usize { x * 2 }
+
+pub fn main() {
+    let x = f(22);
+    assert_eq!(x, 44);
+}
diff --git a/tests/ui/abi/extern/extern-call-indirect.rs b/tests/ui/abi/extern/extern-call-indirect.rs
new file mode 100644
index 00000000000..886e8f6be10
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-indirect.rs
@@ -0,0 +1,35 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_call(
+            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+            data: libc::uintptr_t,
+        ) -> libc::uintptr_t;
+    }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+    if data == 1 { data } else { fact(data - 1) * data }
+}
+
+fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        rustrt::rust_dbg_call(cb, n)
+    }
+}
+
+pub fn main() {
+    let result = fact(10);
+    println!("result = {}", result);
+    assert_eq!(result, 3628800);
+}
diff --git a/tests/ui/abi/extern/extern-call-scrub.rs b/tests/ui/abi/extern/extern-call-scrub.rs
new file mode 100644
index 00000000000..ff33cf31af8
--- /dev/null
+++ b/tests/ui/abi/extern/extern-call-scrub.rs
@@ -0,0 +1,45 @@
+// run-pass
+#![allow(unused_must_use)]
+// This time we're testing repeatedly going up and down both stacks to
+// make sure the stack pointers are maintained properly in both
+// directions
+
+// ignore-emscripten no threads support
+#![feature(rustc_private)]
+
+extern crate libc;
+use std::thread;
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_call(
+            cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
+            data: libc::uintptr_t,
+        ) -> libc::uintptr_t;
+    }
+}
+
+extern "C" fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
+    if data == 1 { data } else { count(data - 1) + count(data - 1) }
+}
+
+fn count(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        rustrt::rust_dbg_call(cb, n)
+    }
+}
+
+pub fn main() {
+    // Make sure we're on a thread with small Rust stacks (main currently
+    // has a large stack)
+    thread::spawn(move || {
+        let result = count(12);
+        println!("result = {}", result);
+        assert_eq!(result, 2048);
+    })
+    .join();
+}
diff --git a/tests/ui/abi/extern/extern-crosscrate.rs b/tests/ui/abi/extern/extern-crosscrate.rs
new file mode 100644
index 00000000000..123ce20ca26
--- /dev/null
+++ b/tests/ui/abi/extern/extern-crosscrate.rs
@@ -0,0 +1,21 @@
+// run-pass
+// aux-build:extern-crosscrate-source.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate externcallback;
+extern crate libc;
+
+fn fact(n: libc::uintptr_t) -> libc::uintptr_t {
+    unsafe {
+        println!("n = {}", n);
+        externcallback::rustrt::rust_dbg_call(externcallback::cb, n)
+    }
+}
+
+pub fn main() {
+    let result = fact(10);
+    println!("result = {}", result);
+    assert_eq!(result, 3628800);
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU16s.rs b/tests/ui/abi/extern/extern-pass-TwoU16s.rs
new file mode 100644
index 00000000000..cff25511cc9
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU16s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU16s {
+    one: u16,
+    two: u16,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU16s { one: 22, two: 23 };
+        let y = rust_dbg_extern_identity_TwoU16s(x);
+        assert_eq!(x, y);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU32s.rs b/tests/ui/abi/extern/extern-pass-TwoU32s.rs
new file mode 100644
index 00000000000..03a8ecf241d
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU32s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU32s {
+    one: u32,
+    two: u32,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU32s { one: 22, two: 23 };
+        let y = rust_dbg_extern_identity_TwoU32s(x);
+        assert_eq!(x, y);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU64s.rs b/tests/ui/abi/extern/extern-pass-TwoU64s.rs
new file mode 100644
index 00000000000..8bbc987c821
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU64s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU64s {
+    one: u64,
+    two: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU64s { one: 22, two: 23 };
+        let y = rust_dbg_extern_identity_TwoU64s(x);
+        assert_eq!(x, y);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-TwoU8s.rs b/tests/ui/abi/extern/extern-pass-TwoU8s.rs
new file mode 100644
index 00000000000..55a53c250bf
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-TwoU8s.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a foreign function that accepts and returns a struct
+// by value.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct TwoU8s {
+    one: u8,
+    two: u8,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s;
+}
+
+pub fn main() {
+    unsafe {
+        let x = TwoU8s { one: 22, two: 23 };
+        let y = rust_dbg_extern_identity_TwoU8s(x);
+        assert_eq!(x, y);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-char.rs b/tests/ui/abi/extern/extern-pass-char.rs
new file mode 100644
index 00000000000..2b10d26d1dd
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-char.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a function that takes/returns a u8.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(22, rust_dbg_extern_identity_u8(22));
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-double.rs b/tests/ui/abi/extern/extern-pass-double.rs
new file mode 100644
index 00000000000..0b556c99e8d
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-double.rs
@@ -0,0 +1,13 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(22.0_f64, rust_dbg_extern_identity_double(22.0_f64));
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-empty.rs b/tests/ui/abi/extern/extern-pass-empty.rs
new file mode 100644
index 00000000000..ee974f6dbde
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-empty.rs
@@ -0,0 +1,55 @@
+// run-pass
+#![allow(improper_ctypes)] // FIXME: this test is inherently not FFI-safe.
+
+// Test a foreign function that accepts empty struct.
+
+// pretty-expanded FIXME #23616
+// ignore-msvc
+// ignore-emscripten emcc asserts on an empty struct as an argument
+
+#[repr(C)]
+struct TwoU8s {
+    one: u8,
+    two: u8,
+}
+
+#[repr(C)]
+struct ManyInts {
+    arg1: i8,
+    arg2: i16,
+    arg3: i32,
+    arg4: i16,
+    arg5: i8,
+    arg6: TwoU8s,
+}
+
+#[repr(C)]
+struct Empty;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts);
+}
+
+pub fn main() {
+    unsafe {
+        let x = ManyInts {
+            arg1: 2,
+            arg2: 3,
+            arg3: 4,
+            arg4: 5,
+            arg5: 6,
+            arg6: TwoU8s { one: 7, two: 8 },
+        };
+        let y = ManyInts {
+            arg1: 1,
+            arg2: 2,
+            arg3: 3,
+            arg4: 4,
+            arg5: 5,
+            arg6: TwoU8s { one: 6, two: 7 },
+        };
+        let empty = Empty;
+        rust_dbg_extern_empty_struct(x, empty, y);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-u32.rs b/tests/ui/abi/extern/extern-pass-u32.rs
new file mode 100644
index 00000000000..c9b8d52cf5b
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-u32.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a function that takes/returns a u32.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(22, rust_dbg_extern_identity_u32(22));
+    }
+}
diff --git a/tests/ui/abi/extern/extern-pass-u64.rs b/tests/ui/abi/extern/extern-pass-u64.rs
new file mode 100644
index 00000000000..5103129abaa
--- /dev/null
+++ b/tests/ui/abi/extern/extern-pass-u64.rs
@@ -0,0 +1,15 @@
+// run-pass
+// ignore-wasm32-bare no libc for ffi testing
+
+// Test a call to a function that takes/returns a u64.
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(22, rust_dbg_extern_identity_u64(22));
+    }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU16s.rs b/tests/ui/abi/extern/extern-return-TwoU16s.rs
new file mode 100644
index 00000000000..2551c93a765
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU16s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU16s {
+    one: u16,
+    two: u16,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s;
+}
+
+pub fn main() {
+    unsafe {
+        let y = rust_dbg_extern_return_TwoU16s();
+        assert_eq!(y.one, 10);
+        assert_eq!(y.two, 20);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU32s.rs b/tests/ui/abi/extern/extern-return-TwoU32s.rs
new file mode 100644
index 00000000000..70a42895d91
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU32s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU32s {
+    one: u32,
+    two: u32,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
+}
+
+pub fn main() {
+    unsafe {
+        let y = rust_dbg_extern_return_TwoU32s();
+        assert_eq!(y.one, 10);
+        assert_eq!(y.two, 20);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU64s.rs b/tests/ui/abi/extern/extern-return-TwoU64s.rs
new file mode 100644
index 00000000000..dd264fb9c19
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU64s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU64s {
+    one: u64,
+    two: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s;
+}
+
+pub fn main() {
+    unsafe {
+        let y = rust_dbg_extern_return_TwoU64s();
+        assert_eq!(y.one, 10);
+        assert_eq!(y.two, 20);
+    }
+}
diff --git a/tests/ui/abi/extern/extern-return-TwoU8s.rs b/tests/ui/abi/extern/extern-return-TwoU8s.rs
new file mode 100644
index 00000000000..b60387aed99
--- /dev/null
+++ b/tests/ui/abi/extern/extern-return-TwoU8s.rs
@@ -0,0 +1,22 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+pub struct TwoU8s {
+    one: u8,
+    two: u8,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s;
+}
+
+pub fn main() {
+    unsafe {
+        let y = rust_dbg_extern_return_TwoU8s();
+        assert_eq!(y.one, 10);
+        assert_eq!(y.two, 20);
+    }
+}
diff --git a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs
new file mode 100644
index 00000000000..3c649b778bd
--- /dev/null
+++ b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs
@@ -0,0 +1,37 @@
+#![crate_name = "foreign_lib"]
+#![feature(rustc_private)]
+
+pub mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_get_test_int() -> libc::intptr_t;
+    }
+}
+
+pub mod rustrt2 {
+    extern crate libc;
+
+    extern "C" {
+        pub fn rust_get_test_int() -> libc::intptr_t;
+    }
+}
+
+pub mod rustrt3 {
+    // Different type, but same ABI (on all supported platforms).
+    // Ensures that we don't ICE or trigger LLVM asserts when
+    // importing the same symbol under different types.
+    // See https://github.com/rust-lang/rust/issues/32740.
+    extern "C" {
+        pub fn rust_get_test_int() -> *const u8;
+    }
+}
+
+pub fn local_uses() {
+    unsafe {
+        let x = rustrt::rust_get_test_int();
+        assert_eq!(x, rustrt2::rust_get_test_int());
+        assert_eq!(x as *const _, rustrt3::rust_get_test_int());
+    }
+}
diff --git a/tests/ui/abi/foreign/foreign-call-no-runtime.rs b/tests/ui/abi/foreign/foreign-call-no-runtime.rs
new file mode 100644
index 00000000000..d5b90a3592b
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-call-no-runtime.rs
@@ -0,0 +1,60 @@
+// run-pass
+// ignore-emscripten no threads support
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::mem;
+use std::thread;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t), data: libc::uintptr_t) -> libc::uintptr_t;
+}
+
+pub fn main() {
+    unsafe {
+        thread::spawn(move || {
+            let i: isize = 100;
+            rust_dbg_call(callback_isize, mem::transmute(&i));
+        })
+        .join()
+        .unwrap();
+
+        thread::spawn(move || {
+            let i: i32 = 100;
+            rust_dbg_call(callback_i32, mem::transmute(&i));
+        })
+        .join()
+        .unwrap();
+
+        thread::spawn(move || {
+            let i: i64 = 100;
+            rust_dbg_call(callback_i64, mem::transmute(&i));
+        })
+        .join()
+        .unwrap();
+    }
+}
+
+extern "C" fn callback_isize(data: libc::uintptr_t) {
+    unsafe {
+        let data: *const isize = mem::transmute(data);
+        assert_eq!(*data, 100);
+    }
+}
+
+extern "C" fn callback_i64(data: libc::uintptr_t) {
+    unsafe {
+        let data: *const i64 = mem::transmute(data);
+        assert_eq!(*data, 100);
+    }
+}
+
+extern "C" fn callback_i32(data: libc::uintptr_t) {
+    unsafe {
+        let data: *const i32 = mem::transmute(data);
+        assert_eq!(*data, 100);
+    }
+}
diff --git a/tests/ui/abi/foreign/foreign-dupe.rs b/tests/ui/abi/foreign/foreign-dupe.rs
new file mode 100644
index 00000000000..3c9f0f583d4
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-dupe.rs
@@ -0,0 +1,17 @@
+// run-pass
+// aux-build:foreign_lib.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+// Check that we can still call duplicated extern (imported) functions
+// which were declared in another crate. See issues #32740 and #32783.
+
+
+extern crate foreign_lib;
+
+pub fn main() {
+    unsafe {
+        let x = foreign_lib::rustrt::rust_get_test_int();
+        assert_eq!(x, foreign_lib::rustrt2::rust_get_test_int());
+        assert_eq!(x as *const _, foreign_lib::rustrt3::rust_get_test_int());
+    }
+}
diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs
new file mode 100644
index 00000000000..f366b6ee1bd
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs
@@ -0,0 +1,30 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct S {
+    x: u64,
+    y: u64,
+    z: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn get_x(x: S) -> u64;
+    pub fn get_y(x: S) -> u64;
+    pub fn get_z(x: S) -> u64;
+}
+
+#[inline(never)]
+fn indirect_call(func: unsafe extern "C" fn(s: S) -> u64, s: S) -> u64 {
+    unsafe { func(s) }
+}
+
+fn main() {
+    let s = S { x: 1, y: 2, z: 3 };
+    assert_eq!(s.x, indirect_call(get_x, s));
+    assert_eq!(s.y, indirect_call(get_y, s));
+    assert_eq!(s.z, indirect_call(get_z, s));
+}
diff --git a/tests/ui/abi/foreign/foreign-no-abi.rs b/tests/ui/abi/foreign/foreign-no-abi.rs
new file mode 100644
index 00000000000..3f4f70c99e6
--- /dev/null
+++ b/tests/ui/abi/foreign/foreign-no-abi.rs
@@ -0,0 +1,22 @@
+// run-pass
+// ABI is cdecl by default
+
+// ignore-wasm32-bare no libc to test ffi with
+// pretty-expanded FIXME #23616
+
+#![feature(rustc_private)]
+
+mod rustrt {
+    extern crate libc;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_get_test_int() -> libc::intptr_t;
+    }
+}
+
+pub fn main() {
+    unsafe {
+        rustrt::rust_get_test_int();
+    }
+}
diff --git a/tests/ui/abi/foreign/invoke-external-foreign.rs b/tests/ui/abi/foreign/invoke-external-foreign.rs
new file mode 100644
index 00000000000..dbd2b4ad865
--- /dev/null
+++ b/tests/ui/abi/foreign/invoke-external-foreign.rs
@@ -0,0 +1,17 @@
+// run-pass
+// aux-build:foreign_lib.rs
+// ignore-wasm32-bare no libc to test ffi with
+
+// The purpose of this test is to check that we can
+// successfully (and safely) invoke external, cdecl
+// functions from outside the crate.
+
+// pretty-expanded FIXME #23616
+
+extern crate foreign_lib;
+
+pub fn main() {
+    unsafe {
+        let _foo = foreign_lib::rustrt::rust_get_test_int();
+    }
+}
diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
new file mode 100644
index 00000000000..d7f5e19219e
--- /dev/null
+++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
@@ -0,0 +1,192 @@
+// This test check that even if we mixup target feature of function with homogenous floats,
+// the abi is sound and still produce the right answer.
+//
+// This is basically the same test as src/test/ui/simd/target-feature-mixup.rs but for floats and
+// without #[repr(simd)]
+
+// run-pass
+// ignore-emscripten
+// ignore-sgx no processes
+
+#![feature(avx512_target_feature)]
+
+#![allow(overflowing_literals)]
+#![allow(unused_variables)]
+
+use std::process::{Command, ExitStatus};
+use std::env;
+
+fn main() {
+    if let Some(level) = env::args().nth(1) {
+        return test::main(&level)
+    }
+
+    match std::env::var("TARGET") {
+        Ok(s) => {
+            // Skip this tests on i586-unknown-linux-gnu where sse2 is disabled
+            if s.contains("i586") {
+                return
+            }
+        }
+        Err(_) => return,
+    }
+
+    let me = env::current_exe().unwrap();
+    for level in ["sse", "avx", "avx512"].iter() {
+        let status = Command::new(&me).arg(level).status().unwrap();
+        if status.success() {
+            println!("success with {}", level);
+            continue
+        }
+
+        // We don't actually know if our computer has the requisite target features
+        // for the test below. Testing for that will get added to libstd later so
+        // for now just assume sigill means this is a machine that can't run this test.
+        if is_sigill(status) {
+            println!("sigill with {}, assuming spurious", level);
+            continue
+        }
+        panic!("invalid status at {}: {}", level, status);
+    }
+}
+
+#[cfg(unix)]
+fn is_sigill(status: ExitStatus) -> bool {
+    use std::os::unix::prelude::*;
+    status.signal() == Some(4)
+}
+
+#[cfg(windows)]
+fn is_sigill(status: ExitStatus) -> bool {
+    status.code() == Some(0xc000001d)
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+#[allow(nonstandard_style)]
+mod test {
+    #[derive(PartialEq, Debug, Clone, Copy)]
+    struct f32x2(f32, f32);
+
+    #[derive(PartialEq, Debug, Clone, Copy)]
+    struct f32x4(f32, f32, f32, f32);
+
+    #[derive(PartialEq, Debug, Clone, Copy)]
+    struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32);
+
+    pub fn main(level: &str) {
+        unsafe {
+            main_normal(level);
+            main_sse(level);
+            if level == "sse" {
+                return
+            }
+            main_avx(level);
+            if level == "avx" {
+                return
+            }
+            main_avx512(level);
+        }
+    }
+
+    macro_rules! mains {
+        ($(
+            $(#[$attr:meta])*
+            unsafe fn $main:ident(level: &str) {
+                ...
+            }
+        )*) => ($(
+            $(#[$attr])*
+            unsafe fn $main(level: &str) {
+                let m128 = f32x2(1., 2.);
+                let m256 = f32x4(3., 4., 5., 6.);
+                let m512 = f32x8(7., 8., 9., 10., 11., 12., 13., 14.);
+                assert_eq!(id_sse_128(m128), m128);
+                assert_eq!(id_sse_256(m256), m256);
+                assert_eq!(id_sse_512(m512), m512);
+
+                if level == "sse" {
+                    return
+                }
+                assert_eq!(id_avx_128(m128), m128);
+                assert_eq!(id_avx_256(m256), m256);
+                assert_eq!(id_avx_512(m512), m512);
+
+                if level == "avx" {
+                    return
+                }
+                assert_eq!(id_avx512_128(m128), m128);
+                assert_eq!(id_avx512_256(m256), m256);
+                assert_eq!(id_avx512_512(m512), m512);
+            }
+        )*)
+    }
+
+    mains! {
+        unsafe fn main_normal(level: &str) { ... }
+        #[target_feature(enable = "sse2")]
+        unsafe fn main_sse(level: &str) { ... }
+        #[target_feature(enable = "avx")]
+        unsafe fn main_avx(level: &str) { ... }
+        #[target_feature(enable = "avx512bw")]
+        unsafe fn main_avx512(level: &str) { ... }
+    }
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn id_sse_128(a: f32x2) -> f32x2 {
+        assert_eq!(a, f32x2(1., 2.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn id_sse_256(a: f32x4) -> f32x4 {
+        assert_eq!(a, f32x4(3., 4., 5., 6.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn id_sse_512(a: f32x8) -> f32x8 {
+        assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx")]
+    unsafe fn id_avx_128(a: f32x2) -> f32x2 {
+        assert_eq!(a, f32x2(1., 2.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx")]
+    unsafe fn id_avx_256(a: f32x4) -> f32x4 {
+        assert_eq!(a, f32x4(3., 4., 5., 6.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx")]
+    unsafe fn id_avx_512(a: f32x8) -> f32x8 {
+        assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx512bw")]
+    unsafe fn id_avx512_128(a: f32x2) -> f32x2 {
+        assert_eq!(a, f32x2(1., 2.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx512bw")]
+    unsafe fn id_avx512_256(a: f32x4) -> f32x4 {
+        assert_eq!(a, f32x4(3., 4., 5., 6.));
+        a.clone()
+    }
+
+    #[target_feature(enable = "avx512bw")]
+    unsafe fn id_avx512_512(a: f32x8) -> f32x8 {
+        assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.));
+        a.clone()
+    }
+}
+
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+mod test {
+    pub fn main(level: &str) {}
+}
diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs
new file mode 100644
index 00000000000..347a840296d
--- /dev/null
+++ b/tests/ui/abi/issue-28676.rs
@@ -0,0 +1,40 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct Quad {
+    a: u64,
+    b: u64,
+    c: u64,
+    d: u64,
+}
+
+mod rustrt {
+    use super::Quad;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn get_c_many_params(
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            f: Quad,
+        ) -> u64;
+    }
+}
+
+fn test() {
+    unsafe {
+        let null = std::ptr::null();
+        let q = Quad { a: 1, b: 2, c: 3, d: 4 };
+        assert_eq!(rustrt::get_c_many_params(null, null, null, null, q), q.c);
+    }
+}
+
+pub fn main() {
+    test();
+}
diff --git a/tests/ui/abi/issues/issue-22565-rust-call.rs b/tests/ui/abi/issues/issue-22565-rust-call.rs
new file mode 100644
index 00000000000..a572666c888
--- /dev/null
+++ b/tests/ui/abi/issues/issue-22565-rust-call.rs
@@ -0,0 +1,31 @@
+#![feature(unboxed_closures)]
+
+extern "rust-call" fn b(_i: i32) {}
+//~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+
+trait Tr {
+    extern "rust-call" fn a();
+    //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+
+    extern "rust-call" fn b() {}
+    //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+struct Foo;
+
+impl Foo {
+    extern "rust-call" fn bar() {}
+    //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+impl Tr for Foo {
+    extern "rust-call" fn a() {}
+    //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument
+}
+
+fn main() {
+    b(10);
+    Foo::bar();
+    <Foo as Tr>::a();
+    <Foo as Tr>::b();
+}
diff --git a/tests/ui/abi/issues/issue-22565-rust-call.stderr b/tests/ui/abi/issues/issue-22565-rust-call.stderr
new file mode 100644
index 00000000000..9d205b444fa
--- /dev/null
+++ b/tests/ui/abi/issues/issue-22565-rust-call.stderr
@@ -0,0 +1,33 @@
+error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument
+  --> $DIR/issue-22565-rust-call.rs:3:1
+   |
+LL | extern "rust-call" fn b(_i: i32) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32`
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+  --> $DIR/issue-22565-rust-call.rs:17:5
+   |
+LL |     extern "rust-call" fn bar() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+  --> $DIR/issue-22565-rust-call.rs:22:5
+   |
+LL |     extern "rust-call" fn a() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+  --> $DIR/issue-22565-rust-call.rs:7:5
+   |
+LL |     extern "rust-call" fn a();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "rust-call" ABI must take a single non-self tuple argument
+  --> $DIR/issue-22565-rust-call.rs:10:5
+   |
+LL |     extern "rust-call" fn b() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs
new file mode 100644
index 00000000000..29b2405189c
--- /dev/null
+++ b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs
@@ -0,0 +1,46 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct QuadFloats {
+    a: f32,
+    b: f32,
+    c: f32,
+    d: f32,
+}
+
+mod rustrt {
+    use super::QuadFloats;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn get_c_exhaust_sysv64_ints(
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            h: QuadFloats,
+        ) -> f32;
+    }
+}
+
+fn test() {
+    unsafe {
+        let null = std::ptr::null();
+        let q = QuadFloats { a: 10.2, b: 20.3, c: 30.4, d: 40.5 };
+        assert_eq!(
+            rustrt::get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+            q.c,
+        );
+    }
+}
+
+pub fn main() {
+    test();
+}
diff --git a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
new file mode 100644
index 00000000000..fba880d4f9a
--- /dev/null
+++ b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs
@@ -0,0 +1,39 @@
+// run-pass
+// ignore-wasm
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn issue_97463_leak_uninit_data(a: u32, b: u32, c: u32) -> u16;
+}
+
+fn main() {
+    const C1: usize = 0x327b23c6;
+    const C2: usize = C1 & 0xFFFF;
+
+    let r1: usize = 0x0;
+    let r2: usize = C1;
+    let r3: usize = 0x0;
+    let value: u16 = unsafe { issue_97463_leak_uninit_data(r1 as u32, r2 as u32, r3 as u32) };
+
+    // NOTE: as an example of the sensitivity of this test to optimization choices,
+    // uncommenting this block of code makes the bug go away on pnkfelix's machine.
+    // (But observing via `dbg!` doesn't hide the bug. At least sometimes.)
+    /*
+    println!("{}", value);
+    println!("{}", value as usize);
+    println!("{}", usize::from(value));
+    println!("{}", (value as usize) & 0xFFFF);
+     */
+
+    let d1 = value;
+    let d2 = value as usize;
+    let d3 = usize::from(value);
+    let d4 = (value as usize) & 0xFFFF;
+
+    let d = (&d1, &d2, &d3, &d4);
+    let d_ = (d1, d2, d3, d4);
+
+    assert_eq!(((&(C2 as u16), &C2, &C2, &C2), (C2 as u16, C2, C2, C2)), (d, d_));
+}
diff --git a/tests/ui/abi/lib-defaults.rs b/tests/ui/abi/lib-defaults.rs
new file mode 100644
index 00000000000..cd0b0bb2321
--- /dev/null
+++ b/tests/ui/abi/lib-defaults.rs
@@ -0,0 +1,17 @@
+// run-pass
+// dont-check-compiler-stderr (rust-lang/rust#54222)
+
+// ignore-wasm32-bare no libc to test ffi with
+
+// compile-flags: -lrust_test_helpers
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    pub fn rust_dbg_extern_identity_u32(x: u32) -> u32;
+}
+
+fn main() {
+    unsafe {
+        rust_dbg_extern_identity_u32(42);
+    }
+}
diff --git a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs
new file mode 100644
index 00000000000..b3392b9c607
--- /dev/null
+++ b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs
@@ -0,0 +1,19 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_interesting_average(_: i64, ...) -> f64;
+}
+
+fn test<T, U>(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 {
+    unsafe {
+        rust_interesting_average(
+            6, a, a as f64, b, b as f64, c, c as f64, d, d as f64, e, e as f64, f, g,
+        ) as i64
+    }
+}
+
+fn main() {
+    assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70);
+}
diff --git a/tests/ui/abi/nullable-pointer-ffi-compat.rs b/tests/ui/abi/nullable-pointer-ffi-compat.rs
new file mode 100644
index 00000000000..0647a18c3c4
--- /dev/null
+++ b/tests/ui/abi/nullable-pointer-ffi-compat.rs
@@ -0,0 +1,28 @@
+// run-pass
+// #11303, #11040:
+// This would previously crash on i686 Linux due to abi differences
+// between returning an Option<T> and T, where T is a non nullable
+// pointer.
+// If we have an enum with two variants such that one is zero sized
+// and the other contains a nonnullable pointer, we don't use a
+// separate discriminant. Instead we use that pointer field to differentiate
+// between the 2 cases.
+// Also, if the variant with the nonnullable pointer has no other fields
+// then we simply express the enum as just a pointer and not wrap it
+// in a struct.
+
+
+use std::mem;
+
+#[inline(never)]
+extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) }
+
+static FOO: isize = 0xDEADBEE;
+
+pub fn main() {
+    unsafe {
+        let f: extern "C" fn(&isize) -> &isize =
+            mem::transmute(foo as extern "C" fn(&isize) -> Option<&isize>);
+        assert_eq!(*f(&FOO), FOO);
+    }
+}
diff --git a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs
new file mode 100644
index 00000000000..19edf9779f3
--- /dev/null
+++ b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs
@@ -0,0 +1,31 @@
+// run-pass
+#![allow(improper_ctypes)]
+
+// MSVC doesn't support 128 bit integers, and other Windows
+// C compilers have very inconsistent views on how the ABI
+// should look like.
+
+// ignore-windows
+// ignore-32bit
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn identity(f: u128) -> u128;
+    fn square(f: i128) -> i128;
+    fn sub(f: i128, f: i128) -> i128;
+}
+
+fn main() {
+    unsafe {
+        let a = 0x734C_C2F2_A521;
+        let b = 0x33EE_0E2A_54E2_59DA_A0E7_8E41;
+        let b_out = identity(b);
+        assert_eq!(b, b_out);
+        let a_square = square(a);
+        assert_eq!(b, a_square as u128);
+        let k = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210;
+        let k_d = 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420;
+        let k_out = sub(k_d, k);
+        assert_eq!(k, k_out);
+    }
+}
diff --git a/tests/ui/abi/rustcall-generic.rs b/tests/ui/abi/rustcall-generic.rs
new file mode 100644
index 00000000000..6eaccc436b6
--- /dev/null
+++ b/tests/ui/abi/rustcall-generic.rs
@@ -0,0 +1,12 @@
+// revisions: normal opt
+// check-pass
+//[opt] compile-flags: -Zmir-opt-level=3
+
+#![feature(unboxed_closures, tuple_trait)]
+
+extern "rust-call" fn foo<T: std::marker::Tuple>(_: T) {}
+
+fn main() {
+    foo(());
+    foo((1, 2));
+}
diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs
new file mode 100644
index 00000000000..ab2b3089485
--- /dev/null
+++ b/tests/ui/abi/segfault-no-out-of-stack.rs
@@ -0,0 +1,48 @@
+// run-pass
+
+#![allow(unused_imports)]
+// ignore-emscripten can't run commands
+// ignore-sgx no processes
+// ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590)
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::env;
+use std::process::{Command, ExitStatus};
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_get_null_ptr() -> *mut ::libc::c_char;
+}
+
+#[cfg(unix)]
+fn check_status(status: std::process::ExitStatus) {
+    use libc;
+    use std::os::unix::process::ExitStatusExt;
+
+    assert!(status.signal() == Some(libc::SIGSEGV) || status.signal() == Some(libc::SIGBUS));
+}
+
+#[cfg(not(unix))]
+fn check_status(status: std::process::ExitStatus) {
+    assert!(!status.success());
+}
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 && args[1] == "segfault" {
+        unsafe {
+            *rust_get_null_ptr() = 1;
+        }; // trigger a segfault
+    } else {
+        let segfault = Command::new(&args[0]).arg("segfault").output().unwrap();
+        let stderr = String::from_utf8_lossy(&segfault.stderr);
+        let stdout = String::from_utf8_lossy(&segfault.stdout);
+        println!("stdout: {}", stdout);
+        println!("stderr: {}", stderr);
+        println!("status: {}", segfault.status);
+        check_status(segfault.status);
+        assert!(!stderr.contains("has overflowed its stack"));
+    }
+}
diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs
new file mode 100644
index 00000000000..6d934538f4c
--- /dev/null
+++ b/tests/ui/abi/stack-probes-lto.rs
@@ -0,0 +1,17 @@
+// run-pass
+// ignore-arm
+// ignore-aarch64
+// ignore-mips
+// ignore-mips64
+// ignore-sparc
+// ignore-sparc64
+// ignore-wasm
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-musl FIXME #31506
+// ignore-pretty
+// ignore-fuchsia no exception handler registered for segfault
+// compile-flags: -C lto
+// no-prefer-dynamic
+
+include!("stack-probes.rs");
diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs
new file mode 100644
index 00000000000..e7b91644b3b
--- /dev/null
+++ b/tests/ui/abi/stack-probes.rs
@@ -0,0 +1,85 @@
+// run-pass
+// ignore-arm
+// ignore-aarch64
+// ignore-mips
+// ignore-mips64
+// ignore-sparc
+// ignore-sparc64
+// ignore-wasm
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-fuchsia no exception handler registered for segfault
+
+use std::env;
+use std::mem::MaybeUninit;
+use std::process::Command;
+use std::thread;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    #[link_name = "rust_dbg_extern_identity_u64"]
+    fn black_box(u: u64);
+}
+
+fn main() {
+    let args = env::args().skip(1).collect::<Vec<_>>();
+    if args.len() > 0 {
+        match &args[0][..] {
+            "main-recurse" => overflow_recurse(),
+            "child-recurse" => thread::spawn(overflow_recurse).join().unwrap(),
+            "child-frame" => overflow_frame(),
+            _ => panic!(),
+        }
+        return;
+    }
+
+    let me = env::current_exe().unwrap();
+
+    // The linux kernel has some different behavior for the main thread because
+    // the main thread's stack can typically grow. We can't always guarantee
+    // that we report stack overflow on the main thread, see #43052 for some
+    // details
+    if cfg!(not(target_os = "linux")) {
+        assert_overflow(Command::new(&me).arg("main-recurse"));
+    }
+    assert_overflow(Command::new(&me).arg("child-recurse"));
+    assert_overflow(Command::new(&me).arg("child-frame"));
+}
+
+#[allow(unconditional_recursion)]
+fn recurse(array: &MaybeUninit<[u64; 1024]>) {
+    unsafe {
+        black_box(array.as_ptr() as u64);
+    }
+    let local: MaybeUninit<[u64; 1024]> = MaybeUninit::uninit();
+    recurse(&local);
+}
+
+#[inline(never)]
+fn overflow_recurse() {
+    recurse(&MaybeUninit::uninit());
+}
+
+fn overflow_frame() {
+    // By using a 1MiB stack frame with only 512KiB stack, we'll jump over any
+    // guard page, even with 64K pages -- but stack probes should catch it.
+    const STACK_SIZE: usize = 512 * 1024;
+    thread::Builder::new().stack_size(STACK_SIZE).spawn(|| {
+        let local: MaybeUninit<[u8; 2 * STACK_SIZE]> = MaybeUninit::uninit();
+        unsafe {
+            black_box(local.as_ptr() as u64);
+        }
+    }).unwrap().join().unwrap();
+}
+
+fn assert_overflow(cmd: &mut Command) {
+    let output = cmd.output().unwrap();
+    assert!(!output.status.success());
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    println!("status: {}", output.status);
+    println!("stdout: {}", stdout);
+    println!("stderr: {}", stderr);
+    assert!(stdout.is_empty());
+    assert!(stderr.contains("has overflowed its stack\n"));
+}
diff --git a/tests/ui/abi/stack-protector.rs b/tests/ui/abi/stack-protector.rs
new file mode 100644
index 00000000000..24bd2e21943
--- /dev/null
+++ b/tests/ui/abi/stack-protector.rs
@@ -0,0 +1,99 @@
+// run-pass
+// only-x86_64-unknown-linux-gnu
+// revisions: ssp no-ssp
+// [ssp] compile-flags: -Z stack-protector=all
+// compile-flags: -C opt-level=2
+// compile-flags: -g
+
+use std::env;
+use std::process::{Command, ExitStatus};
+
+fn main() {
+    if env::args().len() == 1 {
+        // The test is initially run without arguments. Start the process again,
+        // this time *with* an argument; in this configuration, the test program
+        // will deliberately smash the stack.
+        let cur_argv0 = env::current_exe().unwrap();
+        let mut child = Command::new(&cur_argv0);
+        child.arg("stacksmash");
+
+        if cfg!(ssp) {
+            assert_stack_smash_prevented(&mut child);
+        } else {
+            assert_stack_smashed(&mut child);
+        }
+    } else {
+        vulnerable_function();
+        // If we return here the test is broken: it should either have called
+        // malicious_code() which terminates the process, or be caught by the
+        // stack check which also terminates the process.
+        panic!("TEST BUG: stack smash unsuccessful");
+    }
+}
+
+// Avoid inlining to make sure the return address is pushed to stack.
+#[inline(never)]
+fn vulnerable_function() {
+    let mut x = 5usize;
+    let stackaddr = &mut x as *mut usize;
+    let bad_code_ptr = malicious_code as usize;
+    // Overwrite the on-stack return address with the address of `malicious_code()`,
+    // thereby jumping to that function when returning from `vulnerable_function()`.
+    unsafe { fill(stackaddr, bad_code_ptr, 20); }
+}
+
+// Use an uninlined function with its own stack frame to make sure that we don't
+// clobber e.g. the counter or address local variable.
+#[inline(never)]
+unsafe fn fill(addr: *mut usize, val: usize, count: usize) {
+    let mut addr = addr;
+    for _ in 0..count {
+        *addr = val;
+        addr = addr.add(1);
+    }
+}
+
+// We jump to malicious_code() having wreaked havoc with the previous stack
+// frame and not setting up a new one. This function is therefore constrained,
+// e.g. both println!() and std::process::exit() segfaults if called. We
+// therefore keep the amount of work to a minimum by calling POSIX functions
+// directly.
+// The function is un-inlined just to make it possible to set a breakpoint here.
+#[inline(never)]
+fn malicious_code() {
+    let msg = [112u8, 119u8, 110u8, 101u8, 100u8, 33u8, 0u8]; // "pwned!\0" ascii
+    unsafe {
+        write(1, &msg as *const u8, msg.len());
+        _exit(0);
+    }
+}
+extern "C" {
+    fn write(fd: i32, buf: *const u8, count: usize) -> isize;
+    fn _exit(status: i32) -> !;
+}
+
+
+fn assert_stack_smash_prevented(cmd: &mut Command) {
+    let (status, stdout, stderr) = run(cmd);
+    assert!(!status.success());
+    assert!(stdout.is_empty());
+    assert!(stderr.contains("stack smashing detected"));
+}
+
+fn assert_stack_smashed(cmd: &mut Command) {
+    let (status, stdout, stderr) = run(cmd);
+    assert!(status.success());
+    assert!(stdout.contains("pwned!"));
+    assert!(stderr.is_empty());
+}
+
+
+fn run(cmd: &mut Command) -> (ExitStatus, String, String) {
+    let output = cmd.output().unwrap();
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    println!("status: {}", output.status);
+    println!("stdout: {}", stdout);
+    println!("stderr: {}", stderr);
+    (output.status, stdout.to_string(), stderr.to_string())
+}
diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs
new file mode 100644
index 00000000000..ecd8ee94a01
--- /dev/null
+++ b/tests/ui/abi/statics/static-mut-foreign.rs
@@ -0,0 +1,41 @@
+// run-pass
+// Constants (static variables) can be used to match in patterns, but mutable
+// statics cannot. This ensures that there's some form of error if this is
+// attempted.
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    static mut rust_dbg_static_mut: libc::c_int;
+    pub fn rust_dbg_static_mut_check_four();
+}
+
+unsafe fn static_bound(_: &'static libc::c_int) {}
+
+fn static_bound_set(a: &'static mut libc::c_int) {
+    *a = 3;
+}
+
+unsafe fn run() {
+    assert_eq!(rust_dbg_static_mut, 3);
+    rust_dbg_static_mut = 4;
+    assert_eq!(rust_dbg_static_mut, 4);
+    rust_dbg_static_mut_check_four();
+    rust_dbg_static_mut += 1;
+    assert_eq!(rust_dbg_static_mut, 5);
+    rust_dbg_static_mut *= 3;
+    assert_eq!(rust_dbg_static_mut, 15);
+    rust_dbg_static_mut = -3;
+    assert_eq!(rust_dbg_static_mut, -3);
+    static_bound(&rust_dbg_static_mut);
+    static_bound_set(&mut rust_dbg_static_mut);
+}
+
+pub fn main() {
+    unsafe { run() }
+}
diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs
new file mode 100644
index 00000000000..1a7984ea5cd
--- /dev/null
+++ b/tests/ui/abi/struct-enums/struct-return.rs
@@ -0,0 +1,122 @@
+// run-pass
+#![allow(dead_code)]
+// ignore-wasm32-bare no libc to test ffi with
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Quad {
+    a: u64,
+    b: u64,
+    c: u64,
+    d: u64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Floats {
+    a: f64,
+    b: u8,
+    c: f64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct CharCharDouble {
+    a: u8,
+    b: u8,
+    c: f64,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct CharCharFloat {
+    a: u8,
+    b: u8,
+    c: f32,
+}
+
+mod rustrt {
+    use super::{CharCharDouble, CharCharFloat, Floats, Quad};
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern "C" {
+        pub fn rust_dbg_abi_1(q: Quad) -> Quad;
+        pub fn rust_dbg_abi_2(f: Floats) -> Floats;
+        pub fn rust_dbg_abi_3(a: CharCharDouble) -> CharCharDouble;
+        pub fn rust_dbg_abi_4(a: CharCharFloat) -> CharCharFloat;
+    }
+}
+
+fn test1() {
+    unsafe {
+        let q = Quad {
+            a: 0xaaaa_aaaa_aaaa_aaaa,
+            b: 0xbbbb_bbbb_bbbb_bbbb,
+            c: 0xcccc_cccc_cccc_cccc,
+            d: 0xdddd_dddd_dddd_dddd,
+        };
+        let qq = rustrt::rust_dbg_abi_1(q);
+        println!("a: {:x}", qq.a as usize);
+        println!("b: {:x}", qq.b as usize);
+        println!("c: {:x}", qq.c as usize);
+        println!("d: {:x}", qq.d as usize);
+        assert_eq!(qq.a, q.c + 1);
+        assert_eq!(qq.b, q.d - 1);
+        assert_eq!(qq.c, q.a + 1);
+        assert_eq!(qq.d, q.b - 1);
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+fn test2() {
+    unsafe {
+        let f = Floats { a: 1.234567890e-15_f64, b: 0b_1010_1010, c: 1.0987654321e-15_f64 };
+        let ff = rustrt::rust_dbg_abi_2(f);
+        println!("a: {}", ff.a as f64);
+        println!("b: {}", ff.b as usize);
+        println!("c: {}", ff.c as f64);
+        assert_eq!(ff.a, f.c + 1.0f64);
+        assert_eq!(ff.b, 0xff);
+        assert_eq!(ff.c, f.a - 1.0f64);
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+fn test2() {}
+
+#[cfg(target_pointer_width = "64")]
+fn test3() {
+    unsafe {
+        let a = CharCharDouble { a: 1, b: 2, c: 3. };
+        let b = rustrt::rust_dbg_abi_3(a);
+        println!("a: {}", b.a);
+        println!("b: {}", b.b);
+        println!("c: {}", b.c);
+        assert_eq!(b.a, a.a + 1);
+        assert_eq!(b.b, a.b - 1);
+        assert_eq!(b.c, a.c + 1.0);
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+fn test3() {}
+
+fn test4() {
+    unsafe {
+        let a = CharCharFloat { a: 1, b: 2, c: 3. };
+        let b = rustrt::rust_dbg_abi_4(a);
+        println!("a: {}", b.a);
+        println!("b: {}", b.b);
+        println!("c: {}", b.c);
+        assert_eq!(b.a, a.a + 1);
+        assert_eq!(b.b, a.b - 1);
+        assert_eq!(b.c, a.c + 1.0);
+    }
+}
+
+pub fn main() {
+    test1();
+    test2();
+    test3();
+    test4();
+}
diff --git a/tests/ui/abi/union/union-c-interop.rs b/tests/ui/abi/union/union-c-interop.rs
new file mode 100644
index 00000000000..00f04d5b7ff
--- /dev/null
+++ b/tests/ui/abi/union/union-c-interop.rs
@@ -0,0 +1,37 @@
+// run-pass
+#![allow(non_snake_case)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct LARGE_INTEGER_U {
+    LowPart: u32,
+    HighPart: u32,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+union LARGE_INTEGER {
+  __unnamed__: LARGE_INTEGER_U,
+  u: LARGE_INTEGER_U,
+  QuadPart: u64,
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER;
+}
+
+fn main() {
+    unsafe {
+        let mut li = LARGE_INTEGER { QuadPart: 0 };
+        let li_c = increment_all_parts(li);
+        li.__unnamed__.LowPart += 1;
+        li.__unnamed__.HighPart += 1;
+        li.u.LowPart += 1;
+        li.u.HighPart += 1;
+        li.QuadPart += 1;
+        assert_eq!(li.QuadPart, li_c.QuadPart);
+    }
+}
diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr
new file mode 100644
index 00000000000..e86a73ea60f
--- /dev/null
+++ b/tests/ui/abi/unsupported.aarch64.stderr
@@ -0,0 +1,61 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:26:1
+   |
+LL | extern "ptx-kernel" fn ptx() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:28:1
+   |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:30:1
+   |
+LL | extern "wasm" fn wasm() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:32:1
+   |
+LL | extern "aapcs" fn aapcs() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:1
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:1
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:40:1
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:43:1
+   |
+LL | extern "thiscall" fn thiscall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+  --> $DIR/unsupported.rs:47:1
+   |
+LL | extern "stdcall" fn stdcall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 8 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr
new file mode 100644
index 00000000000..f7569c8cdd7
--- /dev/null
+++ b/tests/ui/abi/unsupported.arm.stderr
@@ -0,0 +1,55 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:26:1
+   |
+LL | extern "ptx-kernel" fn ptx() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:28:1
+   |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:30:1
+   |
+LL | extern "wasm" fn wasm() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:1
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:1
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:40:1
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:43:1
+   |
+LL | extern "thiscall" fn thiscall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+  --> $DIR/unsupported.rs:47:1
+   |
+LL | extern "stdcall" fn stdcall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 7 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr
new file mode 100644
index 00000000000..7ca93516db9
--- /dev/null
+++ b/tests/ui/abi/unsupported.i686.stderr
@@ -0,0 +1,39 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:26:1
+   |
+LL | extern "ptx-kernel" fn ptx() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:28:1
+   |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:30:1
+   |
+LL | extern "wasm" fn wasm() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:32:1
+   |
+LL | extern "aapcs" fn aapcs() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:1
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:1
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.rs b/tests/ui/abi/unsupported.rs
new file mode 100644
index 00000000000..6427a5695c0
--- /dev/null
+++ b/tests/ui/abi/unsupported.rs
@@ -0,0 +1,53 @@
+// revisions: x64 i686 aarch64 arm
+//
+// [x64] needs-llvm-components: x86
+// [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+// [i686] needs-llvm-components: x86
+// [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+// [aarch64] needs-llvm-components: aarch64
+// [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu --crate-type=rlib
+// [arm] needs-llvm-components: arm
+// [arm] compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib
+#![no_core]
+#![feature(
+    no_core,
+    lang_items,
+    abi_ptx,
+    abi_msp430_interrupt,
+    abi_avr_interrupt,
+    abi_thiscall,
+    abi_amdgpu_kernel,
+    wasm_abi,
+    abi_x86_interrupt
+)]
+#[lang="sized"]
+trait Sized { }
+
+extern "ptx-kernel" fn ptx() {}
+//~^ ERROR is not a supported ABI
+extern "amdgpu-kernel" fn amdgpu() {}
+//~^ ERROR is not a supported ABI
+extern "wasm" fn wasm() {}
+//~^ ERROR is not a supported ABI
+extern "aapcs" fn aapcs() {}
+//[x64]~^ ERROR is not a supported ABI
+//[i686]~^^ ERROR is not a supported ABI
+//[aarch64]~^^^ ERROR is not a supported ABI
+extern "msp430-interrupt" fn msp430() {}
+//~^ ERROR is not a supported ABI
+extern "avr-interrupt" fn avr() {}
+//~^ ERROR is not a supported ABI
+extern "x86-interrupt" fn x86() {}
+//[aarch64]~^ ERROR is not a supported ABI
+//[arm]~^^ ERROR is not a supported ABI
+extern "thiscall" fn thiscall() {}
+//[x64]~^ ERROR is not a supported ABI
+//[aarch64]~^^ ERROR is not a supported ABI
+//[arm]~^^^ ERROR is not a supported ABI
+extern "stdcall" fn stdcall() {}
+//[x64]~^ WARN use of calling convention not supported
+//[x64]~^^ WARN this was previously accepted
+//[aarch64]~^^^ WARN use of calling convention not supported
+//[aarch64]~^^^^ WARN this was previously accepted
+//[arm]~^^^^^ WARN use of calling convention not supported
+//[arm]~^^^^^^ WARN this was previously accepted
diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr
new file mode 100644
index 00000000000..26023a4584e
--- /dev/null
+++ b/tests/ui/abi/unsupported.x64.stderr
@@ -0,0 +1,55 @@
+error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:26:1
+   |
+LL | extern "ptx-kernel" fn ptx() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"amdgpu-kernel"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:28:1
+   |
+LL | extern "amdgpu-kernel" fn amdgpu() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"wasm"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:30:1
+   |
+LL | extern "wasm" fn wasm() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"aapcs"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:32:1
+   |
+LL | extern "aapcs" fn aapcs() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:1
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:1
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: `"thiscall"` is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:43:1
+   |
+LL | extern "thiscall" fn thiscall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of calling convention not supported on this target
+  --> $DIR/unsupported.rs:47:1
+   |
+LL | extern "stdcall" fn stdcall() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
+
+error: aborting due to 7 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs
new file mode 100644
index 00000000000..a952ea07793
--- /dev/null
+++ b/tests/ui/abi/variadic-ffi.rs
@@ -0,0 +1,84 @@
+// run-pass
+// ignore-wasm32-bare no libc to test ffi with
+#![feature(c_variadic)]
+
+use std::ffi::VaList;
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+    fn rust_interesting_average(_: u64, ...) -> f64;
+
+    // FIXME: we need to disable this lint for `VaList`,
+    // since it contains a `MaybeUninit<i32>` on the asmjs target,
+    // and this type isn't FFI-safe. This is OK for now,
+    // since the type is layout-compatible with `i32`.
+    #[cfg_attr(target_arch = "asmjs", allow(improper_ctypes))]
+    fn rust_valist_interesting_average(_: u64, _: VaList) -> f64;
+}
+
+pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 {
+    rust_valist_interesting_average(n, ap.as_va_list())
+}
+
+pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {
+    let mut ap2 = ap.clone();
+    assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30);
+
+    // Advance one pair in the copy before checking
+    let mut ap2 = ap.clone();
+    let _ = ap2.arg::<u64>();
+    let _ = ap2.arg::<f64>();
+    assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
+
+    // Advance one pair in the original
+    let _ = ap.arg::<u64>();
+    let _ = ap.arg::<f64>();
+
+    let mut ap2 = ap.clone();
+    assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
+
+    let mut ap2 = ap.clone();
+    let _ = ap2.arg::<u64>();
+    let _ = ap2.arg::<f64>();
+    assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70);
+}
+
+pub fn main() {
+    // Call without variadic arguments
+    unsafe {
+        assert!(rust_interesting_average(0).is_nan());
+    }
+
+    // Call with direct arguments
+    unsafe {
+        assert_eq!(rust_interesting_average(1, 10i64, 10.0f64) as i64, 20);
+    }
+
+    // Call with named arguments, variable number of them
+    let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+    unsafe {
+        assert_eq!(rust_interesting_average(2, x1, x2, x3, x4) as i64, 30);
+    }
+
+    // A function that takes a function pointer
+    unsafe fn call(fp: unsafe extern "C" fn(u64, ...) -> f64) {
+        let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+        assert_eq!(fp(2, x1, x2, x3, x4) as i64, 30);
+    }
+
+    unsafe {
+        call(rust_interesting_average);
+
+        // Make a function pointer, pass indirectly
+        let x: unsafe extern "C" fn(u64, ...) -> f64 = rust_interesting_average;
+        call(x);
+    }
+
+    unsafe {
+        assert_eq!(test_valist_forward(2, 10i64, 10f64, 20i64, 20f64) as i64, 30);
+    }
+
+    unsafe {
+        test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64);
+    }
+}
diff --git a/tests/ui/abi/x86stdcall.rs b/tests/ui/abi/x86stdcall.rs
new file mode 100644
index 00000000000..d1cf1319fb0
--- /dev/null
+++ b/tests/ui/abi/x86stdcall.rs
@@ -0,0 +1,22 @@
+// run-pass
+// only-windows
+// GetLastError doesn't seem to work with stack switching
+
+#[cfg(windows)]
+mod kernel32 {
+    extern "system" {
+        pub fn SetLastError(err: usize);
+        pub fn GetLastError() -> usize;
+    }
+}
+
+#[cfg(windows)]
+pub fn main() {
+    unsafe {
+        let expected = 1234;
+        kernel32::SetLastError(expected);
+        let actual = kernel32::GetLastError();
+        println!("actual = {}", actual);
+        assert_eq!(expected, actual);
+    }
+}
diff --git a/tests/ui/abi/x86stdcall2.rs b/tests/ui/abi/x86stdcall2.rs
new file mode 100644
index 00000000000..4d508ecb242
--- /dev/null
+++ b/tests/ui/abi/x86stdcall2.rs
@@ -0,0 +1,27 @@
+// run-pass
+// only-windows
+
+#![allow(non_camel_case_types)]
+pub type HANDLE = usize;
+pub type DWORD = u32;
+pub type SIZE_T = u32;
+pub type LPVOID = usize;
+pub type BOOL = u8;
+
+mod kernel32 {
+    use super::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T};
+
+    extern "system" {
+        pub fn GetProcessHeap() -> HANDLE;
+        pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+        pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+    }
+}
+
+pub fn main() {
+    let heap = unsafe { kernel32::GetProcessHeap() };
+    let mem = unsafe { kernel32::HeapAlloc(heap, 0, 100) };
+    assert!(mem != 0);
+    let res = unsafe { kernel32::HeapFree(heap, 0, mem) };
+    assert!(res != 0);
+}