Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,16 @@ impl LlvmType for CastTarget {
self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes())
};

// Simplify to a single unit or an array if there's no prefix.
// This produces the same layout, but using a simpler type.
// Simplify to an array if there is no prefix.
if self.prefix.is_empty() {
// We can't do this if is_consecutive is set and the unit would get
// split on the target. Currently, this is only relevant for i128
// registers.
if rest_count == 1 && (!self.rest.is_consecutive || self.rest.unit != Reg::i128()) {
// Pass `[1 x unit]` as just `unit`.
//
// On little-endian targets this produces a compatible layout, but using a simpler type.
// But on big-endian targets (e.g. aarch64_be or powerpc64) the value may be passed in
// different halves of the register-sized slot, so unit and [1 x unit] are incompatible.
//
// Setting `is_consecutive` forces use of an array.
if rest_count == 1 && !self.rest.is_consecutive {
return rest_ll_unit;
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/callconv/powerpc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ where
RegKind::Vector { .. } => unit.size.bits() == 128,
};

valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
valid_unit.then_some(Uniform::new(unit, arg.layout.size))
})
}

Expand Down
99 changes: 99 additions & 0 deletions tests/assembly-llvm/singleton-hfa.rs

@folkertdev folkertdev Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//@ add-minicore
//@ revisions: powerpc64 aarch64_be
//@ assembly-output: emit-asm
//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
//
//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu
//@[powerpc64] needs-llvm-components: powerpc
//
//@[aarch64_be] compile-flags: --target aarch64_be-unknown-linux-gnu
//@[aarch64_be] needs-llvm-components: aarch64
#![feature(no_core)]
#![no_core]
#![crate_type = "lib"]

// On big-endian targets, the singleton HFA and the unit are not ABI-compatible, so it matters
// which is used. Test that:
//
// - on powerpc64 SingletonStruct<f32> is passed like f32
// - on aarch64_be SingletonStruct<f32> is passed like [f32; 1]
//
// The test start with 13 float arguments, to exhaust float registers and force the last argument
// onto the stack (technically we only need 8 float arguments on aarch64_be but a couple more
// does not matter).

extern crate minicore;
use minicore::*;

#[repr(C)]
struct SingletonStruct<T>(T);

#[no_mangle]
extern "C" fn singleton_struct_float(
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
x: SingletonStruct<f32>,
) -> f32 {
// powerpc64: lfs 1, 156(1)
// aarch64_be: ldr s0, [sp, #40]
x.0
}

// CHECK-LABEL: singleton_array_float:
#[no_mangle]
extern "C" fn singleton_array_float(
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
x: [f32; 1],
) -> f32 {
// powerpc64: lfs 1, 156(1)
// aarch64_be: ldr s0, [sp, #40]
match x {
[a] => a,
}
}

// CHECK-LABEL: float:
#[no_mangle]
extern "C" fn float(
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
_: f64,
x: f32,
) -> f32 {
// powerpc64: lfs 1, 156(1)
// aarch64_be: ldr s0, [sp, #44]
x
}
23 changes: 18 additions & 5 deletions tests/codegen-llvm/aarch64-abi/homogeneous-aggregate.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//@ add-minicore
//@ compile-flags: -Cno-prepopulate-passes -Copt-level=0
//
//@ revisions: linux win
//@ revisions: linux linux-be win
//@[linux] compile-flags: --target aarch64-unknown-linux-gnu
//@[linux-be] compile-flags: --target aarch64_be-unknown-linux-gnu
//@[win] compile-flags: --target aarch64-pc-windows-msvc
//
//@ needs-llvm-components: aarch64
Expand All @@ -17,17 +18,29 @@ extern crate minicore;
use minicore::simd::*;
use minicore::*;

// A singleton homogeneous float aggregate.
#[repr(C)]
pub struct Hfa1 {
pub a: f32,
}

// NOTE: on aarch64_be `[1 x float]` is not abi-compatible with a bare `float`.
// CHECK: define void @test_hfa1([1 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa1(a: Hfa1) {
hint::black_box(a);
}

