On RV32 a 2*XLEN argument passed when only one register remains is
split: the low word goes in the odd argument register (a7) and the
high word on the incoming stack.  When the middle-end later
reconstructs the value as a single DImode, riscv_zilsd_valid_mem_p
relies only on MEM_ALIGN, which is mis-marked A64 for the spilled
slot, so a single misaligned zilsd "ld"/"sd" is emitted (e.g.
ld s0,76(sp)) and faults under -mzilsd-strict-align.

Double-check the address when it is a constant offset from a
frame-related pointer (sp/hfp/ap/fp, all 16-byte aligned): reject the
zilsd access unless the offset is a multiple of the required alignment,
forcing riscv_split_64bit_move_p to use two 32-bit moves.

Signed-off-by: Ciyan Pan <[email protected]>
gcc/ChangeLog:

        * config/riscv/riscv.cc (riscv_zilsd_valid_mem_p): Double-check address.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/zilsd-align-split-arg-1.c: New test.
        * gcc.target/riscv/zilsd-align-split-arg-2.c: New test.

---
 gcc/config/riscv/riscv.cc                     | 29 +++++++++++++--
 .../riscv/zilsd-align-split-arg-1.c           | 37 +++++++++++++++++++
 .../riscv/zilsd-align-split-arg-2.c           | 22 +++++++++++
 3 files changed, 84 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-2.c

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index f8b54fc4ae68..9fa420a39b41 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1270,10 +1270,31 @@ riscv_zilsd_required_align (void)
 bool
 riscv_zilsd_valid_mem_p (rtx mem, machine_mode mode)
 {
-  return (TARGET_ZILSD
-         && riscv_2x_xlen_mode_p (mode)
-         && MEM_P (mem)
-         && MEM_ALIGN (mem) >= riscv_zilsd_required_align () * BITS_PER_UNIT);
+  if (!TARGET_ZILSD || !riscv_2x_xlen_mode_p (mode) || !MEM_P (mem))
+    return false;
+
+  if (MEM_ALIGN (mem) < riscv_zilsd_required_align () * BITS_PER_UNIT)
+    return false;
+
+  /* MEM_ALIGN can be inaccurate for reconstructed split arguments where the
+     low word is held in an odd register and the high word lives on the
+     incoming stack.  Such accesses materialize at a 4-byte-aligned stack
+     offset, so double-check the address when it is a constant offset from a
+     frame-related pointer (all 16-byte aligned on RISC-V).  */
+  rtx addr = XEXP (mem, 0);
+  if (GET_CODE (addr) == PLUS
+      && REG_P (XEXP (addr, 0))
+      && CONST_INT_P (XEXP (addr, 1)))
+    {
+      unsigned base = REGNO (XEXP (addr, 0));
+      if (base == STACK_POINTER_REGNUM
+         || base == HARD_FRAME_POINTER_REGNUM
+         || base == ARG_POINTER_REGNUM
+         || base == FRAME_POINTER_REGNUM)
+       return (INTVAL (XEXP (addr, 1)) % riscv_zilsd_required_align ()) == 0;
+    }
+
+  return true;
 }
 
 /* Return the effective Zilsd memory access alignment policy.  */
diff --git a/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-1.c 
b/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-1.c
new file mode 100644
index 000000000000..b3a5785862b3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-1.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv32gcv_zilsd -mabi=ilp32d 
-mrvv-vector-bits=scalable -fno-vect-cost-model" } */
+/* { dg-additional-options "-mzilsd-strict-align" } */
+
+#include <stdint-gcc.h>
+
+/* On RV32 an int64_t argument passed after seven pointer arguments is
+   split: the low word lands in the last available argument register (a7)
+   and the high word on the incoming stack.  When the vectoriser later
+   materialises n as a single DImode value, the low word is spilled to a
+   4-byte-aligned stack slot.  With -mzilsd-strict-align the backend must
+   not reload that value with a single misaligned zilsd "ld"; it must split
+   the reload into two 32-bit loads instead, otherwise the access faults on
+   targets that trap on misaligned 64-bit loads.  */
+
+void __attribute__ ((noinline, noclone))
+f6 (uint64_t *__restrict a, uint64_t *__restrict b, uint64_t *__restrict c,
+    uint64_t *__restrict d, uint64_t *__restrict e, uint64_t *__restrict f,
+    uint64_t *__restrict g, int64_t n)
+{
+  for (int64_t i = 0; i < n; ++i)
+    {
+      a[i] = g[i * 6];
+      b[i] = g[i * 6 + 1];
+      c[i] = g[i * 6 + 2];
+      d[i] = g[i * 6 + 3];
+      e[i] = g[i * 6 + 4];
+      f[i] = g[i * 6 + 5];
+    }
+}
+
+/* The spilled low word of n sits at a 4-byte-aligned stack offset (76);
+   it must be reloaded with "lw", never with a single misaligned zilsd
+   "ld".  The vector loop counter, in contrast, lives at an 8-byte-aligned
+   offset (8) and may legitimately use ld/sd.  */
+/* { dg-final { scan-assembler-not {ld\t[a-z0-9]+,76\(sp\)} } } */
+/* { dg-final { scan-assembler-not {sd\t[a-z0-9]+,76\(sp\)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-2.c 
b/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-2.c
new file mode 100644
index 000000000000..248b16b0686e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zilsd-align-split-arg-2.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv32i_zilsd -mabi=ilp32" } */
+/* { dg-additional-options "-mzilsd-strict-align" } */
+
+/* On RV32 an int64_t argument passed after seven pointer arguments is
+   split between registers and the stack: the low word lands in the last
+   available argument register (a7) and the high word on the incoming
+   stack.  Reloading n as a single 64-bit value therefore materialises a
+   zilsd "ld" from a 4-byte-aligned stack slot (offset 12).  Under
+   -mzilsd-strict-align the backend must not emit such a misaligned
+   doubleword load; it must split the reload into two 32-bit loads.  */
+
+long long
+f7 (long long *a, long long *b, long long *c, long long *d,
+    long long *e, long long *f, long long *g, long long n)
+{
+  return n;
+}
+
+/* The misaligned reload must use two 32-bit "lw", never a single zilsd
+   "ld" from the 4-byte-aligned stack offset 12.  */
+/* { dg-final { scan-assembler-not {ld\t[a-z0-9]+,12\(sp\)} } } */
-- 
2.43.0

Reply via email to