From: Kyrylo Tkachov <[email protected]>

A spilled vector can be stored in one mode and immediately reloaded in
another.  CSELIB's memory lookup requires an exact mode match, so postreload
misses an available register value.  For example:

  mem:VNx4SI (addr) = v7:VNx4SI;
  v23:VNx2DI = mem:VNx2DI (addr);

can use a register copy for the second set.  The store must remain when
other paths still load its value.

Search the existing address-value list for an equal-sized vector value in
a register.  Require the target's permission for a bit-preserving mode change 
and a
valid subreg, then use the existing cost and instruction validation rules.
Retain volatile loads and honor float-store.  Expose elt_list alongside
the other CSELIB list structures.

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill

gcc/ChangeLog:

        * cselib.cc (elt_list): Move to cselib.h.
        * cselib.h (elt_list): Move from cselib.cc.
        * postreload.cc (reload_cse_simplify_mem): New.
        (reload_cse_simplify_set): Try reload_cse_simplify_mem.

gcc/testsuite/ChangeLog:

        * gcc.dg/rtl/aarch64/postreload-vector-forward.c: New test.
        * gcc.dg/rtl/aarch64/postreload-vector-forward-be.c: Likewise.
        * gcc.target/aarch64/sve/spill-mode-change.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/cselib.cc                                 |   7 --
 gcc/cselib.h                                  |   7 ++
 gcc/postreload.cc                             |  75 ++++++++++++-
 .../aarch64/postreload-vector-forward-be.c    |  61 ++++++++++
 .../rtl/aarch64/postreload-vector-forward.c   | 105 ++++++++++++++++++
 .../aarch64/sve/spill-mode-change.c           |  94 ++++++++++++++++
 6 files changed, 340 insertions(+), 9 deletions(-)
 create mode 100644 
gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward-be.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/spill-mode-change.c

diff --git a/gcc/cselib.cc b/gcc/cselib.cc
index 3e48fe5dcb0..e86abf3c6dd 100644
--- a/gcc/cselib.cc
+++ b/gcc/cselib.cc
@@ -36,13 +36,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "predict.h"
 #include "rtl-iter.h"
 
-/* A list of cselib_val structures.  */
-struct elt_list
-{
-  struct elt_list *next;
-  cselib_val *elt;
-};
-
 static bool cselib_record_memory;
 static bool cselib_preserve_constants;
 static bool cselib_any_perm_equivs;
diff --git a/gcc/cselib.h b/gcc/cselib.h
index 6493303f7ff..29765d33f30 100644
--- a/gcc/cselib.h
+++ b/gcc/cselib.h
@@ -47,6 +47,13 @@ struct cselib_val
   struct cselib_val *next_containing_mem;
 };
 
+/* A list of cselib_val structures.  */
+struct elt_list
+{
+  struct elt_list *next;
+  cselib_val *elt;
+};
+
 /* A list of rtl expressions that hold the same value.  */
 struct elt_loc_list {
   /* Next element in the list.  */
diff --git a/gcc/postreload.cc b/gcc/postreload.cc
index 475b5889629..cb76f4851a5 100644
--- a/gcc/postreload.cc
+++ b/gcc/postreload.cc
@@ -355,6 +355,77 @@ reload_cse_regs_1 (void)
     cleanup_cfg (0);
 }
 
