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