From 7df5d7f0dd515994866a5362085ff7665d690e9d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 20:45:12 +0200 Subject: [PATCH 1/8] 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 71af508915e59..004cd5c8bc5ee 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -55,6 +55,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 e989f2349d38ce71314dd2d8745405c1179bb6ca Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 21:11:17 +0200 Subject: [PATCH 2/8] sparc: pass and return `f128` indirectly long double (i.e. f128) is special-case in the sparc abi --- compiler/rustc_target/src/callconv/sparc.rs | 17 +++++-- tests/codegen-llvm/f128-sparc-callconv.rs | 53 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 tests/codegen-llvm/f128-sparc-callconv.rs diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 004cd5c8bc5ee..a5a1dc36c9d4c 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,16 +1,20 @@ -use rustc_abi::{HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, Size, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +fn is_long_double(repr: BackendRepr) -> bool { + matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) +} + fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) where C: HasDataLayout, { - if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(32); - } else { + if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { ret.make_indirect(); *offset += cx.data_layout().pointer_size(); + } else { + ret.extend_integer_width_to(32); } } @@ -30,6 +34,11 @@ where *offset += dl.pointer_size(); return; } + if is_long_double(arg.layout.backend_repr) { + 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/f128-sparc-callconv.rs b/tests/codegen-llvm/f128-sparc-callconv.rs new file mode 100644 index 0000000000000..b5e7b95c3fc6b --- /dev/null +++ b/tests/codegen-llvm/f128-sparc-callconv.rs @@ -0,0 +1,53 @@ +//! Verify that Rust implements the expected calling convention for `f128` + +//@ add-minicore +//@ revisions: sparc-none sparc-linux +//@ [sparc-none] compile-flags: --target sparc-unknown-none-elf +//@ [sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 +//@ needs-llvm-components: sparc + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items, f128)] + +extern crate minicore; + +extern "C" { + fn extern_call(arg0: f128); + fn extern_ret() -> f128; +} + +#[no_mangle] +pub extern "C" fn pass(_arg0: u32, arg1: f128) { + // CHECK-LABEL: @pass( + // an f128 is passed via the stack + // CHECK-SAME: ptr {{.*}}byval([16 x i8] + // CHECK: call void @extern_call + unsafe { extern_call(arg1) }; +} + +// Check that we produce the correct return ABI +#[no_mangle] +pub extern "C" fn ret(_arg0: u32, arg1: f128) -> f128 { + // CHECK-LABEL: @ret( + // and an f128 is returned via the stack + // CHECK-SAME: sret([16 x i8]) + // CHECK: %0 = load fp128, ptr %arg1 + // CHECK-NEXT: store fp128 %0, ptr %_0 + // CHECK-NEXT: ret void + arg1 +} + +// Check that we consume the correct return ABI +#[no_mangle] +pub extern "C" fn forward(dst: *mut f128) { + // CHECK-LABEL: @forward + // CHECK-SAME: ptr{{.*}} %dst) + // without optimizatons, an intermediate alloca is used + // CHECK: call void @extern_ret + // CHECK: store fp128 + // CHECK: ret void + unsafe { *dst = extern_ret() }; +} From 3ebccb1de832249a66bdc8ddf81f7938a5dbd033 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 2 Aug 2026 21:29:43 +0200 Subject: [PATCH 3/8] sparc: pass aggregate arguments by reference this matches GCC and Clang. --- compiler/rustc_target/src/callconv/sparc.rs | 35 ++---- tests/codegen-llvm/f128-sparc-callconv.rs | 2 +- .../repr/transparent-imm-array.rs | 4 +- tests/codegen-llvm/repr/transparent-sparc.rs | 116 ++++++++++++++++++ 4 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 tests/codegen-llvm/repr/transparent-sparc.rs diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index a5a1dc36c9d4c..d342c3cff6e88 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,55 +1,38 @@ -use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, Size, TyAbiInterface}; +use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, FnAbi}; fn is_long_double(repr: BackendRepr) -> bool { matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) } -fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) -where - C: HasDataLayout, -{ +fn classify_ret<'a, Ty>(ret: &mut ArgAbi<'a, Ty>) { if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { 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) { - 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 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); + if arg.layout.is_aggregate() || is_long_double(arg.layout.backend_repr) { + arg.make_indirect(); } 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>) @@ -57,19 +40,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(&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/f128-sparc-callconv.rs b/tests/codegen-llvm/f128-sparc-callconv.rs index b5e7b95c3fc6b..d7fd1a0ff4953 100644 --- a/tests/codegen-llvm/f128-sparc-callconv.rs +++ b/tests/codegen-llvm/f128-sparc-callconv.rs @@ -23,7 +23,7 @@ extern "C" { pub extern "C" fn pass(_arg0: u32, arg1: f128) { // CHECK-LABEL: @pass( // an f128 is passed via the stack - // CHECK-SAME: ptr {{.*}}byval([16 x i8] + // CHECK-SAME: ptr {{.*}} // CHECK: call void @extern_call unsafe { extern_call(arg1) }; } 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 diff --git a/tests/codegen-llvm/repr/transparent-sparc.rs b/tests/codegen-llvm/repr/transparent-sparc.rs new file mode 100644 index 0000000000000..a9532a966e170 --- /dev/null +++ b/tests/codegen-llvm/repr/transparent-sparc.rs @@ -0,0 +1,116 @@ +//@ add-minicore +//@ revisions: sparc-none sparc-linux +//@ [sparc-none] compile-flags: --target sparc-unknown-none-elf +//@ [sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes +//@ needs-llvm-components: sparc + +// See ./transparent.rs + +#![feature(no_core, lang_items, transparent_unions)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::*; +impl Copy for BigS {} +impl Copy for BigU {} + +#[repr(C)] +pub struct BigS([u32; 16]); + +#[repr(transparent)] +pub struct TsBigS(BigS); + +#[repr(transparent)] +pub union TuBigS { + field: BigS, +} + +#[repr(transparent)] +pub enum TeBigS { + Variant(BigS), +} + +// CHECK: define{{.*}}void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} + +#[repr(C)] +pub union BigU { + foo: [u32; 16], +} + +#[repr(transparent)] +pub struct TsBigU(BigU); + +#[repr(transparent)] +pub union TuBigU { + field: BigU, +} + +#[repr(transparent)] +pub enum TeBigU { + Variant(BigU), +} + +// CHECK: define{{.*}}void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} From 38f0953f4eabb82ff77fb035627b1560f2a0b7a8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 9 Aug 2026 16:21:42 +0200 Subject: [PATCH 4/8] add sparc c-zst revision --- tests/ui/abi/c-zst.aarch64-darwin.stderr | 2 +- tests/ui/abi/c-zst.powerpc-linux.stderr | 2 +- tests/ui/abi/c-zst.rs | 20 ++--- tests/ui/abi/c-zst.s390x-linux.stderr | 2 +- tests/ui/abi/c-zst.sparc-linux.stderr | 80 +++++++++++++++++++ tests/ui/abi/c-zst.sparc-none.stderr | 80 +++++++++++++++++++ tests/ui/abi/c-zst.sparc64-linux.stderr | 2 +- tests/ui/abi/c-zst.x86_64-linux.stderr | 2 +- .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr | 2 +- 9 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 tests/ui/abi/c-zst.sparc-linux.stderr create mode 100644 tests/ui/abi/c-zst.sparc-none.stderr diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 6d2ac90c0c975..2ed9ffdf791f6 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -60,7 +60,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.rs b/tests/ui/abi/c-zst.rs index 22cb3f98f28dc..973f5f0002f25 100644 --- a/tests/ui/abi/c-zst.rs +++ b/tests/ui/abi/c-zst.rs @@ -15,9 +15,9 @@ extern "C" fn(i32, (), i32); ``` */ -/* - * ZST IN "C" IS ZERO-SIZED - */ +// +// ZST IN "C" IS ZERO-SIZED +// //@ revisions: aarch64-darwin //@[aarch64-darwin] compile-flags: --target aarch64-apple-darwin @@ -27,10 +27,9 @@ extern "C" fn(i32, (), i32); //@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64-linux] needs-llvm-components: x86 - -/* - * ZST IN "C" IS PASS-BY-POINTER - */ +// +// ZST IN "C" IS PASS-BY-POINTER +// // according to the SRV4 ABI, an aggregate is always passed in registers, // and it so happens the GCC extension for ZSTs considers them as structs. @@ -42,7 +41,11 @@ extern "C" fn(i32, (), i32); //@[s390x-linux] compile-flags: --target s390x-unknown-linux-gnu //@[s390x-linux] needs-llvm-components: systemz -//@ revisions: sparc64-linux +//@ revisions: sparc-none sparc-linux sparc64-linux +//@[sparc-none] compile-flags: --target sparc-unknown-none-elf +//@[sparc-none] needs-llvm-components: sparc +//@[sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc-linux] needs-llvm-components: sparc //@[sparc64-linux] compile-flags: --target sparc64-unknown-linux-gnu //@[sparc64-linux] needs-llvm-components: sparc @@ -53,7 +56,6 @@ extern "C" fn(i32, (), i32); //@[x86_64-pc-windows-gnu] needs-llvm-components: x86 //@ ignore-backends: gcc - #![feature(no_core, rustc_attrs)] #![no_core] #![crate_type = "lib"] diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.sparc-linux.stderr b/tests/ui/abi/c-zst.sparc-linux.stderr new file mode 100644 index 0000000000000..302ffe1efc8b8 --- /dev/null +++ b/tests/ui/abi/c-zst.sparc-linux.stderr @@ -0,0 +1,80 @@ +error: fn_abi_of(pass_zst) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: Some( + Align(1 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/c-zst.rs:67:1 + | +LL | extern "C" fn pass_zst(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/c-zst.sparc-none.stderr b/tests/ui/abi/c-zst.sparc-none.stderr new file mode 100644 index 0000000000000..302ffe1efc8b8 --- /dev/null +++ b/tests/ui/abi/c-zst.sparc-none.stderr @@ -0,0 +1,80 @@ +error: fn_abi_of(pass_zst) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: Some( + Align(1 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/c-zst.rs:67:1 + | +LL | extern "C" fn pass_zst(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 6d2ac90c0c975..2ed9ffdf791f6 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -60,7 +60,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 188414046cf8c5347dd2366dd590bf63de5f3aa3 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 25 Jul 2026 22:42:32 +0200 Subject: [PATCH 5/8] make SPARC `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/sparc.rs | 51 ++++++++++++++++++--- tests/codegen-llvm/complex-abi.rs | 23 +++++----- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index d342c3cff6e88..d58a17383c2b0 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,13 +1,43 @@ -use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, TyAbiInterface}; +use rustc_abi::{ + BackendRepr, Float, HasDataLayout, Integer, Numeric, Primitive, RegKind, TyAbiInterface, +}; -use crate::callconv::{ArgAbi, FnAbi}; +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. fn is_long_double(repr: BackendRepr) -> bool { matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) } -fn classify_ret<'a, Ty>(ret: &mut ArgAbi<'a, Ty>) { - if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, +{ + 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); + + 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 is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { + // 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(); } else { ret.extend_integer_width_to(32); @@ -28,7 +58,16 @@ where return; } - if arg.layout.is_aggregate() || is_long_double(arg.layout.backend_repr) { + 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); + } + } 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.make_indirect(); } else { arg.extend_integer_width_to(32); @@ -41,7 +80,7 @@ where C: HasDataLayout, { 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/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 4ba7ecad764f2..a5c3374c8582b 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -61,9 +61,11 @@ //@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 //@ [CSKY] needs-llvm-components: csky -//@ 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 //@ revisions: POWERPC64LE POWERPC64 AIX //@ [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu @@ -75,10 +77,6 @@ // 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: POWERPC // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu // [POWERPC] needs-llvm-components: powerpc @@ -154,7 +152,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([8 x i8]) {{.*}}, 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([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]) {{.*}}) @@ -188,7 +186,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([16 x i8]) {{.*}}, 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([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]) {{.*}}) @@ -203,6 +201,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([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]) {{.*}}) @@ -236,7 +235,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({ i8, 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]) {{.*}}) @@ -270,7 +269,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({ i16, i16 }) {{.*}}) + // 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]) {{.*}}) @@ -304,7 +303,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({ i32, i32 }) {{.*}}) + // 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]) {{.*}}) @@ -338,7 +337,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([16 x i8]) {{.*}}, 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([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]) {{.*}}) @@ -377,7 +376,7 @@ pub extern "C" fn wrapper_cplx_i64( // RISCV64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define { i64, i64 } @wrapper_cplx_i64({ i64, i64 } {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) From 311b0241ebd51137ab58137b1862f99b44d3e539 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 14:02:45 +0200 Subject: [PATCH 6/8] make mips64 `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/mips64.rs | 54 +++++++++++++++++++- tests/codegen-llvm/complex-abi.rs | 33 ++++++++---- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 8002f98507ba8..d28edb46bed85 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, Size, TyAbiInterface, + BackendRepr, FieldsShape, Float, HasDataLayout, Integer, 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,6 +58,22 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { + if let Some(component) = ret.layout.complex_number(cx) { + match component { + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + // 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 }); + } + _ => { + // Otherwise pass in 2 registers. + let reg = Reg { kind: component.reg_kind(), size: component.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 +121,39 @@ where extend_integer_width_mips(arg, 64); } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + } else if let Some(component) = arg.layout.complex_number(cx) { + let slot = dl.pointer_size(); + let curr_offset = offset.align_to(align); + + match component { + Numeric::Float(Float::F128) => { + // 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); + } + Numeric::Float(_) => { + // Only pass a Complex/Complex in FPRs when two argument slots are free, + 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 a Complex which could fit into one slot. + *offset = curr_offset + slot * 2; + return; + } + + // Otherwise pack it into GPRs (or the stack) like an integer of the same size. + arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); + } + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + // Cast Complex into i16, Complex to i32, etc. + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); + // The inreg attribute makes the bits land in the right (upper) bits on BE targets. + arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); + } + Numeric::Int(Integer::I64 | Integer::I128, _) => { + // Complex is passed as 2 separate arguments, which is what the default + // `PassMode::Pair` already does. + } + } } 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 a5c3374c8582b..b20fc12d5b884 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -75,18 +75,24 @@ //@ [AIX] compile-flags: --target powerpc64-ibm-aix //@ [AIX] needs-llvm-components: powerpc +// 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 // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu // [POWERPC] 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 @@ -142,6 +148,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 }) {{.*}}) @@ -176,6 +183,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 }) {{.*}}) @@ -201,6 +209,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 {{.*}}) @@ -225,7 +235,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 } %0) @@ -259,7 +270,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 {{.*}}) @@ -293,7 +305,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 } %0) @@ -327,6 +340,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 }) {{.*}}) @@ -366,6 +380,7 @@ pub extern "C" fn wrapper_cplx_i64( // I686: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // LOONGARCH32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) From bc47af0a6686a7d1b5f77d53c6c0fab486cd43f4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 17:12:50 +0200 Subject: [PATCH 7/8] _Complex for mips --- compiler/rustc_target/src/callconv/mips.rs | 34 +++++++++++++++++----- tests/codegen-llvm/complex-abi.rs | 29 +++++++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index d3a39d05b4964..a68c2e71c923d 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: component.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 b20fc12d5b884..ae454e7c1f226 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -83,11 +83,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 // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu @@ -151,6 +153,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 } %0) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32({ float, float } %0) @@ -185,7 +188,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 } %0) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64({ double, double } %0) @@ -237,7 +241,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 } %0) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8({ i8, i8 } %0) @@ -272,7 +277,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 {{.*}}) @@ -307,7 +313,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 } %0) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32({ i32, i32 } %0) @@ -342,7 +349,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 } %0) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64({ i64, i64 } %0) @@ -382,7 +390,8 @@ pub extern "C" fn wrapper_cplx_i64( // LOONGARCH64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) // MIPS64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) - // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i64, i64 } @wrapper_cplx_i64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0) // POWERPC64LE: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0) From 412dc859f8c19f471ce835e4c8f48284645bfdd6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Jul 2026 20:55:58 +0200 Subject: [PATCH 8/8] make powerpc `Complex` ABI GCC-compatible --- compiler/rustc_target/src/callconv/powerpc.rs | 108 ++++++++++++++++-- tests/codegen-llvm/complex-abi.rs | 24 ++-- 2 files changed, 110 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 2b6a104e1221d..5c32e68aefe36 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,17 +1,45 @@ -use rustc_abi::TyAbiInterface; +use rustc_abi::{BackendRepr, Primitive, Reg, RegKind, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform}; use crate::spec::{Env, HasTargetSpec, Os}; -fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - if ret.layout.is_aggregate() { +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 { + // 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 { + // 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>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if ret.layout.is_complex_number(cx) { + ret.cast_to(complex_cast_target(ret)); + } else if ret.layout.is_aggregate() { ret.make_indirect(); } else { ret.extend_integer_width_to(32); } } -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, { @@ -25,11 +53,70 @@ where } return; } - if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || 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>) @@ -37,10 +124,11 @@ 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); } + 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 ae454e7c1f226..9b64eb1abd1b5 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -75,6 +75,10 @@ //@ [AIX] compile-flags: --target powerpc64-ibm-aix //@ [AIX] needs-llvm-components: powerpc +//@ revisions: POWERPC +//@ [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu +//@ [POWERPC] needs-llvm-components: powerpc + // NOTE: for Mips we follow the GCC ABI, not the Clang ABI. // See https://github.com/llvm/llvm-project/issues/212109. //@ revisions: MIPS64 MIPS64EL @@ -89,11 +93,7 @@ //@ [MIPSEL] compile-flags: --target mipsel-unknown-linux-gnu //@ [MIPSEL] needs-llvm-components: mips -// FIXME: the below revisions are deliberately disabled for now. - -// revisions: POWERPC -// [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu -// [POWERPC] needs-llvm-components: powerpc +// FIXME: the revisions below 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 } %0) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32({ float, float } %0) - // POWERPC: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) + // 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 {{.*}}) @@ -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 } %0) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64({ double, double } %0) - // 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 } %0) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8({ i8, i8 } %0) - // 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 } %0) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32({ i32, i32 } %0) - // POWERPC: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, 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 {{.*}}) @@ -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 } %0) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64({ i64, i64 } %0) - // 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 {{.*}}) @@ -395,7 +395,7 @@ pub extern "C" fn wrapper_cplx_i64( // NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0) // POWERPC64LE: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0) - // POWERPC: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @wrapper_cplx_i64([4 x i32] {{.*}}) // RISCV32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}})