+/* Try to replace the memory source of SET in INSN with a register that
+   holds the same bytes in another mode.  Return nonzero if a replacement
+   is added to the current change group.  */
+
+static int
+reload_cse_simplify_mem (rtx set, rtx_insn *insn)
+{
+  rtx mem = SET_SRC (set);
+  if (!MEM_P (mem) || MEM_VOLATILE_P (mem))
+    return 0;
+
+  machine_mode mode = GET_MODE (mem);
+  if (!VECTOR_MODE_P (mode)
+      || (FLOAT_MODE_P (mode) && flag_float_store))
+    return 0;
+
+  rtx address = XEXP (mem, 0);
+  machine_mode address_mode = GET_MODE (address);
+  if (address_mode == VOIDmode)
+    address_mode = Pmode;
+  cselib_val *addr = cselib_lookup (address, address_mode, 0, mode);
+  if (!addr)
+    return 0;
+  addr = canonical_cselib_val (addr);
+
+  reg_class_t dclass = REGNO_REG_CLASS (true_regnum (SET_DEST (set)));
+  int best_cost = memory_move_cost (mode, dclass, true);
+  rtx best = NULL_RTX;
+  for (elt_list *l = addr->addr_list; l; l = l->next)
+    {
+      machine_mode from = GET_MODE (l->elt->val_rtx);
+      if (from == mode || !VECTOR_MODE_P (from)
+         || !known_eq (GET_MODE_SIZE (from), GET_MODE_SIZE (mode)))
+       continue;
+
+      bool same_address_space = false;
+      for (elt_loc_list *loc = l->elt->locs; loc; loc = loc->next)
+       if (MEM_P (loc->loc)
+           && MEM_ADDR_SPACE (loc->loc) == MEM_ADDR_SPACE (mem))
+         {
+           same_address_space = true;
+           break;
+         }
+      if (!same_address_space)
+       continue;
+
+      /* The target must permit a bitcast that preserves the common bits.
+        This excludes incompatible register layouts, such as big-endian
+        SVE modes with different element sizes.  */
+      for (elt_loc_list *loc = l->elt->locs; loc; loc = loc->next)
+       if (REG_P (loc->loc)
+           && REG_CAN_CHANGE_MODE_P (REGNO (loc->loc), from, mode))
+         {
+           gcc_checking_assert (HARD_REGISTER_P (loc->loc));
+           rtx reg = simplify_subreg (mode, loc->loc, from, 0);
+           if (!reg || !REG_P (reg))
+             continue;
+           int cost = register_move_cost (mode,
+                                          REGNO_REG_CLASS (REGNO (reg)),
+                                          dclass);
+           if (cost <= best_cost)
+             best = reg, best_cost = cost;
+         }
+    }
+  if (!best)
+    return 0;
+
+  validate_unshare_change (insn, &SET_SRC (set), best, 1);
+  return 1;
+}
+
 /* Try to simplify a single SET instruction.  SET is the set pattern.
    INSN is the instruction it came from.
    This function only handles one case: if we set a register to a value
@@ -395,7 +466,7 @@ reload_cse_simplify_set (rtx set, rtx_insn *insn)
 
   val = cselib_lookup (src, GET_MODE (SET_DEST (set)), 0, VOIDmode);
   if (! val)
-    return 0;
+    return reload_cse_simplify_mem (set, insn);
 
   /* If memory loads are cheaper than register copies, don't change them.  */
   if (MEM_P (src))
@@ -476,7 +547,7 @@ reload_cse_simplify_set (rtx set, rtx_insn *insn)
        }
     }
 