// A homogeneous float aggregate.
#[repr(C)]
pub struct Hfa {
pub struct Hfa2 {
pub a: f32,
pub b: f32,
}
impl Copy for Hfa {}

// CHECK: define void @test_hfa([2 x float] %0)
// CHECK: define void @test_hfa2([2 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa(a: Hfa) {
pub extern "C" fn test_hfa2(a: Hfa2) {
hint::black_box(a);
}

Expand Down
66 changes: 49 additions & 17 deletions tests/codegen-llvm/arm-abi/homogeneous-aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,69 @@ extern crate minicore;
use minicore::simd::*;
use minicore::*;

// A singleton homogeneous float aggregate.
#[repr(C)]
pub struct Hfa1 {
pub a: f32,
}

// linux: define void @test_hfa1([1 x float] %0)
// eabi: define dso_local void @test_hfa1([1 x i32] %0)
// watchos: define void @test_hfa1([1 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa1(a: Hfa1) {
hint::black_box(a);
}

// linux: define [1 x float] @ret_hfa1([1 x float] %0)
// eabi: define dso_local i32 @ret_hfa1([1 x i32] %0)
// watchos: define [1 x float] @ret_hfa1([1 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn ret_hfa1(a: Hfa1) -> Hfa1 {
a
}

// When we ask for `extern "aapcs"` specifically, GPRs are used, except on watchOS.
//
// linux: define arm_aapcscc void @test_hfa1_aapcs([1 x i32] %0)
// eabi: define dso_local arm_aapcscc void @test_hfa1_aapcs([1 x i32] %0)
// watchos: define arm_aapcscc void @test_hfa1_aapcs([1 x float] %0)
#[unsafe(no_mangle)]
pub extern "aapcs" fn test_hfa1_aapcs(a: Hfa1) {
hint::black_box(a);
}

// A homogeneous float aggregate, which a hard-float ABI passes in VFP registers.
#[repr(C)]
pub struct Hfa {
pub struct Hfa2 {
pub a: f32,
pub b: f32,
}
impl Copy for Hfa {}
impl Copy for Hfa2 {}

// linux: define void @test_hfa([2 x float] %0)
// eabi: define dso_local void @test_hfa([2 x i32] %0)
// watchos: define void @test_hfa([2 x float] %0)
// linux: define void @test_hfa2([2 x float] %0)
// eabi: define dso_local void @test_hfa2([2 x i32] %0)
// watchos: define void @test_hfa2([2 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa(a: Hfa) {
pub extern "C" fn test_hfa2(a: Hfa2) {
hint::black_box(a);
}

// linux: define [2 x float] @ret_hfa([2 x float] %0)
// eabi: define dso_local void @ret_hfa(ptr sret([8 x i8]) align 4 %_0, [2 x i32] %0)
// watchos: define [2 x float] @ret_hfa([2 x float] %0)
// linux: define [2 x float] @ret_hfa2([2 x float] %0)
// eabi: define dso_local void @ret_hfa2(ptr sret([8 x i8]) align 4 %_0, [2 x i32] %0)
// watchos: define [2 x float] @ret_hfa2([2 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn ret_hfa(a: Hfa) -> Hfa {
pub extern "C" fn ret_hfa2(a: Hfa2) -> Hfa2 {
a
}

// When we ask for `extern "aapcs"` specifically, GPRs are used, except on watchOS.
//
// linux: define arm_aapcscc void @test_hfa_aapcs([2 x i32] %0)
// eabi: define dso_local arm_aapcscc void @test_hfa_aapcs([2 x i32] %0)
// watchos: define arm_aapcscc void @test_hfa_aapcs([2 x float] %0)
// linux: define arm_aapcscc void @test_hfa2_aapcs([2 x i32] %0)
// eabi: define dso_local arm_aapcscc void @test_hfa2_aapcs([2 x i32] %0)
// watchos: define arm_aapcscc void @test_hfa2_aapcs([2 x float] %0)
#[unsafe(no_mangle)]
pub extern "aapcs" fn test_hfa_aapcs(a: Hfa) {
pub extern "aapcs" fn test_hfa2_aapcs(a: Hfa2) {
hint::black_box(a);
}

Expand Down Expand Up @@ -216,7 +248,7 @@ extern "C" {
// linux: declare void @test_hfa_variadic_c([2 x i32], ...)
// eabi: declare dso_local void @test_hfa_variadic_c([2 x i32], ...)
// watchos: declare void @test_hfa_variadic_c([2 x float], ...)
fn test_hfa_variadic_c(_: Hfa, ...);
fn test_hfa_variadic_c(_: Hfa2, ...);

// linux: declare void @test_ints_6_variadic_c([6 x i32], ...)
// eabi: declare dso_local void @test_ints_6_variadic_c([6 x i32], ...)
Expand All @@ -228,15 +260,15 @@ extern "aapcs" {
// linux: declare arm_aapcscc void @test_hfa_variadic_aapcs([2 x i32], ...)
// eabi: declare dso_local arm_aapcscc void @test_hfa_variadic_aapcs([2 x i32], ...)
// watchos: declare arm_aapcscc void @test_hfa_variadic_aapcs([2 x float], ...)
fn test_hfa_variadic_aapcs(_: Hfa, ...);
fn test_hfa_variadic_aapcs(_: Hfa2, ...);

// linux: declare arm_aapcscc void @test_ints_6_variadic_aapcs([6 x i32], ...)
// eabi: declare dso_local arm_aapcscc void @test_ints_6_variadic_aapcs([6 x i32], ...)
// watchos: declare arm_aapcscc void @test_ints_6_variadic_aapcs(ptr align 4, ...)
fn test_ints_6_variadic_aapcs(_: Ints6, ...);
}

pub unsafe fn call_variadics(a: Hfa, b: Ints6) {
pub unsafe fn call_variadics(a: Hfa2, b: Ints6) {
test_hfa_variadic_c(a);
test_ints_6_variadic_c(b);

Expand Down
8 changes: 4 additions & 4 deletions tests/codegen-llvm/arm-abi/struct-alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ pub struct HasU64 {
pub a: u64,
}

// linux: define void @test_has_u64(i64 %0)
// eabi: define dso_local void @test_has_u64(i64 %0)
// watchos: define void @test_has_u64(i64 %0)
// linux: define void @test_has_u64([1 x i64] %0)
// eabi: define dso_local void @test_has_u64([1 x i64] %0)
// watchos: define void @test_has_u64([1 x i64] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_has_u64(a: HasU64) {
hint::black_box(a);
Expand All @@ -62,7 +62,7 @@ pub struct Transparent8 {
//
// linux: define void @test_8([2 x i32] %0, [2 x i32] %1)
// eabi: define dso_local void @test_8([2 x i32] %0, [2 x i32] %1)
// watchos: define void @test_8(i64 %0, i64 %1)
// watchos: define void @test_8([1 x i64] %0, [1 x i64] %1)
#[unsafe(no_mangle)]
pub extern "C" fn test_8(a: Align8, b: Transparent8) {
hint::black_box(a);
Expand Down
4 changes: 2 additions & 2 deletions tests/codegen-llvm/complex-abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub extern "C" fn cplx_i8(x: Complex<i8>) -> Complex<i8> {
// AARCH64_MSVC: define{{.*}} i64 @cplx_i8(i64{{.*}})
// AIX: define{{.*}} { i8, i8 } @cplx_i8({ i8, i8 } %0)
// ARM64EC: define{{.*}} i64 @cplx_i8(i64{{.*}})
// ARM: define{{.*}} i32 @cplx_i8(i32{{.*}})
// ARM: define{{.*}} i32 @cplx_i8([1 x 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]) {{.*}})
Expand Down Expand Up @@ -265,7 +265,7 @@ pub extern "C" fn cplx_i16(x: Complex<i16>) -> Complex<i16> {
// 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(i32{{.*}})
// ARM: define{{.*}} i32 @cplx_i16([1 x 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]) {{.*}})
Expand Down
25 changes: 19 additions & 6 deletions tests/codegen-llvm/powerpc64-abi/homogeneous-aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,32 @@ extern crate minicore;
use minicore::simd::*;
use minicore::*;

// A singleton homogeneous float aggregate.
#[repr(C)]
pub struct Hfa1 {
pub a: f32,
}

// NOTE: on powerpc64 `[1 x float]` is not abi-compatible with a bare `float`.
// CHECK: define void @test_hfa1(float %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa1(a: Hfa1) {
hint::black_box(a);
}

// A homogeneous float aggregate.
#[repr(C)]
pub struct Hfa {
pub struct Hfa2 {
pub a: f32,
pub b: f32,
}
impl Copy for Hfa {}
impl Copy for Hfa2 {}

// ppc64: define void @test_hfa(i64 %0)
// ppc64_vsx: define void @test_hfa(i64 %0)
// ppc64le: define void @test_hfa([2 x float] %0)
// ppc64: define void @test_hfa2(i64 %0)
// ppc64_vsx: define void @test_hfa2(i64 %0)
// ppc64le: define void @test_hfa2([2 x float] %0)
#[unsafe(no_mangle)]
pub extern "C" fn test_hfa(a: Hfa) {
pub extern "C" fn test_hfa2(a: Hfa2) {
hint::black_box(a);
}

Expand Down
Loading