//@ compile-flags: -O -C no-prepopulate-passes //@ ignore-riscv64 riscv64 has an i128 type used with test_Vector //@ ignore-s390x s390x with default march passes vector types per reference //@ ignore-loongarch64 see codegen/loongarch-abi for loongarch function call tests // This codegen test embeds assumptions about how certain "C" psABIs are handled // so it doesn't apply to all architectures or even all OS // For RISCV: see codegen/riscv-abi // For LoongArch: see codegen/loongarch-abi #![crate_type = "lib"] #![feature(repr_simd, transparent_unions)] use std::marker::PhantomData; #[derive(Copy, Clone)] pub struct Zst1; #[derive(Copy, Clone)] pub struct Zst2(()); #[derive(Copy, Clone)] #[repr(transparent)] pub struct F32(f32); // CHECK: define{{.*}}float @test_F32(float noundef %_1) #[no_mangle] pub extern "C" fn test_F32(_: F32) -> F32 { loop {} } #[repr(transparent)] pub struct Ptr(*mut u8); // CHECK: define{{.*}}ptr @test_Ptr(ptr noundef %_1) #[no_mangle] pub extern "C" fn test_Ptr(_: Ptr) -> Ptr { loop {} } #[repr(transparent)] pub struct WithZst(u64, Zst1); // CHECK: define{{.*}}i64 @test_WithZst(i64 noundef %_1) #[no_mangle] pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { loop {} } #[repr(transparent)] pub struct WithZeroSizedArray(*const f32, [i8; 0]); // CHECK: define{{.*}}ptr @test_WithZeroSizedArray(ptr noundef %_1) #[no_mangle] pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} } #[repr(transparent)] pub struct Generic(T); // CHECK: define{{.*}}double @test_Generic(double noundef %_1) #[no_mangle] pub extern "C" fn test_Generic(_: Generic) -> Generic { loop {} } #[repr(transparent)] pub struct GenericPlusZst(T, Zst2); #[repr(u8)] pub enum Bool { True, False, FileNotFound, } // CHECK: define{{( dso_local)?}} noundef{{( zeroext)?( range\(i8 0, 3\))?}} i8 @test_Gpz(i8 noundef{{( zeroext)?( range\(i8 0, 3\))?}} %_1) #[no_mangle] pub extern "C" fn test_Gpz(_: GenericPlusZst) -> GenericPlusZst { loop {} } #[repr(transparent)] pub struct LifetimePhantom<'a, T: 'a>(*const T, PhantomData<&'a T>); // CHECK: define{{.*}}ptr @test_LifetimePhantom(ptr noundef %_1) #[no_mangle] pub extern "C" fn test_LifetimePhantom(_: LifetimePhantom) -> LifetimePhantom { loop {} } // This works despite current alignment resrictions because PhantomData is always align(1) #[repr(transparent)] pub struct UnitPhantom { val: T, unit: PhantomData, } pub struct Px; // CHECK: define{{.*}}float @test_UnitPhantom(float noundef %_1) #[no_mangle] pub extern "C" fn test_UnitPhantom(_: UnitPhantom) -> UnitPhantom { loop {} } #[repr(transparent)] pub struct TwoZsts(Zst1, i8, Zst2); // CHECK: define{{( dso_local)?}} noundef{{( signext)?}} i8 @test_TwoZsts(i8 noundef{{( signext)?}} %_1) #[no_mangle] pub extern "C" fn test_TwoZsts(_: TwoZsts) -> TwoZsts { loop {} } #[repr(transparent)] pub struct Nested1(Zst2, Generic); // CHECK: define{{.*}}double @test_Nested1(double noundef %_1) #[no_mangle] pub extern "C" fn test_Nested1(_: Nested1) -> Nested1 { loop {} } #[repr(transparent)] pub struct Nested2(Nested1, Zst1); // CHECK: define{{.*}}double @test_Nested2(double noundef %_1) #[no_mangle] pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { loop {} } #[repr(simd)] struct f32x4([f32; 4]); #[repr(transparent)] pub struct Vector(f32x4); // CHECK: define{{.*}}<4 x float> @test_Vector(<4 x float> %_1) #[no_mangle] pub extern "C" fn test_Vector(_: Vector) -> Vector { loop {} } trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } #[repr(transparent)] pub struct StructWithProjection(::It); // CHECK: define{{.*}}float @test_Projection(float noundef %_1) #[no_mangle] pub extern "C" fn test_Projection(_: StructWithProjection) -> StructWithProjection { loop {} } #[repr(transparent)] pub enum EnumF32 { Variant(F32), } // CHECK: define{{.*}}float @test_EnumF32(float noundef %_1) #[no_mangle] pub extern "C" fn test_EnumF32(_: EnumF32) -> EnumF32 { loop {} } #[repr(transparent)] pub enum EnumF32WithZsts { Variant(Zst1, F32, Zst2), } // CHECK: define{{.*}}float @test_EnumF32WithZsts(float noundef %_1) #[no_mangle] pub extern "C" fn test_EnumF32WithZsts(_: EnumF32WithZsts) -> EnumF32WithZsts { loop {} } #[repr(transparent)] pub union UnionF32 { field: F32, } // CHECK: define{{.*}} float @test_UnionF32(float %_1) #[no_mangle] pub extern "C" fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} } #[repr(transparent)] pub union UnionF32WithZsts { zst1: Zst1, field: F32, zst2: Zst2, } // CHECK: define{{.*}}float @test_UnionF32WithZsts(float %_1) #[no_mangle] pub extern "C" fn test_UnionF32WithZsts(_: UnionF32WithZsts) -> UnionF32WithZsts { loop {} } // All that remains to be tested are aggregates. They are tested in separate files called // transparent-*.rs with `only-*` or `ignore-*` directives, because the expected LLVM IR // function signatures vary so much that it's not reasonably possible to cover all of them with a // single CHECK line. // // You may be wondering why we don't just compare the return types and argument types for equality // with FileCheck regex captures. Well, rustc doesn't perform newtype unwrapping on newtypes // containing aggregates. This is OK on all ABIs we support, but because LLVM has not gotten rid of // pointee types yet, the IR function signature will be syntactically different (%Foo* vs // %FooWrapper*).