-  return did_change;
+  return did_change ? did_change : reload_cse_simplify_mem (set, insn);
 }
 
 /* Try to replace operands in INSN with equivalent values that are already
diff --git a/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward-be.c 
b/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward-be.c
new file mode 100644
index 00000000000..bd7c1651b1e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward-be.c
@@ -0,0 +1,61 @@
+/* { dg-do compile { target aarch64*-*-* } } */
+/* { dg-options "-O2 -mbig-endian -march=armv8.2-a+sve -msve-vector-bits=256" 
} */
+
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/* A full SVE vector and an Advanced SIMD pair have incompatible register
+   layouts.  Advanced SIMD whole-register moves permit mode changes even
+   on big-endian targets.  */
+
+#pragma GCC target "+sve"
+
+/*
+** forward_sve:
+**     str     z0, \[x0\]
+**     ldp     q1, q2, \[x0\]
+**     stp     q1, q2, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+forward_sve (void)
+{
+(function "forward_sve"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 5 (set (reg:VNx16BI p0)
+       (const_vector:VNx16BI [(const_int 1) repeated x32])))
+      (cinsn 2 (set (mem:VNx16QI (reg:DI x0) [0 S32 A128])
+       (unspec:VNx16QI [(reg:VNx16BI p0) (reg:VNx16QI v0)]
+         UNSPEC_PRED_X)))
+      (cinsn 3 (set (reg:V2x16QI v1) (mem:V2x16QI (reg:DI x0) [0 S32 A128])))
+      (cinsn 4 (set (mem:V2x16QI (reg:DI x1) [0 S32 A128]) (reg:V2x16QI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
+/*
+** forward_advsimd:
+**     str     q0, \[x0\]
+**     str     q0, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+forward_advsimd (void)
+{
+(function "forward_advsimd"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem:V4SI (reg:DI x0) [0 S16 A128]) (reg:V4SI v0)))
+      (cinsn 3 (set (reg:V2DI v1) (mem:V2DI (reg:DI x0) [0 S16 A128])))
+      (cinsn 4 (set (mem:V2DI (reg:DI x1) [0 S16 A128]) (reg:V2DI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
diff --git a/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward.c 
b/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward.c
new file mode 100644
index 00000000000..27b71889a23
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/aarch64/postreload-vector-forward.c
@@ -0,0 +1,105 @@
+/* { dg-do compile { target aarch64*-*-* } } */
+/* { dg-options "-O2 -mlittle-endian" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/* Start before IRA so that reload initializes dataflow state.  The hard
+   registers keep the store and reload modes unchanged until postreload.  */
+
+/*
+** forward:
+**     str     q0, \[x0\]
+**     str     q0, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+forward (void)
+{
+(function "forward"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem:V4SI (reg:DI x0) [0 S16 A128]) (reg:V4SI v0)))
+      (cinsn 3 (set (reg:V2DI v1) (mem:V2DI (reg:DI x0) [0 S16 A128])))
+      (cinsn 4 (set (mem:V2DI (reg:DI x1) [0 S16 A128]) (reg:V2DI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
+/*
+** clobbered:
+**     str     q0, \[x0\]
+**     ldr     q1, \[x0\]
+**     str     q1, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+clobbered (void)
+{
+(function "clobbered"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem:V4SI (reg:DI x0) [0 S16 A128]) (reg:V4SI v0)))
+      (cinsn 7 (set (reg:V4SI v0) (reg:V4SI v2)))
+      (cinsn 3 (set (reg:V2DI v1) (mem:V2DI (reg:DI x0) [0 S16 A128])))
+      (cinsn 4 (set (mem:V2DI (reg:DI x1) [0 S16 A128]) (reg:V2DI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
+/*
+** alias_store:
+**     str     q0, \[x0\]
+**     str     w2, \[x0\]
+**     ldr     q1, \[x0\]
+**     str     q1, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+alias_store (void)
+{
+(function "alias_store"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem:V4SI (reg:DI x0) [0 S16 A128]) (reg:V4SI v0)))
+      (cinsn 7 (set (mem:SI (reg:DI x0) [0 S4 A32]) (reg:SI x2)))
+      (cinsn 3 (set (reg:V2DI v1) (mem:V2DI (reg:DI x0) [0 S16 A128])))
+      (cinsn 4 (set (mem:V2DI (reg:DI x1) [0 S16 A128]) (reg:V2DI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
+/*
+** volatile_load:
+**     str     q0, \[x0\]
+**     ldr     q1, \[x0\]
+**     str     q1, \[x1\]
+**     ret
+*/
+void __RTL (startwith ("ira"))
+volatile_load (void)
+{
+(function "volatile_load"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem:V4SI (reg:DI x0) [0 S16 A128]) (reg:V4SI v0)))
+      (cinsn 3 (set (reg:V2DI v1) (mem/v:V2DI (reg:DI x0) [0 S16 A128])))
+      (cinsn 4 (set (mem:V2DI (reg:DI x1) [0 S16 A128]) (reg:V2DI v1)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/spill-mode-change.c 
b/gcc/testsuite/gcc.target/aarch64/sve/spill-mode-change.c
new file mode 100644
index 00000000000..3230976af0c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/spill-mode-change.c
@@ -0,0 +1,94 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv9-a+sve2 -mtune=generic-armv9-a 
-msve-vector-bits=256 -mautovec-preference=sve-only -fno-ivopts 
-fno-align-functions" } */
+/* { dg-require-effective-target aarch64_little_endian } */
+
+/* Reuse a stored vector across a change in element size.  */
+
+#include <arm_sve.h>
+
+typedef svuint8_t v8 __attribute__ ((arm_sve_vector_bits (256)));
+
+static inline void
+f (v8 *a)
+{
+  for (int i = 0; i < 16; i += 8)
+    for (int j = 0; j < 4; j++)
+      {
+        svuint32_t x = svtrn1_u32 (svreinterpret_u32 (a[i + j]),
+                                 svreinterpret_u32 (a[i + j + 4]));
+        svuint32_t y = svtrn2_u32 (svreinterpret_u32 (a[j]),
+                                 svreinterpret_u32 (a[i + j]));
+        a[i + j] = svreinterpret_u8 (x);
+        a[i + j + 4] = svreinterpret_u8 (y);
+      }
+  for (int i = 0; i < 8; i++)
+    {
+      v8 x = svreinterpret_u8 (svtrn1_u64 (svreinterpret_u64 (a[i]),
+                                         svreinterpret_u64 (a[i + 8])));
+      a[i + 8] = svreinterpret_u8 (svtrn2_u64 (svreinterpret_u64 (a[i]),
+                                             svreinterpret_u64 (a[i + 8])));
+      a[i] = x;
+    }
+}
+
+void
+g (uint8_t *a, long b, uint32_t *c, uint64_t *d, uint8_t *e)
+{
+  unsigned h = c[10];
+  svbool_t p = svptrue_pat_b8 (SV_VL32);
+  svbool_t q = svptrue_pat_b8 (SV_VL16);
+  svbool_t r = svptrue_pat_b32 (SV_VL8);
+  v8 t = svld1_u8 (p, e);
+#define T(x) svtbl_u8 (svuzp1_u8 (svuzp1_u8 (svreinterpret_u8 (x), \
+                       svdup_u8 (0)), svdup_u8 (0)), t)
+  uint32_t k = d[1];
+  for (int i = 0; i < 32; i += 8)
+    {
+      v8 v0 = svreinterpret_u8 (svinsr_n_u64 (svdup_u64 (0), 0));
+      svuint32_t v1 = svunpklo_u32 (svunpklo_u16 (v0));
+      svuint32_t v2 = svadd_n_u32_x (r, svindex_u32 (0, 1), i);
+      svuint32_t v3 = svlsl_u32_x (r, svdup_u32 (1), v2);
+      svbool_t p0 = svand_z (
+          r, svcmpne_n_u32 (r, svand_u32_x (r, svdup_u32 (h), v3), 0),
+          svcmpne_n_u32 (r, v1, 0));
+      svuint32_t v4 = svsel_u32 (p0, svdup_u32 (1), svdup_u32 (0));
+      svbool_t p1 = svcmpne_n_u8 (p, T (v4), 0);
+      if (svptest_any (p, p1))
+        continue;
+      uint8_t *s = a + b, *u = s + 7;
+      v8 v[16];
+      for (int j = 0; j < 16; j++)
+        {
+          v[j] = svsplice_u8 (q, svld1_u8 (q, s), svld1_u8 (q, u));
+          u += b;
+        }
+      f (v);
+      svuint32_t v5 = svmax_n_u32_x (r, svdup_u32 (k), 1);
+      svuint32_t v6 = svlsr_n_u32_x (r, v1, 4);
+      svbool_t p2 = svcmpne_n_u32 (
+          r, svand_u32_x (r, svdup_u32 (c[1]), v3), 0);
+      svuint32_t v7 = svsel_u32 (p2, svdup_u32 (8), svdup_u32 (4));
+      v8 v15 = T (v5), v9 = T (v6), v10 = T (v7);
+      svbool_t p3 = svcmple_u8 (p, v[7], v15);
+      v8 v11 = svabd_u8_x (p, v[6], v[7]);
+      v11 = svqadd_u8 (v11, svlsr_n_u8_x (
+          p, svabd_u8_x (p, v[9], v[8]), 1));
+      svbool_t p4 = svcmple_u8 (p, v11, v15);
+      svbool_t p5 = svand_z (p, svand_z (p, p3, p4), p1);
+      if (svptest_any (p, p5))
+        continue;
+      svbool_t p6 = svcmpgt_u8 (p, v[7], v9);
+      svbool_t p7 = svcmpge_n_u8 (p, v10, 6);
+      v8 v12 = svdup_u8 (128);
+      svint8_t v13 = svqsub_s8_m (p6, svreinterpret_s8 (v12), svdup_s8 (0));
+      v8 v14 = sveor_u8_x (p, svreinterpret_u8 (v13), v12);
+      v[8] = svsel_u8 (p7, v14, v[8]);
+      for (int j = 0; j < 16; j++)
+        {
+          svst1_u8 (q, s, v[j]);
+          s += b;
+        }
+    }
+}
+
+/* { dg-final { scan-assembler-not {\tstr\t(z[0-9]+), (\[sp, #[0-9]+, mul 
vl\])\n\tldr\t\1, \2} } } */
-- 
2.50.1 (Apple Git-155)

Reply via email to