[PATCH 1/6] arm: fix wrong-code for __fp16 homogeneous aggregate return [PR92999]
Dominic P <[email protected]> Sun, 2 Aug 2026 11:56:03 +0100
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
With the hard-float (VFP) PCS, a homogeneous aggregate whose elements are __fp16 (HFmode) was returned incorrectly: every element was placed in s0 instead of consecutive VFP registers, so all elements collapsed onto s0; on a round-trip every field read back as the last element's value. aapcs_vfp_allocate_return_reg computed the per-element register stride as GET_MODE_SIZE (ag_mode) / GET_MODE_SIZE (SFmode). For an HFmode element this is 2 / 4 == 0, so element i was allocated to FIRST_VFP_REGNUM + i * 0, i.e. s0 for every element. Fix it the same way aapcs_vfp_allocate already does for argument passing: clamp the element size to at least the size of SFmode before dividing, giving a stride of one VFP register per __fp16 element. For all other element modes (SF, DF, DI, vector) MAX has no effect, so the generated code for every already-correct case is unchanged. Assisted-by: Claude Opus 5 (Anthropic) gcc/ChangeLog: PR target/92999 * config/arm/arm.cc (aapcs_vfp_allocate_return_reg): Clamp the aggregate element size to at least the size of SFmode when computing the VFP register stride, mirroring aapcs_vfp_allocate. gcc/testsuite/ChangeLog: PR target/92999 * gcc.target/arm/pr92999.c: New test. Signed-off-by: Dominic P <[email protected]> --- gcc/config/arm/arm.cc | 3 +- gcc/testsuite/gcc.target/arm/pr92999.c | 43 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.target/arm/pr92999.c diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc index 0bc66abe2..4597b6bc0 100644 --- a/gcc/config/arm/arm.cc +++ b/gcc/config/arm/arm.cc @@ -6858,7 +6858,8 @@ aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED, count *= 2; } } - shift = GET_MODE_SIZE(ag_mode) / GET_MODE_SIZE(SFmode); + shift = (MAX (GET_MODE_SIZE (ag_mode), GET_MODE_SIZE (SFmode)) + / GET_MODE_SIZE (SFmode)); par = gen_rtx_PARALLEL (mode, rtvec_alloc (count)); for (i = 0; i < count; i++) { diff --git a/gcc/testsuite/gcc.target/arm/pr92999.c b/gcc/testsuite/gcc.target/arm/pr92999.c new file mode 100644 index 000000000..f69a019aa --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr92999.c @@ -0,0 +1,43 @@ +/* PR target/92999: with the hard-float (VFP) ABI, returning a structure + that is a homogeneous aggregate of __fp16 elements placed every element + into s0, corrupting all but the last field. Check that a round-trip + through the ABI preserves every field. */ + +/* { dg-do run { target arm_eabi } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O2 -mfpu=vfp -mfloat-abi=hard" } */ + +struct h2 { __fp16 a; __fp16 b; }; +struct h3 { __fp16 a; __fp16 b; __fp16 c; }; +struct h4 { __fp16 a; __fp16 b; __fp16 c; __fp16 d; }; + +struct h2 __attribute__((noipa)) ret2 (struct h2 s) { return s; } +struct h3 __attribute__((noipa)) ret3 (struct h3 s) { return s; } +struct h4 __attribute__((noipa)) ret4 (struct h4 s) { return s; } + +int +main (void) +{ + union { struct h2 s; unsigned short u[2]; } i2, o2; + union { struct h3 s; unsigned short u[3]; } i3, o3; + union { struct h4 s; unsigned short u[4]; } i4, o4; + + i2.u[0] = 0x3c00; i2.u[1] = 0x4000; /* 1.0, 2.0 */ + o2.s = ret2 (i2.s); + if (o2.u[0] != 0x3c00 || o2.u[1] != 0x4000) + __builtin_abort (); + + i3.u[0] = 0x3c00; i3.u[1] = 0x4000; i3.u[2] = 0x4200; /* 1, 2, 3 */ + o3.s = ret3 (i3.s); + if (o3.u[0] != 0x3c00 || o3.u[1] != 0x4000 || o3.u[2] != 0x4200) + __builtin_abort (); + + i4.u[0] = 0x3c00; i4.u[1] = 0x4000; i4.u[2] = 0x4200; i4.u[3] = 0x4400; + o4.s = ret4 (i4.s); + if (o4.u[0] != 0x3c00 || o4.u[1] != 0x4000 + || o4.u[2] != 0x4200 || o4.u[3] != 0x4400) + __builtin_abort (); + + return 0; +} -- 2.55.0