From ca08f5167f9a87195d326fbedf99c706b9eb9939 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 00:20:35 +0200 Subject: [PATCH 01/25] test aarch64 Complex --- tests/codegen-llvm/complex-abi.rs | 61 ++++++++++++++++--------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 277936d0bac2a..a1401d7b83c67 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -21,17 +21,17 @@ //@ [WIN32_GNU] compile-flags: --target i686-pc-windows-gnu //@ [WIN32_GNU] needs-llvm-components: x86 -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: AARCH64 AARCH64_DARWIN AARCH64_MSVC ARM64EC +//@ [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [AARCH64_DARWIN] compile-flags: --target aarch64-apple-darwin +//@ [AARCH64_DARWIN] needs-llvm-components: aarch64 +//@ [AARCH64_MSVC] compile-flags: --target aarch64-pc-windows-msvc +//@ [AARCH64_MSVC] needs-llvm-components: aarch64 +//@ [ARM64EC] compile-flags: --target arm64ec-pc-windows-msvc +//@ [ARM64EC] needs-llvm-components: aarch64 -// revisions: AARCH64 AARCH64_DARWIN AARCH64_MSVC ARM64EC -// [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu -// [AARCH64] needs-llvm-components: aarch64 -// [AARCH64_DARWIN] compile-flags: --target aarch64-apple-darwin -// [AARCH64_DARWIN] needs-llvm-components: aarch64 -// [AARCH64_MSVC] compile-flags: --target aarch64-pc-windows-msvc -// [AARCH64_MSVC] needs-llvm-components: aarch64 -// [ARM64EC] compile-flags: --target arm64ec-pc-windows-msvc -// [ARM64EC] needs-llvm-components: aarch64 +// FIXME: the below revisions are deliberately disabled for now. // revisions: ARM // [ARM] compile-flags: --target arm-unknown-linux-gnueabihf @@ -103,10 +103,10 @@ use minicore::num::Complex; #[no_mangle] pub extern "C" fn cplx_f16(x: Complex) -> Complex { - // AARCH64: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // AARCH64_MSVC: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // ARM64EC: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) + // AARCH64: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // ARM64EC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // ARM: define{{.*}} i32 @cplx_f16([1 x i32] {{.*}}) // I686: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) @@ -125,11 +125,11 @@ pub extern "C" fn cplx_f16(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f32(x: Complex) -> Complex { - // AARCH64: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) - // AARCH64_MSVC: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) + // AARCH64: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // AIX: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // ARM64EC: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) + // ARM64EC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // ARM: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // BPF: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) @@ -159,11 +159,11 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f64(x: Complex) -> Complex { - // AARCH64: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) - // AARCH64_MSVC: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) + // AARCH64: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // AIX: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // ARM64EC: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) + // ARM64EC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // ARM: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // BPF: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [2 x i64] {{.*}}) // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [4 x i32] {{.*}}) @@ -193,6 +193,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f128(x: Complex) -> Complex { + // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) @@ -204,11 +205,11 @@ pub extern "C" fn cplx_f128(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_i8(x: Complex) -> Complex { - // AARCH64: define{{.*}} i16 @cplx_i8(i64{{.*}}) - // AARCH64_DARWIN: define{{.*}} i16 @cplx_i8(i64{{.*}}) - // AARCH64_MSVC: define{{.*}} i16 @cplx_i8(i64{{.*}}) + // AARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // AARCH64_DARWIN: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // AARCH64_MSVC: define{{.*}} i64 @cplx_i8(i64{{.*}}) // AIX: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) - // ARM64EC: define{{.*}} i16 @cplx_i8(i64{{.*}}) + // ARM64EC: define{{.*}} i64 @cplx_i8(i64{{.*}}) // ARM: define{{.*}} i16 @cplx_i8([1 x i32]{{.*}}) // BPF: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, i16 {{.*}}) // CSKY: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) @@ -238,11 +239,11 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_i16(x: Complex) -> Complex { - // AARCH64: define{{.*}} i32 @cplx_i16(i64{{.*}}) - // AARCH64_DARWIN: define{{.*}} i32 @cplx_i16(i64{{.*}}) - // AARCH64_MSVC: define{{.*}} i32 @cplx_i16(i64{{.*}}) + // AARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // AARCH64_DARWIN: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // AARCH64_MSVC: define{{.*}} i64 @cplx_i16(i64{{.*}}) // AIX: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) - // ARM64EC: define{{.*}} i32 @cplx_i16(i64{{.*}}) + // ARM64EC: define{{.*}} i64 @cplx_i16(i64{{.*}}) // ARM: define{{.*}} i32 @cplx_i16([1 x i32] {{.*}}) // BPF: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, i32 {{.*}}) // CSKY: define{{.*}} i32 @cplx_i16(i32 {{.*}}) From 9b5670a5014cead0bfd63e6b03166310396ec284 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 00:27:22 +0200 Subject: [PATCH 02/25] test arm Complex --- tests/codegen-llvm/complex-abi.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index a1401d7b83c67..0e43f008eb93f 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -31,11 +31,11 @@ //@ [ARM64EC] compile-flags: --target arm64ec-pc-windows-msvc //@ [ARM64EC] needs-llvm-components: aarch64 -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: ARM +//@ [ARM] compile-flags: --target arm-unknown-linux-gnueabihf +//@ [ARM] needs-llvm-components: arm -// revisions: ARM -// [ARM] compile-flags: --target arm-unknown-linux-gnueabihf -// [ARM] needs-llvm-components: arm +// FIXME: the below revisions are deliberately disabled for now. // revisions: RISCV64 RISCV32 // [RISCV64] compile-flags: --target riscv64gc-unknown-linux-gnu @@ -107,7 +107,7 @@ pub extern "C" fn cplx_f16(x: Complex) -> Complex { // AARCH64_DARWIN: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // AARCH64_MSVC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // ARM64EC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) - // ARM: define{{.*}} i32 @cplx_f16([1 x i32] {{.*}}) + // ARM: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // I686: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) // LOONGARCH64: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) @@ -130,7 +130,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // AIX: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // ARM64EC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) - // ARM: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // ARM: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // BPF: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) // I686: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -164,7 +164,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // AIX: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // ARM64EC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) - // ARM: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // ARM: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // BPF: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [2 x i64] {{.*}}) // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) @@ -210,7 +210,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} i64 @cplx_i8(i64{{.*}}) // AIX: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // ARM64EC: define{{.*}} i64 @cplx_i8(i64{{.*}}) - // ARM: define{{.*}} i16 @cplx_i8([1 x i32]{{.*}}) + // ARM: define{{.*}} i32 @cplx_i8(i32{{.*}}) // BPF: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, i16 {{.*}}) // CSKY: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) // I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) @@ -244,7 +244,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} i64 @cplx_i16(i64{{.*}}) // AIX: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) // ARM64EC: define{{.*}} i64 @cplx_i16(i64{{.*}}) - // ARM: define{{.*}} i32 @cplx_i16([1 x i32] {{.*}}) + // ARM: define{{.*}} i32 @cplx_i16(i32{{.*}}) // BPF: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, i32 {{.*}}) // CSKY: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // I686: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) @@ -278,7 +278,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // AIX: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // ARM64EC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // ARM: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, [2 x i32] {{.*}}) + // ARM: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, [2 x i32] {{.*}}) // BPF: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // I686: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -312,7 +312,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // AIX: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // ARM64EC: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) - // ARM: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) + // ARM: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, [2 x i64] {{.*}}) // BPF: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) From 3fc84d4f59d9cf2ee53a21b7cad988e3a2532cae Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 00:48:47 +0200 Subject: [PATCH 03/25] test riscv/loongarch Complex --- tests/codegen-llvm/complex-abi.rs | 64 ++++++++++++++++--------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 0e43f008eb93f..83d3f08bb396e 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -35,19 +35,19 @@ //@ [ARM] compile-flags: --target arm-unknown-linux-gnueabihf //@ [ARM] needs-llvm-components: arm -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: RISCV64 RISCV32 +//@ [RISCV64] compile-flags: --target riscv64gc-unknown-linux-gnu +//@ [RISCV64] needs-llvm-components: riscv +//@ [RISCV32] compile-flags: --target riscv32gc-unknown-linux-gnu +//@ [RISCV32] needs-llvm-components: riscv -// revisions: RISCV64 RISCV32 -// [RISCV64] compile-flags: --target riscv64gc-unknown-linux-gnu -// [RISCV64] needs-llvm-components: riscv -// [RISCV32] compile-flags: --target riscv32gc-unknown-linux-gnu -// [RISCV32] needs-llvm-components: riscv +//@ revisions: LOONGARCH64 LOONGARCH32 +//@ [LOONGARCH64] compile-flags: --target loongarch64-unknown-linux-gnu +//@ [LOONGARCH64] needs-llvm-components: loongarch +//@ [LOONGARCH32] compile-flags: --target loongarch32-unknown-none +//@ [LOONGARCH32] needs-llvm-components: loongarch -// revisions: LOONGARCH64 LOONGARCH32 -// [LOONGARCH64] compile-flags: --target loongarch64-unknown-linux-gnu -// [LOONGARCH64] needs-llvm-components: loongarch -// [LOONGARCH32] compile-flags: --target loongarch32-unknown-none -// [LOONGARCH32] needs-llvm-components: loongarch +// FIXME: the below revisions are deliberately disabled for now. // revisions: SPARC64 SPARC // [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu @@ -109,11 +109,11 @@ pub extern "C" fn cplx_f16(x: Complex) -> Complex { // ARM64EC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // ARM: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // I686: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) - // LOONGARCH64: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) + // LOONGARCH32: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) + // LOONGARCH64: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) // NVPTX: define{{.*}} { half, half } @cplx_f16(ptr {{.*}} byval({ half, half }) {{.*}}) - // RISCV32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) - // RISCV64: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) + // RISCV32: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) + // RISCV64: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) // S390X: define{{.*}} void @cplx_f16(ptr {{.*}} sret({ half, half }) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) @@ -134,16 +134,16 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // BPF: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) // I686: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // LOONGARCH64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) + // LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS: define{{.*}} { float, float } @cplx_f32(i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) - // RISCV32: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // RISCV64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) + // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} {{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) @@ -168,16 +168,16 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // BPF: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [2 x i64] {{.*}}) // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // LOONGARCH64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) + // LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // POWERPC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // POWERPC: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) - // RISCV32: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // RISCV64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) + // RISCV32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) @@ -200,6 +200,8 @@ pub extern "C" fn cplx_f128(x: Complex) -> Complex { // WIN32_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // X86_64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // LOONGARCH64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) + // RISCV64 define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) x } @@ -214,16 +216,16 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // BPF: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, i16 {{.*}}) // CSKY: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) // I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) - // LOONGARCH64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) + // LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}}) + // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // MIPS64EL: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) // MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // POWERPC: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) - // RISCV32: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) - // RISCV64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) + // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) + // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) @@ -249,7 +251,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // CSKY: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // I686: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) - // LOONGARCH64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) + // LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // MIPS64EL: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) // MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) @@ -257,7 +259,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // POWERPC64LE: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) // POWERPC: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) - // RISCV64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) + // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) @@ -316,7 +318,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // BPF: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) + // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) @@ -324,7 +326,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // POWERPC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) - // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) + // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) From ff3c873d13412d1a9809c644d7e0b4c6aeb0e7b8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 01:00:53 +0200 Subject: [PATCH 04/25] cleanup --- tests/codegen-llvm/complex-abi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 83d3f08bb396e..78806982412ee 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -145,7 +145,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) + // SPARC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) @@ -214,7 +214,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // ARM64EC: define{{.*}} i64 @cplx_i8(i64{{.*}}) // ARM: define{{.*}} i32 @cplx_i8(i32{{.*}}) // BPF: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, i16 {{.*}}) - // CSKY: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) + // CSKY: define{{.*}} i32 @cplx_i8(i32{{.*}}) // I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) @@ -227,7 +227,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) + // SPARC64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) @@ -261,7 +261,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) + // SPARC64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) From 2b3054016d1f86c53c8a96184ac1546199817b19 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 23:43:30 +0200 Subject: [PATCH 05/25] add Complex abi run-make test --- tests/run-make/complex-abi/main.rs | 499 ++++++++++++++++++++++++++++ tests/run-make/complex-abi/rmake.rs | 13 + tests/run-make/complex-abi/test.c | 113 +++++++ 3 files changed, 625 insertions(+) create mode 100644 tests/run-make/complex-abi/main.rs create mode 100644 tests/run-make/complex-abi/rmake.rs create mode 100644 tests/run-make/complex-abi/test.c diff --git a/tests/run-make/complex-abi/main.rs b/tests/run-make/complex-abi/main.rs new file mode 100644 index 0000000000000..5c9a4367b8c62 --- /dev/null +++ b/tests/run-make/complex-abi/main.rs @@ -0,0 +1,499 @@ +// ignore-tidy-file-linelength +#![feature(complex_numbers, f128)] +#![allow(improper_ctypes)] +#![allow(unused_features)] +#![deny(dead_code)] + +use std::ffi::*; +use std::num::Complex; + +fn main() { + sqrt(); + #[cfg(not(target_family = "wasm"))] + mul(); + #[cfg(not(target_family = "wasm"))] + div(); + pass_simple(); + aligned_int(); + aligned_float(); + spill_gpr(); + spill_fpr(); + partial_gpr(); +} + +// This is just a placeholder until we actually add c_longdouble. +#[allow(non_camel_case_types)] +#[allow(unused)] +type c_longdouble = cfg_select! { + target_arch = "aarch64" => f128, + _ => (), +}; + +fn sqrt() { + unsafe extern "C" { + safe fn csqrtf(_: Complex) -> Complex; + safe fn csqrt(_: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn csqrtl(_: Complex) -> Complex; + } + + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrtf(c), Complex::new(0.0, 1.0)); + + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrt(c), Complex::new(0.0, 1.0)); + + #[cfg(target_arch = "aarch64")] + { + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrtl(c), Complex::new(0.0, 1.0)); + } +} + +#[cfg(not(target_family = "wasm"))] +fn mul() { + unsafe extern "C" { + safe fn __mulsc3(a: c_float, b: c_float, c: c_float, d: c_float) -> Complex; + safe fn __muldc3(a: c_double, b: c_double, c: c_double, d: c_double) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn __multc3( + a: c_longdouble, + b: c_longdouble, + c: c_longdouble, + d: c_longdouble, + ) -> Complex; + } + + assert_eq!(__mulsc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + assert_eq!(__muldc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + + #[cfg(target_arch = "aarch64")] + assert_eq!(__multc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + + // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it. + assert_eq!( + __mulsc3(1.0, 0.0, c_float::INFINITY, c_float::INFINITY), + Complex::new(c_float::INFINITY, c_float::INFINITY) + ); + assert_eq!( + __muldc3(1.0, 0.0, c_double::INFINITY, c_double::INFINITY), + Complex::new(c_double::INFINITY, c_double::INFINITY) + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + __multc3(1.0, 0.0, c_longdouble::INFINITY, c_longdouble::INFINITY), + Complex::new(c_longdouble::INFINITY, c_longdouble::INFINITY) + ); +} + +#[cfg(not(target_family = "wasm"))] +fn div() { + unsafe extern "C" { + safe fn __divsc3(a: c_float, b: c_float, c: c_float, d: c_float) -> Complex; + safe fn __divdc3(a: c_double, b: c_double, c: c_double, d: c_double) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn __divtc3( + a: c_longdouble, + b: c_longdouble, + c: c_longdouble, + d: c_longdouble, + ) -> Complex; + } + + assert_eq!(__divsc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + assert_eq!(__divdc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + + #[cfg(target_arch = "aarch64")] + assert_eq!(__divtc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + + assert_eq!( + __divsc3(c_float::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY) + ); + assert_eq!( + __divdc3(c_double::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY) + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + __divtc3(c_longdouble::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ); +} + +fn pass_simple() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn pass_simple_complex_float(x: Complex) -> Complex; + safe fn pass_simple_complex_double(x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn pass_simple_complex_long_double(x: Complex) -> Complex; + + safe fn pass_simple_complex_char(x: Complex) -> Complex; + safe fn pass_simple_complex_short(x: Complex) -> Complex; + safe fn pass_simple_complex_int(x: Complex) -> Complex; + safe fn pass_simple_complex_long(x: Complex) -> Complex; + safe fn pass_simple_complex_long_long(x: Complex) -> Complex; + } + + assert_eq!(Complex::new(3.5, 4.5), pass_simple_complex_float(Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), pass_simple_complex_double(Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + pass_simple_complex_float(Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + pass_simple_complex_double(Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + pass_simple_complex_long_double(Complex::new( + c_longdouble::INFINITY, + c_longdouble::NEG_INFINITY + )) + ); + + assert_eq!(Complex::new(3, 4), pass_simple_complex_char(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_short(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_int(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_long(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_long_long(Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + pass_simple_complex_char(Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + pass_simple_complex_short(Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + pass_simple_complex_int(Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + pass_simple_complex_long(Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + pass_simple_complex_long_long(Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +fn aligned_int() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn complex_float_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_double_align_int(_: c_int, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn complex_long_double_align_int(_: c_int, x: Complex) -> Complex; + + safe fn complex_char_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_short_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_int_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_long_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_long_long_align_int(_: c_int, x: Complex) -> Complex; + } + + let a = 0xAAAA_AAAAu32 as c_int; + + assert_eq!(Complex::new(3.5, 4.5), complex_float_align_int(a, Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), complex_double_align_int(a, Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + complex_float_align_int(a, Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + complex_double_align_int(a, Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + complex_long_double_align_int( + a, + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ) + ); + + assert_eq!(Complex::new(3, 4), complex_char_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_short_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_int_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_long_align_int(a, Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + complex_char_align_int(a, Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + complex_short_align_int(a, Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + complex_int_align_int(a, Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + complex_long_align_int(a, Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + complex_long_long_align_int(a, Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +fn aligned_float() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn complex_float_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_double_align_float(_: c_float, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn complex_long_double_align_float(_: c_float, x: Complex) -> Complex; + + safe fn complex_char_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_short_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_int_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_long_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_long_long_align_float(_: c_float, x: Complex) -> Complex; + } + + let a = 3.14 as c_float; + + assert_eq!(Complex::new(3.5, 4.5), complex_float_align_float(a, Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), complex_double_align_float(a, Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + complex_float_align_float(a, Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + complex_double_align_float(a, Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + complex_long_double_align_float( + a, + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ) + ); + + assert_eq!(Complex::new(3, 4), complex_char_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_short_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_int_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_long_align_float(a, Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + complex_char_align_float(a, Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + complex_short_align_float(a, Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + complex_int_align_float(a, Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + complex_long_align_float(a, Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + complex_long_long_align_float(a, Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +#[rustfmt::skip] +fn spill_gpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + fn spill_trailing_complex_float_1(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex, h: c_int) -> c_int; + + fn spill_trailing_complex_double_1(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, x: Complex, g: c_int) -> c_int; + fn spill_trailing_complex_double_2(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, x: Complex, f: c_int, g: c_int) -> c_int; + fn spill_trailing_complex_double_3(a: c_int, b: c_int, c: c_int, d: c_int, x: Complex, e: c_int, f: c_int, g: c_int) -> c_int; + + #[cfg(target_arch = "aarch64")] + fn spill_trailing_complex_long_double_1(a: c_int, x: Complex, b: c_int) -> c_int; + } + + unsafe { + assert_eq!( + spill_trailing_complex_float_1(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777, Complex::new(1.0, 2.0), 0x88888888_u32 as c_int), + 0x88888888_u32 as c_int, + ); + + assert_eq!( + spill_trailing_complex_double_1(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, Complex::new(1.0, 2.0), 0x77777777), + 0x77777777, + ); + + assert_eq!( + spill_trailing_complex_double_2(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, Complex::new(1.0, 2.0), 0x66666666, 0x77777777), + 0x77777777, + ); + + assert_eq!( + spill_trailing_complex_double_3(0x11111111, 0x22222222, 0x33333333, 0x44444444, Complex::new(1.0, 2.0), 0x55555555, 0x66666666, 0x77777777), + 0x77777777, + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + spill_trailing_complex_long_double_1(0x11111111, Complex::new(1.0, 2.0), 0x22222222,), + 0x22222222, + ); + } +} + +#[rustfmt::skip] +fn spill_fpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn spill_trailing_complex_float_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, value: Complex, x: f32) -> f32; + safe fn spill_trailing_complex_float_2_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, a7: f32, value: Complex, x: f32) -> f32; + + safe fn spill_trailing_complex_double_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, value: Complex, x: f32) -> f32; + safe fn spill_trailing_complex_double_2_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, value: Complex, x: f32, y: f32) -> f32; + safe fn spill_trailing_complex_double_3_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, a7: f32, value: Complex, x: f32, y: f32) -> f32; + + #[cfg(target_arch = "aarch64")] + safe fn spill_trailing_complex_long_double_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, value: Complex, x: f32) -> f32; + } + + assert_eq!( + spill_trailing_complex_float_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, Complex::new(1.0, 2.0), 0.8), + 0.8, + ); + assert_eq!( + spill_trailing_complex_float_2_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, Complex::new(1.0, 2.0), 0.8), + 0.8, + ); + + assert_eq!( + spill_trailing_complex_double_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, Complex::new(1.0, 2.0), 0.6), + 0.6, + ); + assert_eq!( + spill_trailing_complex_double_2_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, Complex::new(1.0, 2.0), 0.7, 0.8), + 0.8, + ); + assert_eq!( + spill_trailing_complex_double_3_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, Complex::new(1.0, 2.0), 0.8, 0.9), + 0.9, + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + spill_trailing_complex_long_double_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, Complex::new(1.0, 2.0), 0.6), + 0.6, + ); +} + +#[rustfmt::skip] +fn partial_gpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn partial_complex_float(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_double(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn partial_complex_long_double(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + + safe fn partial_complex_char(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_short(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_int(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_long(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_long_long(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + } + + let complex = Complex { re: 3.14, im: 6.28 }; + assert_eq!( + partial_complex_float( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3.14, im: 6.28 }; + assert_eq!( + partial_complex_double( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + #[cfg(target_arch = "aarch64")] + { + let complex = Complex { re: 3.14 as c_longdouble, im: 6.28 as c_longdouble }; + assert_eq!( + partial_complex_long_double( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + } + + let complex = Complex { re: 3 as c_schar, im: 6 as c_schar }; + assert_eq!( + partial_complex_char( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_short, im: 6 as c_short }; + assert_eq!( + partial_complex_short( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_int, im: 6 as c_int }; + assert_eq!( + partial_complex_int( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_long, im: 6 as c_long }; + assert_eq!( + partial_complex_long( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_longlong, im: 6 as c_longlong }; + assert_eq!( + partial_complex_long_long( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); +} diff --git a/tests/run-make/complex-abi/rmake.rs b/tests/run-make/complex-abi/rmake.rs new file mode 100644 index 0000000000000..afb4dc9621d6b --- /dev/null +++ b/tests/run-make/complex-abi/rmake.rs @@ -0,0 +1,13 @@ +//@ needs-target-std +//@ ignore-android: FIXME(#142855) +//@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic +// linking, ignore the test). + +use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc}; + +fn main() { + build_native_static_lib("test"); + rustc().linker(&env_var("CC")).input("main.rs").run(); + run(&bin_name("main")); +} diff --git a/tests/run-make/complex-abi/test.c b/tests/run-make/complex-abi/test.c new file mode 100644 index 0000000000000..45155ca6f113d --- /dev/null +++ b/tests/run-make/complex-abi/test.c @@ -0,0 +1,113 @@ +// ignore-tidy-file-linelength + +_Complex float pass_simple_complex_float(_Complex float x) { return x; } +_Complex double pass_simple_complex_double(_Complex double x) { return x; } +_Complex long double pass_simple_complex_long_double(_Complex long double x) { return x; } + +_Complex char pass_simple_complex_char(_Complex char x) { return x; } +_Complex short pass_simple_complex_short(_Complex short x) { return x; } +_Complex int pass_simple_complex_int(_Complex int x) { return x; } +_Complex long pass_simple_complex_long(_Complex long x) { return x; } +_Complex long long pass_simple_complex_long_long(_Complex long long x) { return x; } + + + +_Complex float complex_float_align_int(int a0, _Complex float value) { return value; } +_Complex double complex_double_align_int(int a0, _Complex double value) { return value; } +_Complex long double complex_long_double_align_int(int a0, _Complex long double value) { return value; } + +_Complex char complex_char_align_int(int a0, _Complex char value) { return value; } +_Complex short complex_short_align_int(int a0, _Complex short value) { return value; } +_Complex int complex_int_align_int(int a0, _Complex int value) { return value; } +_Complex long complex_long_align_int(int a0, _Complex long value) { return value; } +_Complex long long complex_long_long_align_int(int a0, _Complex long long value) { return value; } + + + +_Complex float complex_float_align_float(float a0, _Complex float value) { return value; } +_Complex double complex_double_align_float(float a0, _Complex double value) { return value; } +_Complex long double complex_long_double_align_float(float a0, _Complex long double value) { return value; } + +_Complex char complex_char_align_float(float a0, _Complex char value) { return value; } +_Complex short complex_short_align_float(float a0, _Complex short value) { return value; } +_Complex int complex_int_align_float(float a0, _Complex int value) { return value; } +_Complex long complex_long_align_float(float a0, _Complex long value) { return value; } +_Complex long long complex_long_long_align_float(float a0, _Complex long long value) { return value; } + + + +int spill_trailing_complex_float_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, _Complex float value, int x) { return x; } + +int spill_trailing_complex_double_1(int a0, int a1, int a2, int a3, int a4, int a5, _Complex double value, int x) { return x; } +int spill_trailing_complex_double_2(int a0, int a1, int a2, int a3, int a4, _Complex double value, int x, int y) { return y; } +int spill_trailing_complex_double_3(int a0, int a1, int a2, int a3, _Complex double value, int x, int y, int z) { return z; } + +int spill_trailing_complex_long_double_1(int a0, _Complex long double value, int x) { return x; } + + + +float spill_trailing_complex_float_1_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, _Complex float value, float x) { return x; } +float spill_trailing_complex_float_2_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, _Complex float value, float x) { return x; } + +float spill_trailing_complex_double_1_float(float a0, float a1, float a2, float a3, float a4, float a5, _Complex double value, float x) { return x; } +float spill_trailing_complex_double_2_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, _Complex double value, float x, float y) { return y; } +float spill_trailing_complex_double_3_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, _Complex double value, float x, float y) { return y; } + +float spill_trailing_complex_long_double_1_float(float a0, float a1, float a2, float a3, float a4, float a5, _Complex long double value, float x) { return x; } + + +_Complex float partial_complex_float( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex float value) { + return value; +} + +_Complex double partial_complex_double( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex double value) { + return value; +} + +_Complex long double partial_complex_long_double( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long double value) { + return value; +} + +_Complex char partial_complex_char( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex char value) { + return value; +} + +_Complex short partial_complex_short( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex short value) { + return value; +} + +_Complex int partial_complex_int( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex int value) { + return value; +} + +_Complex long partial_complex_long( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long value) { + return value; +} + +_Complex long long partial_complex_long_long( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long long value) { + return value; +} From 42e6695fb31632e8f5c3d83282ce039e598cd3cb Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 13 Aug 2026 14:22:00 +0200 Subject: [PATCH 06/25] add `TyAndLayout::complex_number` --- compiler/rustc_abi/src/layout/ty.rs | 17 ++++++++++++++--- compiler/rustc_abi/src/lib.rs | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index b835909d2c2ca..013a42b0df050 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -7,7 +7,7 @@ use rustc_macros::StableHash; use crate::layout::{FieldIdx, VariantIdx}; use crate::{ - AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, + AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, Numeric, PointeeInfo, Primitive, Size, Variants, }; @@ -299,7 +299,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { found } - pub fn complex_float(&self, cx: &C) -> Option + pub fn complex_number(&self, cx: &C) -> Option where Ty: TyAbiInterface<'a, C> + Copy, { @@ -314,7 +314,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { debug_assert_eq!(a, b); match a.primitive() { - Primitive::Float(f) => Some(f), + Primitive::Int(i, sign) => Some(Numeric::Int(i, sign)), + Primitive::Float(f) => Some(Numeric::Float(f)), + Primitive::Pointer(_) => None, + } + } + + pub fn complex_float(&self, cx: &C) -> Option + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.complex_number(cx) { + Some(Numeric::Float(f)) => Some(f), _ => None, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 589c5a5cb1229..75b8a606d66f7 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1445,6 +1445,31 @@ impl Float { } } +/// Numeric primitives. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(StableHash))] +pub enum Numeric { + /// The `bool` is the signedness of the `Integer` type. + Int(Integer, bool), + Float(Float), +} + +impl Numeric { + pub fn size(self) -> Size { + match self { + Numeric::Int(integer, _) => integer.size(), + Numeric::Float(float) => float.size(), + } + } + + pub fn reg_kind(self) -> RegKind { + match self { + Numeric::Int(_, _) => RegKind::Integer, + Numeric::Float(_) => RegKind::Float, + } + } +} + /// Fundamental unit of memory access and layout. #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(StableHash))] From 5f3d7b06828d05716cdc3b0d5320dc20ba8ad819 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Jul 2026 23:43:42 +0200 Subject: [PATCH 07/25] _Complex for sparc64 --- tests/codegen-llvm/complex-abi.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 78806982412ee..763bbe39fa8a0 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -47,11 +47,13 @@ //@ [LOONGARCH32] compile-flags: --target loongarch32-unknown-none //@ [LOONGARCH32] needs-llvm-components: loongarch +//@ revisions: SPARC64 +//@ [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu +//@ [SPARC64] needs-llvm-components: sparc + // FIXME: the below revisions are deliberately disabled for now. -// revisions: SPARC64 SPARC -// [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu -// [SPARC64] needs-llvm-components: sparc +// revisions: SPARC // [SPARC] compile-flags: --target sparc-unknown-linux-gnu // [SPARC] needs-llvm-components: sparc @@ -145,7 +147,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) + // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) @@ -179,7 +181,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // RISCV32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) + // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) @@ -329,7 +331,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) + // SPARC64: define { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) From d0ae725b786e9050ee0e18948bc269f918cf7d3f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 25 Jul 2026 22:42:32 +0200 Subject: [PATCH 08/25] _Complex for sparc --- compiler/rustc_target/src/callconv/sparc.rs | 43 +++++++++++++++++---- tests/codegen-llvm/complex-abi.rs | 21 +++++----- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index d424214aa497e..9dc5bfac3c1ba 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,16 +1,41 @@ -use rustc_abi::{HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, RegKind, Size, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg, Uniform}; -fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, offset: &mut Size) where + Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(32); - } else { + if ret.layout.is_complex() { + let component = ret.layout.field(cx, 0); + + let (reg, is_long_double) = match component.backend_repr { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Int(..) => (Reg { kind: RegKind::Integer, size: component.size }, false), + Primitive::Float(float) => { + (Reg { kind: RegKind::Float, size: component.size }, float == Float::F128) + } + Primitive::Pointer(_) => { + unreachable!("complex component cannot be a pointer") + } + }, + _ => unreachable!("complex component must be scalar"), + }; + + let mut cast = CastTarget::pair(reg, reg); + + // long double _Complex is special in that it should be marked as inreg. + // See Clang `SparcV8ABIInfo::classifyReturnType`. + if is_long_double { + cast.attrs.set(ArgAttribute::InReg); + } + ret.cast_to(cast); + } else if ret.layout.is_aggregate() { ret.make_indirect(); *offset += cx.data_layout().pointer_size(); + } else { + ret.extend_integer_width_to(32); } } @@ -33,7 +58,11 @@ where let size = arg.layout.size; let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); - if arg.layout.is_aggregate() { + if arg.layout.is_complex() { + // Like clang, `_Complex` is passed on the stack, occupying `size` bytes there (rather than + // the single pointer-sized slot that `make_indirect` arguments use). + arg.pass_by_stack_offset(None); + } else if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 763bbe39fa8a0..f31377906aa97 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -47,16 +47,14 @@ //@ [LOONGARCH32] compile-flags: --target loongarch32-unknown-none //@ [LOONGARCH32] needs-llvm-components: loongarch -//@ revisions: SPARC64 +//@ revisions: SPARC64 SPARC //@ [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu //@ [SPARC64] needs-llvm-components: sparc +//@ [SPARC] compile-flags: --target sparc-unknown-linux-gnu +//@ [SPARC] needs-llvm-components: sparc // FIXME: the below revisions are deliberately disabled for now. -// revisions: SPARC -// [SPARC] compile-flags: --target sparc-unknown-linux-gnu -// [SPARC] needs-llvm-components: sparc - // revisions: S390X // [S390X] compile-flags: --target s390x-unknown-linux-gnu // [S390X] needs-llvm-components: systemz @@ -148,7 +146,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) - // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) + // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -182,7 +180,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) - // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) + // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) @@ -197,6 +195,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { pub extern "C" fn cplx_f128(x: Complex) -> Complex { // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} byval([32 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) @@ -230,7 +229,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i8(i64{{.*}}) - // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) // WIN32_GNU: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) @@ -264,7 +263,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i16(i64{{.*}}) - // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) // WIN32_GNU: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) @@ -298,7 +297,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -332,7 +331,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) // SPARC64: define { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) From 72ee52d56d97383b8a0aeed27924c7f12d27264a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 25 Jul 2026 22:53:29 +0200 Subject: [PATCH 09/25] _Complex for s390x --- compiler/rustc_target/src/callconv/s390x.rs | 5 +++++ tests/codegen-llvm/complex-abi.rs | 22 ++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index 581c1e2e862c5..f0d9675de34f4 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -42,6 +42,11 @@ where return; } + if arg.layout.is_complex_number(cx) { + arg.make_indirect(); + return; + } + let size = arg.layout.size; if size.bits() <= 128 { if let BackendRepr::SimdVector { .. } = arg.layout.backend_repr { diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index f31377906aa97..fac9b0dc56bed 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -53,11 +53,11 @@ //@ [SPARC] compile-flags: --target sparc-unknown-linux-gnu //@ [SPARC] needs-llvm-components: sparc -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: S390X +//@ [S390X] compile-flags: --target s390x-unknown-linux-gnu +//@ [S390X] needs-llvm-components: systemz -// revisions: S390X -// [S390X] compile-flags: --target s390x-unknown-linux-gnu -// [S390X] needs-llvm-components: systemz +// FIXME: the below revisions are deliberately disabled for now. // revisions: POWERPC POWERPC64LE POWERPC64 AIX // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu @@ -114,7 +114,7 @@ pub extern "C" fn cplx_f16(x: Complex) -> Complex { // NVPTX: define{{.*}} { half, half } @cplx_f16(ptr {{.*}} byval({ half, half }) {{.*}}) // RISCV32: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) // RISCV64: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) - // S390X: define{{.*}} void @cplx_f16(ptr {{.*}} sret({ half, half }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_f16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i32 @cplx_f16(i32 {{.*}}) @@ -144,7 +144,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) - // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) @@ -178,7 +178,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) // RISCV32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) - // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) @@ -227,7 +227,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) - // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) @@ -261,7 +261,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) - // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) @@ -295,7 +295,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) // RISCV32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) @@ -329,7 +329,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // POWERPC: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) - // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) From 2f17599c53f77211c3b63dae06a69b823e22e837 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 25 Jul 2026 23:09:55 +0200 Subject: [PATCH 10/25] _Complex for wasm rust does not emit `byval` here (also not for normal structs) while clang does. --- tests/codegen-llvm/complex-abi.rs | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index fac9b0dc56bed..dfe4a3e690023 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -57,6 +57,12 @@ //@ [S390X] compile-flags: --target s390x-unknown-linux-gnu //@ [S390X] needs-llvm-components: systemz +//@ revisions: WASM32 WASM64 +//@ [WASM32] compile-flags: --target wasm32-unknown-unknown +//@ [WASM32] needs-llvm-components: webassembly +//@ [WASM64] compile-flags: --target wasm64-unknown-unknown +//@ [WASM64] needs-llvm-components: webassembly + // FIXME: the below revisions are deliberately disabled for now. // revisions: POWERPC POWERPC64LE POWERPC64 AIX @@ -75,12 +81,6 @@ // [MIPS] compile-flags: --target mips-unknown-linux-gnu // [MIPS] needs-llvm-components: mips -// revisions: WASM32 WASM64 -// [WASM32] compile-flags: --target wasm32-unknown-unknown -// [WASM32] needs-llvm-components: webassembly -// [WASM64] compile-flags: --target wasm64-unknown-unknown -// [WASM64] needs-llvm-components: webassembly - // revisions: CSKY // [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 // [CSKY] needs-llvm-components: csky @@ -147,8 +147,8 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) + // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i64 @cplx_f32(i64 {{.*}}) @@ -181,8 +181,8 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval([16 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) + // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) @@ -196,8 +196,8 @@ pub extern "C" fn cplx_f128(x: Complex) -> Complex { // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) // SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} byval([32 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) + // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // X86_64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) @@ -230,8 +230,8 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i16 @cplx_i8(i16 {{.*}}) @@ -264,8 +264,8 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i32 @cplx_i16(i32 {{.*}}) @@ -298,8 +298,8 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i64 @cplx_i32(i64 {{.*}}) @@ -332,8 +332,8 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) From cb7c8a2cbea31e019222253ab8186a849f72c429 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 16:46:15 +0200 Subject: [PATCH 11/25] _Complex for csky --- tests/codegen-llvm/complex-abi.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index dfe4a3e690023..adcacccb183d1 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -63,6 +63,10 @@ //@ [WASM64] compile-flags: --target wasm64-unknown-unknown //@ [WASM64] needs-llvm-components: webassembly +//@ revisions: CSKY +//@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 +//@ [CSKY] needs-llvm-components: csky + // FIXME: the below revisions are deliberately disabled for now. // revisions: POWERPC POWERPC64LE POWERPC64 AIX @@ -81,10 +85,6 @@ // [MIPS] compile-flags: --target mips-unknown-linux-gnu // [MIPS] needs-llvm-components: mips -// revisions: CSKY -// [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 -// [CSKY] needs-llvm-components: csky - // revisions: NVPTX // [NVPTX] compile-flags: --target nvptx64-nvidia-cuda // [NVPTX] needs-llvm-components: nvptx @@ -137,7 +137,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // MIPS: define{{.*}} { float, float } @cplx_f32(i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) @@ -166,7 +166,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // ARM64EC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // ARM: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // BPF: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [2 x i64] {{.*}}) - // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [4 x i32] {{.*}}) + // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) @@ -317,7 +317,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // ARM64EC: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // ARM: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, [2 x i64] {{.*}}) // BPF: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) - // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [4 x i32] {{.*}}) + // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) From 4cd8e50601529edd8e5a6898fd607782916182d8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 14:02:45 +0200 Subject: [PATCH 12/25] _Complex for mips64 --- compiler/rustc_target/src/callconv/mips64.rs | 31 ++++++++++++++++++- tests/codegen-llvm/complex-abi.rs | 32 ++++++++++++++------ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index a9d5ec958889f..b6452794cb001 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -1,6 +1,6 @@ use arrayvec::ArrayVec; use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, + BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, }; use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; @@ -55,6 +55,21 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { + if ret.layout.is_complex() { + if !ret.layout.is_complex_float() && size <= cx.data_layout().pointer_size() { + // Return a Complex<{integer}> packed into a single register when that fits. + // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. + ret.cast_to(Reg { kind: RegKind::Integer, size }); + } else { + // Otherwise pass in 2 registers. + let kind = + if ret.layout.is_complex_float() { RegKind::Float } else { RegKind::Integer }; + let reg = Reg { kind, size: ret.layout.field(cx, 0).size }; + ret.cast_to(CastTarget::pair(reg, reg)); + } + return; + } + // Unlike other architectures which return aggregates in registers, MIPS n64 limits the // use of float registers to structures (not unions) containing exactly one or two // float fields. @@ -102,6 +117,20 @@ where extend_integer_width_mips(arg, 64); } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + } else if arg.layout.is_complex_float() && size > dl.pointer_size() * 2 { + // Complex is passed in 4 GPRs, but aligned to 16 so may need padding. + let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size }; + arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32); + } else if arg.layout.is_complex() && size <= dl.pointer_size() * 2 { + if arg.layout.is_complex_float() || size > dl.pointer_size() { + // Complex and Complex<{float}> are passed as 2 separate arguments, which is what + // the default `PassMode::Pair` already does. + } else { + // Cast Complex into i16, Complex to i32, etc. The inreg attribute is required + // to make the bits land in the right (upper) bits on big endian targets. + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); + arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); + } } else { match arg.layout.fields { FieldsShape::Primitive => unreachable!(), diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index adcacccb183d1..8611ae8f0ac55 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -67,8 +67,20 @@ //@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 //@ [CSKY] needs-llvm-components: csky +// NOTE: for Mips we follow the GCC ABI, not the Clang ABI. +// See https://github.com/llvm/llvm-project/issues/212109. +//@ revisions: MIPS64 MIPS64EL +//@ [MIPS64] compile-flags: --target mips64-unknown-linux-gnuabi64 +//@ [MIPS64] needs-llvm-components: mips +//@ [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 +//@ [MIPS64EL] needs-llvm-components: mips + // FIXME: the below revisions are deliberately disabled for now. +// revisions: MIPS +// [MIPS] compile-flags: --target mips-unknown-linux-gnu +// [MIPS] needs-llvm-components: mips + // revisions: POWERPC POWERPC64LE POWERPC64 AIX // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu // [POWERPC] needs-llvm-components: powerpc @@ -79,12 +91,6 @@ // [AIX] compile-flags: --target powerpc64-ibm-aix // [AIX] needs-llvm-components: powerpc -// revisions: MIPS64EL MIPS -// [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 -// [MIPS64EL] needs-llvm-components: mips -// [MIPS] compile-flags: --target mips-unknown-linux-gnu -// [MIPS] needs-llvm-components: mips - // revisions: NVPTX // [NVPTX] compile-flags: --target nvptx64-nvidia-cuda // [NVPTX] needs-llvm-components: nvptx @@ -136,6 +142,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // I686: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // MIPS64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) @@ -170,6 +177,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // MIPS64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) @@ -195,6 +203,8 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { pub extern "C" fn cplx_f128(x: Complex) -> Complex { // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // MIPS64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1) + // MIPS64EL: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1) // SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} byval([32 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) @@ -219,7 +229,8 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) - // MIPS64EL: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) + // MIPS64: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) + // MIPS64EL: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) // MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) @@ -253,7 +264,8 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // I686: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) - // MIPS64EL: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) + // MIPS64: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) + // MIPS64EL: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) // MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) @@ -287,7 +299,8 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // I686: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // LOONGARCH64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // MIPS64EL: define{{.*}} { i32, i32 } @cplx_i32(i64 {{.*}}) + // MIPS64: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) + // MIPS64EL: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) // MIPS: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) @@ -321,6 +334,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) From 74f8b4dbc5df2250e85ce2022d148c5d17338ae6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 17:12:50 +0200 Subject: [PATCH 13/25] _Complex for mips --- compiler/rustc_target/src/callconv/mips.rs | 34 +++++++++++++++++----- tests/codegen-llvm/complex-abi.rs | 26 +++++++++++------ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index d2572cc035c1c..828362795bd91 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -1,16 +1,36 @@ -use rustc_abi::{HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{Float, HasDataLayout, Integer, Numeric, Reg, RegKind, Size, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform}; -fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, offset: &mut Size) where + Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(32); - } else { + let dl = cx.data_layout(); + let size = ret.layout.size; + + if let Some(component) = ret.layout.complex_number(cx) { + match component { + Numeric::Float(Float::F128) => { + // Same as an aggregate. + ret.make_indirect(); + *offset += dl.pointer_size(); + } + Numeric::Int(Integer::I8 | Integer::I16, _) => { + // Pack Complex<{integer}> into a single register if that fits. + ret.cast_to(Reg { kind: RegKind::Integer, size }); + } + _ => { + let reg = Reg { kind: component.reg_kind(), size: ret.layout.field(cx, 0).size }; + ret.cast_to(CastTarget::pair(reg, reg)); + } + } + } else if ret.layout.is_aggregate() { ret.make_indirect(); - *offset += cx.data_layout().pointer_size(); + *offset += dl.pointer_size(); + } else { + ret.extend_integer_width_to(32); } } diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 8611ae8f0ac55..f277f37c6bba4 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -75,11 +75,13 @@ //@ [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 //@ [MIPS64EL] needs-llvm-components: mips -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: MIPS MIPSEL +//@ [MIPS] compile-flags: --target mips-unknown-linux-gnu +//@ [MIPS] needs-llvm-components: mips +//@ [MIPSEL] compile-flags: --target mipsel-unknown-linux-gnu +//@ [MIPSEL] needs-llvm-components: mips -// revisions: MIPS -// [MIPS] compile-flags: --target mips-unknown-linux-gnu -// [MIPS] needs-llvm-components: mips +// FIXME: the below revisions are deliberately disabled for now. // revisions: POWERPC POWERPC64LE POWERPC64 AIX // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu @@ -145,6 +147,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // MIPS64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) + // MIPSEL: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) @@ -179,7 +182,8 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // MIPS64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { double, double } @cplx_f64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { double, double } @cplx_f64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // POWERPC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) @@ -231,7 +235,8 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // MIPS64: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) // MIPS64EL: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) - // MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) + // MIPS: define{{.*}} i16 @cplx_i8(i32 {{.*}}) + // MIPSEL: define{{.*}} i16 @cplx_i8(i32 {{.*}}) // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) @@ -266,7 +271,8 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // MIPS64: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) // MIPS64EL: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) - // MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) + // MIPS: define{{.*}} i32 @cplx_i16(i32 {{.*}}) + // MIPSEL: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) // POWERPC64LE: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) @@ -301,7 +307,8 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // LOONGARCH64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // MIPS64: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) // MIPS64EL: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) - // MIPS: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { i32, i32 } @cplx_i32([2 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i32, i32 } @cplx_i32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) @@ -336,7 +343,8 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // MIPS64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { i64, i64 } @cplx_i64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i64, i64 } @cplx_i64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) From 53fd52c0881917a110f07fe28622aedee0fc816b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 20:54:37 +0200 Subject: [PATCH 14/25] add placeholder for amdgpu and a note on llvm 23 changes for bpf --- tests/codegen-llvm/complex-abi.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index f277f37c6bba4..14a0bd6c4cd7a 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -97,6 +97,12 @@ // [NVPTX] compile-flags: --target nvptx64-nvidia-cuda // [NVPTX] needs-llvm-components: nvptx +// revisions: AMDGPU +// [AMDGPU] compile-flags: --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +// [AMDGPU] needs-llvm-components: amdgpu + +// NOTE: BPF cannot codegen sret, which the larger Complex<..> types use. +// LLVM 23 adds support, see https://github.com/llvm/llvm-project/pull/206876. // revisions: BPF // [BPF] compile-flags: --target bpfel-unknown-none // [BPF] needs-llvm-components: bpf From 2c84a4864efc62da4294e33135e74a5adb1789fa Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 20:55:58 +0200 Subject: [PATCH 15/25] _Complex for powerpc --- compiler/rustc_target/src/callconv/powerpc.rs | 31 ++++++++++++++---- .../rustc_target/src/callconv/powerpc64.rs | 9 +++++- tests/codegen-llvm/complex-abi.rs | 32 +++++++++---------- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 2b6a104e1221d..386a9357d8202 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,10 +1,25 @@ -use rustc_abi::TyAbiInterface; +use rustc_abi::{Reg, RegKind, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi, Uniform}; use crate::spec::{Env, HasTargetSpec, Os}; -fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - if ret.layout.is_aggregate() { +fn classify_complex(arg: &mut ArgAbi<'_, Ty>) { + // NOTE: we follow GCC, not Clang here, see https://github.com/llvm/llvm-project/pull/208917. + let size = arg.layout.size; + if size.bytes() <= 4 { + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } else { + arg.cast_to(Uniform::new(Reg::i32(), size)); + } +} + +fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if ret.layout.is_complex() { + classify_complex(ret); + } else if ret.layout.is_aggregate() { ret.make_indirect(); } else { ret.extend_integer_width_to(32); @@ -25,7 +40,11 @@ where } return; } - if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() { + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + arg.make_indirect(); + } else if arg.layout.is_complex() { + classify_complex(arg); + } else if arg.layout.is_aggregate() { arg.make_indirect(); } else { arg.extend_integer_width_to(32); @@ -37,7 +56,7 @@ where Ty: TyAbiInterface<'a, C> + Copy, { if !fn_abi.ret.is_ignore() { - classify_ret(&mut fn_abi.ret); + classify_ret(cx, &mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 3eb40abe90f33..7d017d5a19174 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -4,7 +4,7 @@ use rustc_abi::{HasDataLayout, TyAbiInterface}; -use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::callconv::{Align, ArgAbi, CastTarget, FnAbi, Reg, RegKind, Uniform}; use crate::spec::{HasTargetSpec, LlvmAbi, Os}; #[derive(Debug, Clone, Copy, PartialEq)] @@ -60,6 +60,13 @@ where arg.extend_integer_width_to(64); return; } + if let Some(component) = arg.layout.complex_number(cx) { + if is_ret { + let reg = Reg { kind: component.reg_kind(), size: component.size() }; + arg.cast_to(CastTarget::pair(reg, reg)); + } + return; + } // The AIX ABI expect byval for aggregates // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp. diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 14a0bd6c4cd7a..340b17812f926 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -81,17 +81,17 @@ //@ [MIPSEL] compile-flags: --target mipsel-unknown-linux-gnu //@ [MIPSEL] needs-llvm-components: mips -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: POWERPC POWERPC64LE POWERPC64 AIX +//@ [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu +//@ [POWERPC] needs-llvm-components: powerpc +//@ [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu +//@ [POWERPC64LE] needs-llvm-components: powerpc +//@ [POWERPC64] compile-flags: --target powerpc64-unknown-linux-gnu +//@ [POWERPC64] needs-llvm-components: powerpc +//@ [AIX] compile-flags: --target powerpc64-ibm-aix +//@ [AIX] needs-llvm-components: powerpc -// revisions: POWERPC POWERPC64LE POWERPC64 AIX -// [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu -// [POWERPC] needs-llvm-components: powerpc -// [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu -// [POWERPC64LE] needs-llvm-components: powerpc -// [POWERPC64] compile-flags: --target powerpc64-unknown-linux-gnu -// [POWERPC64] needs-llvm-components: powerpc -// [AIX] compile-flags: --target powerpc64-ibm-aix -// [AIX] needs-llvm-components: powerpc +// FIXME: the below revisions are deliberately disabled for now. // revisions: NVPTX // [NVPTX] compile-flags: --target nvptx64-nvidia-cuda @@ -157,7 +157,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // POWERPC: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) + // POWERPC: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) @@ -193,7 +193,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // POWERPC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // POWERPC: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @cplx_f64([4 x i32] {{.*}}) // RISCV32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) @@ -246,7 +246,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // POWERPC: define{{.*}} i16 @cplx_i8(i16 {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) @@ -282,7 +282,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) // POWERPC64LE: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // POWERPC: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) @@ -318,7 +318,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // POWERPC: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // RISCV32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) @@ -354,7 +354,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @cplx_i64([4 x i32] {{.*}}) // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) From ca7429e729c086a5160db622151fb502efd0835d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 27 Jul 2026 23:42:45 +0200 Subject: [PATCH 16/25] make SPARC64 `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/sparc64.rs | 11 +++++++++-- tests/codegen-llvm/complex-abi.rs | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 6b19f8ebd76ce..c67fd3ccf0e92 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,7 +1,7 @@ use arrayvec::ArrayVec; use rustc_abi::{ - Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, - TyAndLayout, Variants, + Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, RegKind, Size, + TyAbiInterface, TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; @@ -143,6 +143,13 @@ fn classify_arg<'a, Ty, C>( *total_double_word_count = start_double_word_count + double_word_count; + // Clang treats `_Complex` like a struct, GCC like a big scalar. That changes how the bits get + // packed. We follow GCC here. See also https://github.com/llvm/llvm-project/pull/212340. + if arg.layout.is_complex() && !arg.layout.is_complex_float() && total < Size::from_bytes(8) { + arg.cast_to(Reg { kind: RegKind::Integer, size: total }); + return; + } + const ARGUMENT_REGISTERS: usize = 8; let mut double_words = [DoubleWord::Words([Word::Integer; 2]); ARGUMENT_REGISTERS / 2]; diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 340b17812f926..99bb41c18a6a5 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -250,7 +250,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // SPARC64: define{{.*}} i16 @cplx_i8(i16{{.*}}) // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) @@ -286,7 +286,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // SPARC64: define{{.*}} i32 @cplx_i16(i32{{.*}}) // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) From 483ecfdfffdb64b2024695040352914a92627ddc Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 13 Aug 2026 15:36:09 +0200 Subject: [PATCH 17/25] update for sparc64 --- compiler/rustc_target/src/callconv/sparc64.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index c67fd3ccf0e92..c95add6af4bd9 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,7 +1,7 @@ use arrayvec::ArrayVec; use rustc_abi::{ - Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, RegKind, Size, - TyAbiInterface, TyAndLayout, Variants, + Align, BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg, + RegKind, Size, TyAbiInterface, TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; @@ -145,7 +145,7 @@ fn classify_arg<'a, Ty, C>( // Clang treats `_Complex` like a struct, GCC like a big scalar. That changes how the bits get // packed. We follow GCC here. See also https://github.com/llvm/llvm-project/pull/212340. - if arg.layout.is_complex() && !arg.layout.is_complex_float() && total < Size::from_bytes(8) { + if let Some(Numeric::Int(Integer::I8 | Integer::I16, _)) = arg.layout.complex_number(cx) { arg.cast_to(Reg { kind: RegKind::Integer, size: total }); return; } From fb6eaa11e41f4a54aa0420449c30735ec98ce116 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 27 Jul 2026 23:53:41 +0200 Subject: [PATCH 18/25] make SPARC `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/sparc.rs | 11 ++++++++--- tests/codegen-llvm/complex-abi.rs | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 9dc5bfac3c1ba..2836b99e1c694 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -23,12 +23,15 @@ where _ => unreachable!("complex component must be scalar"), }; + let size = ret.layout.size; let mut cast = CastTarget::pair(reg, reg); // long double _Complex is special in that it should be marked as inreg. // See Clang `SparcV8ABIInfo::classifyReturnType`. if is_long_double { cast.attrs.set(ArgAttribute::InReg); + } else if !ret.layout.is_complex_float() && size <= Size::from_bytes(8) { + cast = CastTarget::from(Reg { kind: RegKind::Integer, size }); } ret.cast_to(cast); } else if ret.layout.is_aggregate() { @@ -59,9 +62,11 @@ where let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_complex() { - // Like clang, `_Complex` is passed on the stack, occupying `size` bytes there (rather than - // the single pointer-sized slot that `make_indirect` arguments use). - arg.pass_by_stack_offset(None); + if !arg.layout.is_complex_float() && size <= Size::from_bytes(8) { + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } else { + arg.pass_by_stack_offset(None); + } } else if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 99bb41c18a6a5..3333076d2ec65 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -251,7 +251,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i16 @cplx_i8(i16{{.*}}) - // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) + // SPARC: define{{.*}} i16 @cplx_i8(i16{{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) @@ -287,7 +287,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i32 @cplx_i16(i32{{.*}}) - // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) + // SPARC: define{{.*}} i32 @cplx_i16(i32{{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) @@ -323,7 +323,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) + // SPARC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) From d439e80c73cb6003e18c960b251d700b28b8fdce Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 13 Aug 2026 15:35:59 +0200 Subject: [PATCH 19/25] update for sparc --- compiler/rustc_target/src/callconv/sparc.rs | 48 +++++++++------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 2836b99e1c694..45086ef844b0f 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,4 +1,6 @@ -use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, RegKind, Size, TyAbiInterface}; +use rustc_abi::{ + BackendRepr, Float, HasDataLayout, Integer, Numeric, Primitive, RegKind, TyAbiInterface, +}; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg, Uniform}; @@ -7,32 +9,24 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if ret.layout.is_complex() { - let component = ret.layout.field(cx, 0); - - let (reg, is_long_double) = match component.backend_repr { - BackendRepr::Scalar(scalar) => match scalar.primitive() { - Primitive::Int(..) => (Reg { kind: RegKind::Integer, size: component.size }, false), - Primitive::Float(float) => { - (Reg { kind: RegKind::Float, size: component.size }, float == Float::F128) - } - Primitive::Pointer(_) => { - unreachable!("complex component cannot be a pointer") - } - }, - _ => unreachable!("complex component must be scalar"), - }; - - let size = ret.layout.size; + if let Some(component) = ret.layout.complex_number(cx) { + let reg = Reg { kind: component.reg_kind(), size: component.size() }; let mut cast = CastTarget::pair(reg, reg); - // long double _Complex is special in that it should be marked as inreg. - // See Clang `SparcV8ABIInfo::classifyReturnType`. - if is_long_double { - cast.attrs.set(ArgAttribute::InReg); - } else if !ret.layout.is_complex_float() && size <= Size::from_bytes(8) { - cast = CastTarget::from(Reg { kind: RegKind::Integer, size }); + match component { + Numeric::Float(Float::F128) => { + // long double _Complex is special in that it should be marked as inreg. + // See Clang `SparcV8ABIInfo::classifyReturnType`. + cast.attrs.set(ArgAttribute::InReg); + } + Numeric::Float(Float::F16) + | Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + let size = ret.layout.size; + cast = CastTarget::from(Reg { kind: RegKind::Integer, size }); + } + _ => { /* default behavior */ } } + ret.cast_to(cast); } else if ret.layout.is_aggregate() { ret.make_indirect(); @@ -61,9 +55,9 @@ where let size = arg.layout.size; let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); - if arg.layout.is_complex() { - if !arg.layout.is_complex_float() && size <= Size::from_bytes(8) { - arg.cast_to(Reg { kind: RegKind::Integer, size }); + if let Some(component) = arg.layout.complex_number(cx) { + if let Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) = component { + arg.cast_to(Reg { kind: RegKind::Integer, size: 2 * component.size() }); } else { arg.pass_by_stack_offset(None); } From 32c9a04fb6f4194701c64e75caf35fdeb31486da Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 29 Jul 2026 20:55:22 +0200 Subject: [PATCH 20/25] make mips64 `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/mips64.rs | 37 ++++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index b6452794cb001..c71e2a02c1c9c 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -1,10 +1,13 @@ use arrayvec::ArrayVec; use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, + BackendRepr, FieldsShape, Float, HasDataLayout, Numeric, Primitive, Reg, RegKind, Size, + TyAbiInterface, }; use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; +const NUM_ARG_SLOTS: u64 = 8; + fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr @@ -55,16 +58,14 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { - if ret.layout.is_complex() { - if !ret.layout.is_complex_float() && size <= cx.data_layout().pointer_size() { + if let Some(component) = ret.layout.complex_number(cx) { + if matches!(component, Numeric::Int(..)) && size <= cx.data_layout().pointer_size() { // Return a Complex<{integer}> packed into a single register when that fits. // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. ret.cast_to(Reg { kind: RegKind::Integer, size }); } else { // Otherwise pass in 2 registers. - let kind = - if ret.layout.is_complex_float() { RegKind::Float } else { RegKind::Integer }; - let reg = Reg { kind, size: ret.layout.field(cx, 0).size }; + let reg = Reg { kind: component.reg_kind(), size: component.size() }; ret.cast_to(CastTarget::pair(reg, reg)); } return; @@ -117,14 +118,28 @@ where extend_integer_width_mips(arg, 64); } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); - } else if arg.layout.is_complex_float() && size > dl.pointer_size() * 2 { + } else if arg.layout.complex_float(cx).is_some() && size > dl.pointer_size() * 2 { // Complex is passed in 4 GPRs, but aligned to 16 so may need padding. let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size }; arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32); - } else if arg.layout.is_complex() && size <= dl.pointer_size() * 2 { - if arg.layout.is_complex_float() || size > dl.pointer_size() { - // Complex and Complex<{float}> are passed as 2 separate arguments, which is what - // the default `PassMode::Pair` already does. + } else if arg.layout.is_complex_number(cx) && size <= dl.pointer_size() * 2 { + let slot = dl.pointer_size(); + let curr_offset = offset.align_to(align); + + if arg.layout.complex_float(cx).is_some() { + // Only pass a Complex/Complex in FPRs when two argument slots are free, + // otherwise pack it into GPRs (or the stack) like an integer of the same size. + if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { + // The default `PassMode::Pair` already passes one component per register. Both + // components claim a slot, even for a Complex which could fit into one slot. + *offset = curr_offset + slot * 2; + return; + } + + arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); + } else if size > slot { + // Complex is passed as 2 separate arguments, which is what the default + // `PassMode::Pair` already does. } else { // Cast Complex into i16, Complex to i32, etc. The inreg attribute is required // to make the bits land in the right (upper) bits on big endian targets. From b5d550cfbaf3ac24851b64039f2650fc24428bd4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 29 Jul 2026 11:10:21 +0200 Subject: [PATCH 21/25] make `pad_i32` of `PassMode::cast` an integer so that we can specify more than one i32 of padding. --- .../rustc_codegen_cranelift/src/abi/pass_mode.rs | 2 +- compiler/rustc_codegen_gcc/src/abi.rs | 8 ++++---- compiler/rustc_codegen_gcc/src/type_of.rs | 4 ++-- compiler/rustc_codegen_llvm/src/abi.rs | 14 ++++++++------ compiler/rustc_codegen_ssa/src/mir/block.rs | 5 +++-- compiler/rustc_codegen_ssa/src/mir/mod.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 +- compiler/rustc_public/src/abi.rs | 2 +- compiler/rustc_target/src/callconv/mips.rs | 2 +- compiler/rustc_target/src/callconv/mips64.rs | 2 +- compiler/rustc_target/src/callconv/mod.rs | 12 ++++++------ compiler/rustc_target/src/callconv/sparc.rs | 2 +- compiler/rustc_target/src/callconv/sparc64.rs | 2 +- tests/ui/abi/pass-indirectly-attr.stderr | 2 +- 14 files changed, 35 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index c4d4ddcf6b753..f5500169be254 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -123,7 +123,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Cast { ref cast, pad_i32 } => { - assert!(!pad_i32, "padding support not yet implemented"); + assert_eq!(pad_i32, 0, "padding support not yet implemented"); cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 1b7bb8c907735..0ce8eba510336 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -169,10 +169,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { continue; } PassMode::Cast { ref cast, pad_i32 } => { - // add padding - if pad_i32 { - argument_tys.push(Reg::i32().gcc_type(cx)); - } + // Add padding. + argument_tys + .extend(std::iter::repeat_n(Reg::i32().gcc_type(cx), usize::from(pad_i32))); + let ty = cast.gcc_type(cx); apply_attrs(ty, &cast.attrs, argument_tys.len()) } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index c6c32236ab49f..53192c0a087e4 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -346,8 +346,8 @@ impl<'gcc, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn_abi.ptr_to_gcc_type(self) } - fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> { - unimplemented!(); + fn reg_backend_type(&self, ty: &Reg) -> Type<'gcc> { + ty.gcc_type(self) } fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 65bb32ee666f2..f4fcfec5f1ced 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -406,10 +406,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), PassMode::Cast { cast, pad_i32 } => { - // add padding - if *pad_i32 { - llargument_tys.push(Reg::i32().llvm_type(cx)); - } + // Add padding. + llargument_tys.extend(std::iter::repeat_n( + Reg::i32().llvm_type(cx), + usize::from(*pad_i32), + )); + // Compute the LLVM type we use for this function from the cast type. // We assume here that ABI-compatible Rust types have the same cast type. cast.llvm_type(cx) @@ -581,7 +583,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + for _ in 0..*pad_i32 { apply(&ArgAttributes::new()); } apply(&cast.attrs); @@ -667,7 +669,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply(bx.cx, b); } PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + for _ in 0..*pad_i32 { apply(bx.cx, &ArgAttributes::new()); } apply(bx.cx, &cast.attrs); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 7f907bc630b2f..2d07647f4361b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1936,9 +1936,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) { match arg.mode { PassMode::Ignore => return, - PassMode::Cast { pad_i32: true, .. } => { + PassMode::Cast { pad_i32, .. } => { // Fill padding with undef value, where applicable. - llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32()))); + let undef = bx.const_undef(bx.reg_backend_type(&Reg::i32())); + llargs.extend(std::iter::repeat_n(undef, usize::from(pad_i32))); } PassMode::Pair(..) => match op.val { Pair(a, b) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index f8f4f09f75825..c7d615308e6e4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -501,8 +501,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for i in 0..tupled_arg_tys.len() { let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32, .. } = arg.mode { + llarg_idx += usize::from(pad_i32); } let pr_field = place.project_field(bx, i); bx.store_fn_arg(arg, &mut llarg_idx, pr_field); @@ -529,8 +529,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32, .. } = arg.mode { + llarg_idx += usize::from(pad_i32); } if !memory_locals.contains(local) { diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 33cc321ea6d32..d51565f45a3ba 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -474,7 +474,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t }, PassMode::Cast { pad_i32, ref cast } => { // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` - assert!(!pad_i32, "not currently used by wasm calling convention"); + assert_eq!(pad_i32, 0, "not currently used by wasm calling convention"); assert!(cast.prefix.is_empty(), "no prefix"); assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 02674e4107c77..054867c0cb4a2 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -55,7 +55,7 @@ pub enum PassMode { /// The argument has a layout abi of `ScalarPair`. Pair(Opaque, Opaque), /// Pass the argument after casting it. - Cast { pad_i32: bool, cast: Opaque }, + Cast { pad_i32: u8, cast: Opaque }, /// Pass the argument indirectly via a hidden pointer. Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, } diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 828362795bd91..8b5be8dd3daec 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -55,7 +55,7 @@ where let size = arg.layout.size; if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index c71e2a02c1c9c..ac059547d45b5 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -112,7 +112,7 @@ where // Detect need for padding let align = Ord::clamp(arg.layout.align.abi, dl.i64_align, dl.i128_align); - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); if !arg.layout.is_aggregate() { extend_integer_width_mips(arg, 64); diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index a06a6a0a69e12..22595b9ad24d5 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -55,8 +55,9 @@ pub enum PassMode { Pair(ArgAttributes, ArgAttributes), /// Pass the argument after casting it. See the `CastTarget` docs for details. /// - /// `pad_i32` indicates if a `Reg::i32()` dummy argument is emitted before the real argument. - Cast { pad_i32: bool, cast: Box }, + /// `pad_i32` indicates how many `Reg::i32()` dummy arguments are emitted before the real + /// argument. + Cast { pad_i32: u8, cast: Box }, /// Pass the argument indirectly via a hidden pointer. /// /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized @@ -507,12 +508,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn cast_to>(&mut self, target: T) { - self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: 0 }; } pub fn cast_to_with_attrs>(&mut self, target: T, attrs: ArgAttributes) { - self.mode = - PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; + self.mode = PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: 0 }; } /// Cast to `target`, forwarding `NoUndef` only when the layout provably has no uninit @@ -535,7 +535,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { self.cast_to_with_attrs(target, attr.into()); } - pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { + pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: u8) { self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 45086ef844b0f..27203183a513c 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -62,7 +62,7 @@ where arg.pass_by_stack_offset(None); } } else if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index c95add6af4bd9..2ea4cfae1038c 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -197,7 +197,7 @@ fn classify_arg<'a, Ty, C>( _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)), }; - arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), pad); + arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), u8::from(pad)); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 320840c8149f5..232810d7b57e0 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -121,7 +121,7 @@ error: fn_abi_of(extern_rust) = FnAbi { }, }, mode: Cast { - pad_i32: false, + pad_i32: 0, cast: CastTarget { prefix: [], rest_offset: None, From d06d0fce970180f6b3278ab89968f0105661e483 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 30 Jul 2026 00:42:44 +0200 Subject: [PATCH 22/25] make powerpc `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/powerpc.rs | 105 +++++++++++++++--- tests/codegen-llvm/complex-abi.rs | 4 +- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 386a9357d8202..5c32e68aefe36 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,24 +1,37 @@ -use rustc_abi::{Reg, RegKind, TyAbiInterface}; +use rustc_abi::{BackendRepr, Primitive, Reg, RegKind, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Uniform}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform}; use crate::spec::{Env, HasTargetSpec, Os}; -fn classify_complex(arg: &mut ArgAbi<'_, Ty>) { - // NOTE: we follow GCC, not Clang here, see https://github.com/llvm/llvm-project/pull/208917. +const NUM_ARG_GPRS: u32 = 8; // r3..=r10 + +/// How to cast `Complex` so that we match the GCC ABI. +fn complex_cast_target(arg: &ArgAbi<'_, Ty>) -> CastTarget { let size = arg.layout.size; + if size.bytes() <= 4 { - arg.cast_to(Reg { kind: RegKind::Integer, size }); + // Coerce to an integer for `Complex` and `Complex`. + CastTarget::from(Reg { kind: RegKind::Integer, size }) + } else if size.bytes() == 8 { + // Coerce to a single `i64` for `Complex` and `Complex`, which has the correct + // register alignment of 8 bytes. + // + // NOTE: clang uses a vector (e.g. <2 x f32>) here, but if we try that we run into + // ABI issues because vectors require the altivec target feature. + CastTarget::from(Reg::i64()) } else { - arg.cast_to(Uniform::new(Reg::i32(), size)); + // Coerce to an array `[N x i32]` for everything wider. An array of i32 gives the correct + // 4-byte register alignment. + CastTarget::from(Uniform::new(Reg::i32(), size)) } } -fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, { - if ret.layout.is_complex() { - classify_complex(ret); + if ret.layout.is_complex_number(cx) { + ret.cast_to(complex_cast_target(ret)); } else if ret.layout.is_aggregate() { ret.make_indirect(); } else { @@ -26,7 +39,7 @@ where } } -fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>, arg_gprs_left: &mut u32) where Ty: TyAbiInterface<'a, C> + Copy, { @@ -40,15 +53,70 @@ where } return; } - if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { - arg.make_indirect(); - } else if arg.layout.is_complex() { - classify_complex(arg); - } else if arg.layout.is_aggregate() { - arg.make_indirect(); + + let default = |arg: &mut ArgAbi<'a, Ty>| { + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(32); + } + }; + + let is_complex = arg.layout.is_complex_number(cx); + let is_float = match arg.layout.backend_repr { + BackendRepr::Scalar(scalar) => matches!(scalar.primitive(), Primitive::Float(_)), + _ => false, + }; + + // Arguments that are not relevant for the GPR budget: floats go in the FPRs, and once the GPRs + // are exhausted everything lands on the stack anyway. Complex always needs custom handling. + if (*arg_gprs_left == 0 || is_float) && !is_complex { + return default(arg); + } + + let size = arg.layout.size; + let regs_needed = size.bytes().div_ceil(4) as u32; // 32-bit registers + + if arg.layout.is_aggregate() && !is_complex { + // Non-complex aggregates are passed indirectly, and consume one GPR. + *arg_gprs_left -= 1; } else { - arg.extend_integer_width_to(32); + let mut padding = 0; + + // The powerpc ABI in GCC hardcodes a special rule for values of size 8. It remarks + // + // > V.4 wants long longs and doubles to be double word aligned. Just + // > testing the mode size is a boneheaded way to do this as it means + // > that other types such as complex int are also double word aligned. + // > However, we're stuck with this because changing the ABI might break + // > existing library interfaces. + // + // An eight-byte value must start in an even-numbered GPR. The `i64` it is coerced to + // already makes LLVM skip an odd register, so only account for it in the budget. + if size.bytes() == 8 && !arg_gprs_left.is_multiple_of(2) { + *arg_gprs_left -= 1; + } + + if regs_needed <= *arg_gprs_left { + // Everything fits, great! + *arg_gprs_left -= regs_needed; + } else if is_complex { + // Never split a Complex across the GPRs and the stack. + // + // The full complex value is passed via the stack, and the remaining GPRs are consumed, + // so all subsequent arguments will also be passed via the stack. Use the padding value + // to fill up the remaining GPRs. + padding += *arg_gprs_left; + *arg_gprs_left = 0; + } + + if is_complex { + arg.cast_to_and_pad_i32(complex_cast_target(arg), padding as u8); + return; + } } + + default(arg) } pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -59,7 +127,8 @@ where classify_ret(cx, &mut fn_abi.ret); } + let mut arg_gprs_left = NUM_ARG_GPRS; for arg in fn_abi.args.iter_mut() { - classify_arg(cx, arg); + classify_arg(cx, arg, &mut arg_gprs_left); } } diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 3333076d2ec65..e056cd1d85a7c 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -157,7 +157,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) // POWERPC64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // POWERPC: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) + // POWERPC: define{{.*}} i64 @cplx_f32(i64 {{.*}}) // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) @@ -318,7 +318,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) - // POWERPC: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) + // POWERPC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // RISCV32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) From 9eef920dfd8e4577823b3e58b2b954f85cc35f62 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 20:45:12 +0200 Subject: [PATCH 23/25] sparc: pass ZST arguments --- compiler/rustc_target/src/callconv/sparc.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 27203183a513c..265c092787b0c 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -83,6 +83,10 @@ where for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { + if arg.layout.is_zst() { + arg.make_indirect_from_ignore(); + offset += cx.data_layout().pointer_size(); + } continue; } classify_arg(cx, arg, &mut offset); From 8bd2239709549d1ed3e6cf0bc625daf89ce87acd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 21:11:17 +0200 Subject: [PATCH 24/25] sparc: pass and return `f128` indirectly C `long double` is IEEE binary128 on 32-bit SPARC, and both GCC and Clang pass it `byval` and return it through an `sret` pointer (see Clang `SparcV8ABIInfo::classifyArgumentType` / `classifyReturnType`). rustc returned `f128` directly. The 32-bit SPARC ABI marks an indirect return by placing an `unimp ` word after the call, which the callee must skip by returning to `%o7+12`; LLVM keys that off the `sret` attribute. Without it the caller emitted the marker but the callee returned to `%o7+8`, landing on the marker word and dying with SIGILL. --- compiler/rustc_target/src/callconv/sparc.rs | 17 ++++++- tests/codegen-llvm/abi-sparc-f128.rs | 50 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/abi-sparc-f128.rs diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 265c092787b0c..fb9f1d8664fef 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -4,6 +4,12 @@ use rustc_abi::{ use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg, Uniform}; +/// C `long double` is IEEE binary128 on 32-bit SPARC, i.e. Rust's `f128`. It is passed and +/// returned indirectly. +fn is_long_double(repr: BackendRepr) -> bool { + matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) +} + fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, offset: &mut Size) where Ty: TyAbiInterface<'a, C> + Copy, @@ -28,7 +34,10 @@ where } ret.cast_to(cast); - } else if ret.layout.is_aggregate() { + } else if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { + // `long double` is returned through an `sret` pointer, see Clang + // `SparcV8ABIInfo::classifyReturnType`. The `sret` attribute is also what makes LLVM emit + // the `unimp` marker after the call and return to `%o7+12` in the callee. ret.make_indirect(); *offset += cx.data_layout().pointer_size(); } else { @@ -52,6 +61,12 @@ where *offset += dl.pointer_size(); return; } + if is_long_double(arg.layout.backend_repr) { + // See Clang `SparcV8ABIInfo::classifyArgumentType`. + arg.pass_by_stack_offset(None); + *offset += dl.pointer_size(); + return; + } let size = arg.layout.size; let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); diff --git a/tests/codegen-llvm/abi-sparc-f128.rs b/tests/codegen-llvm/abi-sparc-f128.rs new file mode 100644 index 0000000000000..fdc427851c903 --- /dev/null +++ b/tests/codegen-llvm/abi-sparc-f128.rs @@ -0,0 +1,50 @@ +// Checks that 32-bit SPARC passes and returns `f128` indirectly, like C `long double`. GCC and +// Clang both use an `sret` pointer for the return value, which is also what makes LLVM emit the +// `unimp` marker after the call and the matching `%o7+12` return in the callee. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target=sparc-unknown-linux-gnu +//@ needs-llvm-components: sparc + +#![feature(no_core, lang_items, f128)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: define void @ret_f128(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn ret_f128() -> f128 { + 1.5 +} + +// CHECK-LABEL: define void @id_f128(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}, ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn id_f128(x: f128) -> f128 { + x +} + +extern "C" { + fn take_f128(x: f128); +} + +// CHECK-LABEL: define void @call_f128( +// CHECK: call void @take_f128(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn call_f128(x: f128) { + unsafe { take_f128(x) } +} + +// `f32` and `f64` are still passed and returned directly. +// CHECK-LABEL: define{{.*}} float @id_f32(float {{.*}}) +#[no_mangle] +pub extern "C" fn id_f32(x: f32) -> f32 { + x +} + +// CHECK-LABEL: define{{.*}} double @id_f64(double {{.*}}) +#[no_mangle] +pub extern "C" fn id_f64(x: f64) -> f64 { + x +} From f62998986eb93f13275b7b445e3f6c8c931f259c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 21:29:43 +0200 Subject: [PATCH 25/25] sparc: pass aggregate arguments by reference GCC and Clang pass by-value aggregate arguments by reference on 32-bit SPARC: the caller makes a copy and passes its address. Clang's `SparcV8ABIInfo` falls back to `DefaultABIInfo::classifyArgumentType`, which makes any aggregate `byval`. rustc instead cast aggregates to a run of `i32` registers, so every by-value struct crossing the Rust/C boundary was corrupted, at any size. Aggregate returns were already indirect and stay that way. With aggregates and `f128` now all indirect, nothing reads the parameter array offset any more, so drop the offset tracking. --- compiler/rustc_target/src/callconv/sparc.rs | 40 ++++-------- tests/codegen-llvm/abi-sparc-aggregate.rs | 65 +++++++++++++++++++ .../repr/transparent-byval-struct-ptr.rs | 4 +- .../repr/transparent-imm-array.rs | 4 +- 4 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 tests/codegen-llvm/abi-sparc-aggregate.rs diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index fb9f1d8664fef..6866d7e717d9e 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -2,7 +2,7 @@ use rustc_abi::{ BackendRepr, Float, HasDataLayout, Integer, Numeric, Primitive, RegKind, TyAbiInterface, }; -use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg}; /// C `long double` is IEEE binary128 on 32-bit SPARC, i.e. Rust's `f128`. It is passed and /// returned indirectly. @@ -10,7 +10,7 @@ fn is_long_double(repr: BackendRepr) -> bool { matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) } -fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, offset: &mut Size) +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, @@ -35,40 +35,28 @@ where ret.cast_to(cast); } else if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { - // `long double` is returned through an `sret` pointer, see Clang - // `SparcV8ABIInfo::classifyReturnType`. The `sret` attribute is also what makes LLVM emit - // the `unimp` marker after the call and return to `%o7+12` in the callee. + // See Clang `SparcV8ABIInfo::classifyReturnType`, which returns `long double` through an + // `sret` pointer. The `sret` attribute is also what makes LLVM emit the `unimp` marker + // after the call and return to `%o7+12` in the callee. ret.make_indirect(); - *offset += cx.data_layout().pointer_size(); } else { ret.extend_integer_width_to(32); } } -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, offset: &mut Size) +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { if !arg.layout.is_sized() { - // FIXME: Update offset? // Not touching this... return; } - let dl = cx.data_layout(); if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); - *offset += dl.pointer_size(); return; } - if is_long_double(arg.layout.backend_repr) { - // See Clang `SparcV8ABIInfo::classifyArgumentType`. - arg.pass_by_stack_offset(None); - *offset += dl.pointer_size(); - return; - } - let size = arg.layout.size; - let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if let Some(component) = arg.layout.complex_number(cx) { if let Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) = component { @@ -76,14 +64,14 @@ where } else { arg.pass_by_stack_offset(None); } - } else if arg.layout.is_aggregate() { - let pad_i32 = u8::from(!offset.is_aligned(align)); - arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); + } else if is_long_double(arg.layout.backend_repr) || arg.layout.is_aggregate() { + // `long double` and aggregates are passed by reference: the caller makes a copy and passes + // its address. See Clang `SparcV8ABIInfo::classifyArgumentType` and the + // `DefaultABIInfo::classifyArgumentType` it falls back to. + arg.pass_by_stack_offset(None); } else { arg.extend_integer_width_to(32); } - - *offset = offset.align_to(align) + size.align_to(align); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -91,19 +79,17 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - let mut offset = Size::ZERO; if !fn_abi.ret.is_ignore() { - classify_ret(cx, &mut fn_abi.ret, &mut offset); + classify_ret(cx, &mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { if arg.layout.is_zst() { arg.make_indirect_from_ignore(); - offset += cx.data_layout().pointer_size(); } continue; } - classify_arg(cx, arg, &mut offset); + classify_arg(cx, arg); } } diff --git a/tests/codegen-llvm/abi-sparc-aggregate.rs b/tests/codegen-llvm/abi-sparc-aggregate.rs new file mode 100644 index 0000000000000..429640c35726c --- /dev/null +++ b/tests/codegen-llvm/abi-sparc-aggregate.rs @@ -0,0 +1,65 @@ +// Checks that 32-bit SPARC passes aggregate arguments by reference, matching GCC and Clang +// (`SparcV8ABIInfo` falls back to Clang's `DefaultABIInfo::classifyArgumentType`, which makes +// aggregates `byval`). Regression test for rustc passing them as a run of `i32` registers. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target=sparc-unknown-linux-gnu +//@ needs-llvm-components: sparc + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[repr(C)] +pub struct S4 { + a: u32, +} + +#[repr(C)] +pub struct S16 { + a: u32, + b: u64, +} + +#[repr(C)] +pub struct S240 { + a: [u32; 60], +} + +// CHECK-LABEL: define void @take_s4(ptr {{.*}}byval([4 x i8]) align 4 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s4(_: S4) {} + +// CHECK-LABEL: define void @take_s16(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s16(_: S16) {} + +// CHECK-LABEL: define void @take_s240(ptr {{.*}}byval([240 x i8]) align 4 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s240(_: S240) {} + +// An aggregate return keeps using `sret`. +// CHECK-LABEL: define void @roundtrip_s16(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}, ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn roundtrip_s16(s: S16) -> S16 { + s +} + +extern "C" { + fn extern_take_s16(s: S16); +} + +// CHECK-LABEL: define void @call_s16( +// CHECK: call void @extern_take_s16(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn call_s16(s: S16) { + unsafe { extern_take_s16(s) } +} + +// Scalars keep being passed directly, and small integers are sign/zero extended to 32 bits. +// CHECK-LABEL: define void @take_scalars(i8 noundef signext %_a, i16 noundef zeroext %_b, i32 noundef %_c) +#[no_mangle] +pub extern "C" fn take_scalars(_a: i8, _b: u16, _c: u32) {} diff --git a/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs b/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs index 6ecad80b008f5..a16f9dcc3a43e 100644 --- a/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs +++ b/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs @@ -1,5 +1,5 @@ //@ add-minicore -//@ revisions: i686-linux i686-freebsd x64-linux x64-apple +//@ revisions: i686-linux i686-freebsd x64-linux x64-apple sparc //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@[i686-linux] compile-flags: --target i686-unknown-linux-gnu @@ -10,6 +10,8 @@ //@[x64-linux] needs-llvm-components: x86 //@[x64-apple] compile-flags: --target x86_64-apple-darwin //@[x64-apple] needs-llvm-components: x86 +//@[sparc] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc] needs-llvm-components: sparc // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR diff --git a/tests/codegen-llvm/repr/transparent-imm-array.rs b/tests/codegen-llvm/repr/transparent-imm-array.rs index c72151741400a..04c9727b815d4 100644 --- a/tests/codegen-llvm/repr/transparent-imm-array.rs +++ b/tests/codegen-llvm/repr/transparent-imm-array.rs @@ -1,5 +1,5 @@ //@ add-minicore -//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb sparc +//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@[arm-linux] compile-flags: --target arm-unknown-linux-gnueabi @@ -14,8 +14,6 @@ //@[mips] needs-llvm-components: mips //@[thumb] compile-flags: --target thumbv7neon-linux-androideabi //@[thumb] needs-llvm-components: arm -//@[sparc] compile-flags: --target sparc-unknown-linux-gnu -//@[sparc] needs-llvm-components: sparc // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR