about summary refs log tree commit diff
path: root/tests/codegen-llvm/riscv-abi
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-07-21 14:34:12 +0200
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2025-07-22 14:28:48 +0200
commita27f3e3fd1e4d16160f8885b6b06665b5319f56c (patch)
treeb033935392cbadf6f85d2dbddf433a88e323aeeb /tests/codegen-llvm/riscv-abi
parented93c1783b404d728d4809973a0550eb33cd293f (diff)
downloadrust-a27f3e3fd1e4d16160f8885b6b06665b5319f56c.tar.gz
rust-a27f3e3fd1e4d16160f8885b6b06665b5319f56c.zip
Rename `tests/codegen` into `tests/codegen-llvm`
Diffstat (limited to 'tests/codegen-llvm/riscv-abi')
-rw-r--r--tests/codegen-llvm/riscv-abi/call-llvm-intrinsics.rs30
-rw-r--r--tests/codegen-llvm/riscv-abi/cast-local-large-enough.rs44
-rw-r--r--tests/codegen-llvm/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs176
-rw-r--r--tests/codegen-llvm/riscv-abi/riscv64-lp64d-abi.rs299
-rw-r--r--tests/codegen-llvm/riscv-abi/riscv64-lp64f-lp64d-abi.rs283
5 files changed, 832 insertions, 0 deletions
diff --git a/tests/codegen-llvm/riscv-abi/call-llvm-intrinsics.rs b/tests/codegen-llvm/riscv-abi/call-llvm-intrinsics.rs
new file mode 100644
index 00000000000..e72a649a530
--- /dev/null
+++ b/tests/codegen-llvm/riscv-abi/call-llvm-intrinsics.rs
@@ -0,0 +1,30 @@
+//@ compile-flags: -C no-prepopulate-passes
+
+//@ only-riscv64
+
+#![feature(link_llvm_intrinsics)]
+#![crate_type = "lib"]
+
+struct A;
+
+impl Drop for A {
+    fn drop(&mut self) {
+        println!("A");
+    }
+}
+
+extern "C" {
+    #[link_name = "llvm.sqrt.f32"]
+    fn sqrt(x: f32) -> f32;
+}
+
+pub fn do_call() {
+    let _a = A;
+
+    unsafe {
+        // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them
+        // CHECK: store float 4.000000e+00, ptr %{{.}}, align 4
+        // CHECK: call float @llvm.sqrt.f32(float %{{.}}
+        sqrt(4.0);
+    }
+}
diff --git a/tests/codegen-llvm/riscv-abi/cast-local-large-enough.rs b/tests/codegen-llvm/riscv-abi/cast-local-large-enough.rs
new file mode 100644
index 00000000000..9d21d73b459
--- /dev/null
+++ b/tests/codegen-llvm/riscv-abi/cast-local-large-enough.rs
@@ -0,0 +1,44 @@
+//@ add-core-stubs
+//@ compile-flags: -Copt-level=0 -Cdebuginfo=0 --target riscv64gc-unknown-linux-gnu
+//@ needs-llvm-components: riscv
+
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+#![crate_type = "lib"]
+
+extern crate minicore;
+use minicore::*;
+
+#[repr(C, align(64))]
+struct Aligned(f64);
+
+#[repr(C, align(64))]
+struct AlignedPair(f32, f64);
+
+impl Copy for Aligned {}
+impl Copy for AlignedPair {}
+
+// CHECK-LABEL: define double @read_aligned
+#[unsafe(no_mangle)]
+pub extern "C" fn read_aligned(x: &Aligned) -> Aligned {
+    // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64
+    // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false)
+    // CHECK-NEXT: %[[RES:.*]] = load double, ptr %[[TEMP]], align 64
+    // CHECK-NEXT: ret double %[[RES]]
+    *x
+}
+
+// CHECK-LABEL: define { float, double } @read_aligned_pair
+#[unsafe(no_mangle)]
+pub extern "C" fn read_aligned_pair(x: &AlignedPair) -> AlignedPair {
+    // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64
+    // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false)
+    // CHECK-NEXT: %[[FIRST:.*]] = load float, ptr %[[TEMP]], align 64
+    // CHECK-NEXT: %[[SECOND_PTR:.*]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 8
+    // CHECK-NEXT: %[[SECOND:.*]] = load double, ptr %[[SECOND_PTR]], align 8
+    // CHECK-NEXT: %[[RES1:.*]] = insertvalue { float, double } poison, float %[[FIRST]], 0
+    // CHECK-NEXT: %[[RES2:.*]] = insertvalue { float, double } %[[RES1]], double %[[SECOND]], 1
+    // CHECK-NEXT: ret { float, double } %[[RES2]]
+    *x
+}
diff --git a/tests/codegen-llvm/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/tests/codegen-llvm/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs
new file mode 100644
index 00000000000..df99f6969fc
--- /dev/null
+++ b/tests/codegen-llvm/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs
@@ -0,0 +1,176 @@
+//@ add-core-stubs
+//@ compile-flags: --target riscv64gc-unknown-linux-gnu -Copt-level=3 -C no-prepopulate-passes -C panic=abort
+//@ needs-llvm-components: riscv
+
+#![crate_type = "lib"]
+#![no_core]
+#![feature(no_core, lang_items)]
+#![allow(improper_ctypes)]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK: define void @f_void()
+#[no_mangle]
+pub extern "C" fn f_void() {}
+
+// CHECK: define noundef zeroext i1 @f_scalar_0(i1 noundef zeroext %a)
+#[no_mangle]
+pub extern "C" fn f_scalar_0(a: bool) -> bool {
+    a
+}
+
+// CHECK: define noundef signext i8 @f_scalar_1(i8 noundef signext %x)
+#[no_mangle]
+pub extern "C" fn f_scalar_1(x: i8) -> i8 {
+    x
+}
+
+// CHECK: define noundef zeroext i8 @f_scalar_2(i8 noundef zeroext %x)
+#[no_mangle]
+pub extern "C" fn f_scalar_2(x: u8) -> u8 {
+    x
+}
+
+// CHECK: define noundef signext i32 @f_scalar_3(i32 noundef signext %x)
+#[no_mangle]
+pub extern "C" fn f_scalar_3(x: i32) -> u32 {
+    x as u32
+}
+
+// CHECK: define noundef i64 @f_scalar_4(i64 noundef %x)
+#[no_mangle]
+pub extern "C" fn f_scalar_4(x: i64) -> i64 {
+    x
+}
+
+// CHECK: define float @f_fp_scalar_1(float %0)
+#[no_mangle]
+pub extern "C" fn f_fp_scalar_1(x: f32) -> f32 {
+    x
+}
+// CHECK: define double @f_fp_scalar_2(double %0)
+#[no_mangle]
+pub extern "C" fn f_fp_scalar_2(x: f64) -> f64 {
+    x
+}
+
+#[repr(C)]
+pub struct Empty {}
+
+// CHECK: define void @f_agg_empty_struct()
+#[no_mangle]
+pub extern "C" fn f_agg_empty_struct(e: Empty) -> Empty {
+    e
+}
+
+#[repr(C)]
+pub struct Tiny {
+    a: u16,
+    b: u16,
+    c: u16,
+    d: u16,
+}
+
+// CHECK: define void @f_agg_tiny(i64 %0)
+#[no_mangle]
+pub extern "C" fn f_agg_tiny(mut e: Tiny) {}
+
+// CHECK: define i64 @f_agg_tiny_ret()
+#[no_mangle]
+pub extern "C" fn f_agg_tiny_ret() -> Tiny {
+    Tiny { a: 1, b: 2, c: 3, d: 4 }
+}
+
+#[repr(C)]
+pub struct Small {
+    a: i64,
+    b: *mut i64,
+}
+
+// CHECK: define void @f_agg_small([2 x i64] %0)
+#[no_mangle]
+pub extern "C" fn f_agg_small(mut x: Small) {}
+
+// CHECK: define [2 x i64] @f_agg_small_ret()
+#[no_mangle]
+pub extern "C" fn f_agg_small_ret() -> Small {
+    Small { a: 1, b: 0 as *mut _ }
+}
+
+#[repr(C)]
+pub struct SmallAligned {
+    a: i128,
+}
+
+// CHECK: define void @f_agg_small_aligned(i128 %0)
+#[no_mangle]
+pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) {}
+
+#[repr(C)]
+pub struct Large {
+    a: i64,
+    b: i64,
+    c: i64,
+    d: i64,
+}
+
+// CHECK: define void @f_agg_large(ptr {{.*}}%x)
+#[no_mangle]
+pub extern "C" fn f_agg_large(mut x: Large) {}
+
+// CHECK: define void @f_agg_large_ret(ptr {{.*}}sret{{.*}}, i32 noundef signext %i, i8 noundef signext %j)
+#[no_mangle]
+pub extern "C" fn f_agg_large_ret(i: i32, j: i8) -> Large {
+    Large { a: 1, b: 2, c: 3, d: 4 }
+}
+
+// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, ptr {{.*}}%d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef %g, i8 noundef %h)
+#[no_mangle]
+pub extern "C" fn f_scalar_stack_1(
+    a: Tiny,
+    b: Small,
+    c: SmallAligned,
+    d: Large,
+    e: u8,
+    f: i8,
+    g: u8,
+    h: i8,
+) {
+}
+
+// CHECK: define void @f_scalar_stack_2(ptr {{.*}}sret{{.*}} %_0, i64 noundef %a, i128 %0, i128 %1, i64 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g)
+#[no_mangle]
+pub extern "C" fn f_scalar_stack_2(
+    a: u64,
+    b: SmallAligned,
+    c: SmallAligned,
+    d: u64,
+    e: u8,
+    f: i8,
+    g: u8,
+) -> Large {
+    Large { a: a as i64, b: e as i64, c: f as i64, d: g as i64 }
+}
+
+extern "C" {
+    fn f_va_callee(_: i32, ...) -> i32;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn f_va_caller() {
+    // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, ptr {{.*}})
+    f_va_callee(
+        1,
+        2i32,
+        3i64,
+        4.0f64,
+        5.0f64,
+        Tiny { a: 1, b: 2, c: 3, d: 4 },
+        Small { a: 10, b: 0 as *mut _ },
+        SmallAligned { a: 11 },
+        Large { a: 12, b: 13, c: 14, d: 15 },
+    );
+    // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i128 {{.*}}, i32 noundef signext 6, i32 noundef signext 7, i32 noundef 8, i32 noundef 9)
+    f_va_callee(1, 2i32, 3i32, 4i32, SmallAligned { a: 5 }, 6i32, 7i32, 8i32, 9i32);
+}
diff --git a/tests/codegen-llvm/riscv-abi/riscv64-lp64d-abi.rs b/tests/codegen-llvm/riscv-abi/riscv64-lp64d-abi.rs
new file mode 100644
index 00000000000..d768ab9381a
--- /dev/null
+++ b/tests/codegen-llvm/riscv-abi/riscv64-lp64d-abi.rs
@@ -0,0 +1,299 @@
+//@ add-core-stubs
+//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target riscv64gc-unknown-linux-gnu
+//@ needs-llvm-components: riscv
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_std]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK: define void @f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 noundef zeroext %i)
+#[no_mangle]
+pub extern "C" fn f_fpr_tracking(
+    a: f64,
+    b: f64,
+    c: f64,
+    d: f64,
+    e: f64,
+    f: f64,
+    g: f64,
+    h: f64,
+    i: u8,
+) {
+}
+
+#[repr(C)]
+pub struct Double {
+    f: f64,
+}
+
+#[repr(C)]
+pub struct DoubleDouble {
+    f: f64,
+    g: f64,
+}
+
+#[repr(C)]
+pub struct DoubleFloat {
+    f: f64,
+    g: f32,
+}
+
+// CHECK: define void @f_double_s_arg(double %0)
+#[no_mangle]
+pub extern "C" fn f_double_s_arg(a: Double) {}
+
+// CHECK: define double @f_ret_double_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_s() -> Double {
+    Double { f: 1. }
+}
+
+// CHECK: define void @f_double_double_s_arg({ double, double } %0)
+#[no_mangle]
+pub extern "C" fn f_double_double_s_arg(a: DoubleDouble) {}
+
+// CHECK: define { double, double } @f_ret_double_double_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_double_s() -> DoubleDouble {
+    DoubleDouble { f: 1., g: 2. }
+}
+
+// CHECK: define void @f_double_float_s_arg({ double, float } %0)
+#[no_mangle]
+pub extern "C" fn f_double_float_s_arg(a: DoubleFloat) {}
+
+// CHECK: define { double, float } @f_ret_double_float_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_float_s() -> DoubleFloat {
+    DoubleFloat { f: 1., g: 2. }
+}
+
+// CHECK: define void @f_double_double_s_arg_insufficient_fprs(double %0, double %1, double %2, double %3, double %4, double %5, double %6, [2 x i64] %7)
+#[no_mangle]
+pub extern "C" fn f_double_double_s_arg_insufficient_fprs(
+    a: f64,
+    b: f64,
+    c: f64,
+    d: f64,
+    e: f64,
+    f: f64,
+    g: f64,
+    h: DoubleDouble,
+) {
+}
+
+#[repr(C)]
+pub struct DoubleInt8 {
+    f: f64,
+    i: i8,
+}
+
+#[repr(C)]
+pub struct DoubleUInt8 {
+    f: f64,
+    i: u8,
+}
+
+#[repr(C)]
+pub struct DoubleInt32 {
+    f: f64,
+    i: i32,
+}
+
+#[repr(C)]
+pub struct DoubleInt64 {
+    f: f64,
+    i: i64,
+}
+
+// CHECK: define void @f_double_int8_s_arg({ double, i8 } %0)
+#[no_mangle]
+pub extern "C" fn f_double_int8_s_arg(a: DoubleInt8) {}
+
+// CHECK: define { double, i8 } @f_ret_double_int8_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_int8_s() -> DoubleInt8 {
+    DoubleInt8 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_double_int32_s_arg({ double, i32 } %0)
+#[no_mangle]
+pub extern "C" fn f_double_int32_s_arg(a: DoubleInt32) {}
+
+// CHECK: define { double, i32 } @f_ret_double_int32_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_int32_s() -> DoubleInt32 {
+    DoubleInt32 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_double_uint8_s_arg({ double, i8 } %0)
+#[no_mangle]
+pub extern "C" fn f_double_uint8_s_arg(a: DoubleUInt8) {}
+
+// CHECK: define { double, i8 } @f_ret_double_uint8_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_uint8_s() -> DoubleUInt8 {
+    DoubleUInt8 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_double_int64_s_arg({ double, i64 } %0)
+#[no_mangle]
+pub extern "C" fn f_double_int64_s_arg(a: DoubleInt64) {}
+
+// CHECK: define { double, i64 } @f_ret_double_int64_s()
+#[no_mangle]
+pub extern "C" fn f_ret_double_int64_s() -> DoubleInt64 {
+    DoubleInt64 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 noundef signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, [2 x i64] %0)
+#[no_mangle]
+pub extern "C" fn f_double_int8_s_arg_insufficient_gprs(
+    a: i32,
+    b: i32,
+    c: i32,
+    d: i32,
+    e: i32,
+    f: i32,
+    g: i32,
+    h: i32,
+    i: DoubleInt8,
+) {
+}
+
+// CHECK: define void @f_struct_double_int8_insufficient_fprs(float %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, [2 x i64] %8)
+#[no_mangle]
+pub extern "C" fn f_struct_double_int8_insufficient_fprs(
+    a: f32,
+    b: f64,
+    c: f64,
+    d: f64,
+    e: f64,
+    f: f64,
+    g: f64,
+    h: f64,
+    i: DoubleInt8,
+) {
+}
+
+#[repr(C)]
+pub struct DoubleArr1 {
+    a: [f64; 1],
+}
+
+// CHECK: define void @f_doublearr1_s_arg(double %0)
+#[no_mangle]
+pub extern "C" fn f_doublearr1_s_arg(a: DoubleArr1) {}
+
+// CHECK: define double @f_ret_doublearr1_s()
+#[no_mangle]
+pub extern "C" fn f_ret_doublearr1_s() -> DoubleArr1 {
+    DoubleArr1 { a: [1.] }
+}
+
+#[repr(C)]
+pub struct DoubleArr2 {
+    a: [f64; 2],
+}
+
+// CHECK: define void @f_doublearr2_s_arg({ double, double } %0)
+#[no_mangle]
+pub extern "C" fn f_doublearr2_s_arg(a: DoubleArr2) {}
+
+// CHECK: define { double, double } @f_ret_doublearr2_s()
+#[no_mangle]
+pub extern "C" fn f_ret_doublearr2_s() -> DoubleArr2 {
+    DoubleArr2 { a: [1., 2.] }
+}
+
+#[repr(C)]
+pub struct Tricky1 {
+    f: [f64; 1],
+}
+
+#[repr(C)]
+pub struct DoubleArr2Tricky1 {
+    g: [Tricky1; 2],
+}
+
+// CHECK: define void @f_doublearr2_tricky1_s_arg({ double, double } %0)
+#[no_mangle]
+pub extern "C" fn f_doublearr2_tricky1_s_arg(a: DoubleArr2Tricky1) {}
+
+// CHECK: define { double, double } @f_ret_doublearr2_tricky1_s()
+#[no_mangle]
+pub extern "C" fn f_ret_doublearr2_tricky1_s() -> DoubleArr2Tricky1 {
+    DoubleArr2Tricky1 { g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
+}
+
+#[repr(C)]
+pub struct EmptyStruct {}
+
+#[repr(C)]
+pub struct DoubleArr2Tricky2 {
+    s: EmptyStruct,
+    g: [Tricky1; 2],
+}
+
+// CHECK: define void @f_doublearr2_tricky2_s_arg({ double, double } %0)
+#[no_mangle]
+pub extern "C" fn f_doublearr2_tricky2_s_arg(a: DoubleArr2Tricky2) {}
+
+// CHECK: define { double, double } @f_ret_doublearr2_tricky2_s()
+#[no_mangle]
+pub extern "C" fn f_ret_doublearr2_tricky2_s() -> DoubleArr2Tricky2 {
+    DoubleArr2Tricky2 { s: EmptyStruct {}, g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
+}
+
+#[repr(C)]
+pub struct IntDoubleInt {
+    a: i32,
+    b: f64,
+    c: i32,
+}
+
+// CHECK: define void @f_int_double_int_s_arg(ptr {{.*}} %a)
+#[no_mangle]
+pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
+
+// CHECK: define void @f_ret_int_double_int_s(ptr {{.*}} sret([24 x i8]) align 8 {{.*}}dereferenceable(24) %_0)
+#[no_mangle]
+pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt {
+    IntDoubleInt { a: 1, b: 2., c: 3 }
+}
+
+#[repr(C)]
+pub struct CharCharDouble {
+    a: u8,
+    b: u8,
+    c: f64,
+}
+
+// CHECK: define void @f_char_char_double_s_arg([2 x i64] %0)
+#[no_mangle]
+pub extern "C" fn f_char_char_double_s_arg(a: CharCharDouble) {}
+
+// CHECK: define [2 x i64] @f_ret_char_char_double_s()
+#[no_mangle]
+pub extern "C" fn f_ret_char_char_double_s() -> CharCharDouble {
+    CharCharDouble { a: 1, b: 2, c: 3. }
+}
+
+#[repr(C)]
+pub union DoubleU {
+    a: f64,
+}
+
+// CHECK: define void @f_double_u_arg(i64 %0)
+#[no_mangle]
+pub extern "C" fn f_double_u_arg(a: DoubleU) {}
+
+// CHECK: define i64 @f_ret_double_u()
+#[no_mangle]
+pub extern "C" fn f_ret_double_u() -> DoubleU {
+    unsafe { DoubleU { a: 1. } }
+}
diff --git a/tests/codegen-llvm/riscv-abi/riscv64-lp64f-lp64d-abi.rs b/tests/codegen-llvm/riscv-abi/riscv64-lp64f-lp64d-abi.rs
new file mode 100644
index 00000000000..361f0322690
--- /dev/null
+++ b/tests/codegen-llvm/riscv-abi/riscv64-lp64f-lp64d-abi.rs
@@ -0,0 +1,283 @@
+//@ add-core-stubs
+//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target riscv64gc-unknown-linux-gnu
+//@ needs-llvm-components: riscv
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_std]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK: define void @f_fpr_tracking(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i8 noundef zeroext %i)
+#[no_mangle]
+pub extern "C" fn f_fpr_tracking(
+    a: f32,
+    b: f32,
+    c: f32,
+    d: f32,
+    e: f32,
+    f: f32,
+    g: f32,
+    h: f32,
+    i: u8,
+) {
+}
+
+#[repr(C)]
+pub struct Float {
+    f: f32,
+}
+
+#[repr(C)]
+pub struct FloatFloat {
+    f: f32,
+    g: f32,
+}
+
+// CHECK: define void @f_float_s_arg(float %0)
+#[no_mangle]
+pub extern "C" fn f_float_s_arg(a: Float) {}
+
+// CHECK: define float @f_ret_float_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_s() -> Float {
+    Float { f: 1. }
+}
+
+// CHECK: define void @f_float_float_s_arg({ float, float } %0)
+#[no_mangle]
+pub extern "C" fn f_float_float_s_arg(a: FloatFloat) {}
+
+// CHECK: define { float, float } @f_ret_float_float_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_float_s() -> FloatFloat {
+    FloatFloat { f: 1., g: 2. }
+}
+
+// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float %0, float %1, float %2, float %3, float %4, float %5, float %6, i64 %7)
+#[no_mangle]
+pub extern "C" fn f_float_float_s_arg_insufficient_fprs(
+    a: f32,
+    b: f32,
+    c: f32,
+    d: f32,
+    e: f32,
+    f: f32,
+    g: f32,
+    h: FloatFloat,
+) {
+}
+
+#[repr(C)]
+pub struct FloatInt8 {
+    f: f32,
+    i: i8,
+}
+
+#[repr(C)]
+pub struct FloatUInt8 {
+    f: f32,
+    i: u8,
+}
+
+#[repr(C)]
+pub struct FloatInt32 {
+    f: f32,
+    i: i32,
+}
+
+#[repr(C)]
+pub struct FloatInt64 {
+    f: f32,
+    i: i64,
+}
+
+// CHECK: define void @f_float_int8_s_arg({ float, i8 } %0)
+#[no_mangle]
+pub extern "C" fn f_float_int8_s_arg(a: FloatInt8) {}
+
+// CHECK: define { float, i8 } @f_ret_float_int8_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_int8_s() -> FloatInt8 {
+    FloatInt8 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_float_int32_s_arg({ float, i32 } %0)
+#[no_mangle]
+pub extern "C" fn f_float_int32_s_arg(a: FloatInt32) {}
+
+// CHECK: define { float, i32 } @f_ret_float_int32_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_int32_s() -> FloatInt32 {
+    FloatInt32 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_float_uint8_s_arg({ float, i8 } %0)
+#[no_mangle]
+pub extern "C" fn f_float_uint8_s_arg(a: FloatUInt8) {}
+
+// CHECK: define { float, i8 } @f_ret_float_uint8_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_uint8_s() -> FloatUInt8 {
+    FloatUInt8 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_float_int64_s_arg({ float, i64 } %0)
+#[no_mangle]
+pub extern "C" fn f_float_int64_s_arg(a: FloatInt64) {}
+
+// CHECK: define { float, i64 } @f_ret_float_int64_s()
+#[no_mangle]
+pub extern "C" fn f_ret_float_int64_s() -> FloatInt64 {
+    FloatInt64 { f: 1., i: 2 }
+}
+
+// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 noundef signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, i64 %0)
+#[no_mangle]
+pub extern "C" fn f_float_int8_s_arg_insufficient_gprs(
+    a: i32,
+    b: i32,
+    c: i32,
+    d: i32,
+    e: i32,
+    f: i32,
+    g: i32,
+    h: i32,
+    i: FloatInt8,
+) {
+}
+
+// CHECK: define void @f_struct_float_int8_insufficient_fprs(float %0, float %1, float %2,  float %3, float %4, float %5, float %6, float %7, i64 %8)
+#[no_mangle]
+pub extern "C" fn f_struct_float_int8_insufficient_fprs(
+    a: f32,
+    b: f32,
+    c: f32,
+    d: f32,
+    e: f32,
+    f: f32,
+    g: f32,
+    h: f32,
+    i: FloatInt8,
+) {
+}
+
+#[repr(C)]
+pub struct FloatArr1 {
+    a: [f32; 1],
+}
+
+// CHECK: define void @f_floatarr1_s_arg(float %0)
+#[no_mangle]
+pub extern "C" fn f_floatarr1_s_arg(a: FloatArr1) {}
+
+// CHECK: define float @f_ret_floatarr1_s()
+#[no_mangle]
+pub extern "C" fn f_ret_floatarr1_s() -> FloatArr1 {
+    FloatArr1 { a: [1.] }
+}
+
+#[repr(C)]
+pub struct FloatArr2 {
+    a: [f32; 2],
+}
+
+// CHECK: define void @f_floatarr2_s_arg({ float, float } %0)
+#[no_mangle]
+pub extern "C" fn f_floatarr2_s_arg(a: FloatArr2) {}
+
+// CHECK: define { float, float } @f_ret_floatarr2_s()
+#[no_mangle]
+pub extern "C" fn f_ret_floatarr2_s() -> FloatArr2 {
+    FloatArr2 { a: [1., 2.] }
+}
+
+#[repr(C)]
+pub struct Tricky1 {
+    f: [f32; 1],
+}
+
+#[repr(C)]
+pub struct FloatArr2Tricky1 {
+    g: [Tricky1; 2],
+}
+
+// CHECK: define void @f_floatarr2_tricky1_s_arg({ float, float } %0)
+#[no_mangle]
+pub extern "C" fn f_floatarr2_tricky1_s_arg(a: FloatArr2Tricky1) {}
+
+// CHECK: define { float, float } @f_ret_floatarr2_tricky1_s()
+#[no_mangle]
+pub extern "C" fn f_ret_floatarr2_tricky1_s() -> FloatArr2Tricky1 {
+    FloatArr2Tricky1 { g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
+}
+
+#[repr(C)]
+pub struct EmptyStruct {}
+
+#[repr(C)]
+pub struct FloatArr2Tricky2 {
+    s: EmptyStruct,
+    g: [Tricky1; 2],
+}
+
+// CHECK: define void @f_floatarr2_tricky2_s_arg({ float, float } %0)
+#[no_mangle]
+pub extern "C" fn f_floatarr2_tricky2_s_arg(a: FloatArr2Tricky2) {}
+
+// CHECK: define { float, float } @f_ret_floatarr2_tricky2_s()
+#[no_mangle]
+pub extern "C" fn f_ret_floatarr2_tricky2_s() -> FloatArr2Tricky2 {
+    FloatArr2Tricky2 { s: EmptyStruct {}, g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
+}
+
+#[repr(C)]
+pub struct IntFloatInt {
+    a: i32,
+    b: f32,
+    c: i32,
+}
+
+// CHECK: define void @f_int_float_int_s_arg([2 x i64] %0)
+#[no_mangle]
+pub extern "C" fn f_int_float_int_s_arg(a: IntFloatInt) {}
+
+// CHECK: define [2 x i64] @f_ret_int_float_int_s()
+#[no_mangle]
+pub extern "C" fn f_ret_int_float_int_s() -> IntFloatInt {
+    IntFloatInt { a: 1, b: 2., c: 3 }
+}
+
+#[repr(C)]
+pub struct CharCharFloat {
+    a: u8,
+    b: u8,
+    c: f32,
+}
+
+// CHECK: define void @f_char_char_float_s_arg(i64 %0)
+#[no_mangle]
+pub extern "C" fn f_char_char_float_s_arg(a: CharCharFloat) {}
+
+// CHECK: define i64 @f_ret_char_char_float_s()
+#[no_mangle]
+pub extern "C" fn f_ret_char_char_float_s() -> CharCharFloat {
+    CharCharFloat { a: 1, b: 2, c: 3. }
+}
+
+#[repr(C)]
+pub union FloatU {
+    a: f32,
+}
+
+// CHECK: define void @f_float_u_arg(i64 %0)
+#[no_mangle]
+pub extern "C" fn f_float_u_arg(a: FloatU) {}
+
+// CHECK: define i64 @f_ret_float_u()
+#[no_mangle]
+pub extern "C" fn f_ret_float_u() -> FloatU {
+    unsafe { FloatU { a: 1. } }
+}