Allow LDINDEXED to match ADD, ADDW, or ADD.UW followed by an
undisplaced scalar integer load.  The load may overwrite the computed
address or use a different destination.

Share the add-plus-memory structural matching with the following
ADD_ST fusion patch.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_set_is_addw_p): New function.
        (riscv_insn_is_add_type_p): Likewise.
        (riscv_fuse_add_mem_p): Likewise.
        (riscv_fuse_ldindexed): Use the common matcher and accept ADDW,
        ADD.UW, and either load-destination relationship.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-add-ldst.c: New test.
        * gcc.target/riscv/fusion-addw-adduw-ldst.c: Likewise.
        * gcc.target/riscv/fusion-load-dest-equal-rtl-add-ldst.c: Likewise.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 95 +++++++++++++++----
 .../gcc.target/riscv/fusion-add-ldst.c        | 18 ++++
 .../gcc.target/riscv/fusion-addw-adduw-ldst.c | 67 +++++++++++++
 .../fusion-load-dest-equal-rtl-add-ldst.c     | 33 +++++++
 4 files changed, 193 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-addw-adduw-ldst.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-load-dest-equal-rtl-add-ldst.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 758e576706f..840bad47002 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -308,6 +308,43 @@ riscv_set_is_addiw_p (rtx set, rtx *src0 = NULL)
   return false;
 }
 
+/* Matches an addw:
+     (set (reg:DI rd)
+         (sign_extend:DI (plus:SI (reg:SI rs1) (reg:SI rs2))))
+   or an equivalent word-add RTL form.  */
+
+static bool
+riscv_set_is_addw_p (rtx set)
+{
+  if (!TARGET_64BIT)
+    return false;
+
+  rtx src;
+  return (riscv_set_extract_word_binary_p (set, PLUS, &src)
+         && REG_P (XEXP (src, 0))
+         && REG_P (XEXP (src, 1))
+         && riscv_regno (SET_DEST (set)) != INVALID_REGNUM);
+}
+
+/* Matches an add-type instruction:
+     (set (reg rd) (plus (reg rs1) (reg rs2)))
+   or an accepted addw or add.uw RTL form.  */
+
+static bool
+riscv_insn_is_add_type_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  enum attr_type type = get_attr_type (insn);
+  return ((type == TYPE_ARITH
+          && (riscv_set_is_add_p (set) || riscv_set_is_addw_p (set)))
+         || (TARGET_64BIT
+             && type == TYPE_BITMANIP
+             && riscv_set_is_adduw_p (set)));
+}
+
 /* Matches an mv or li instruction:
      (set (reg rd) (reg rs1))
    or:
@@ -581,6 +618,35 @@ riscv_fuse_mem_p (rtx_insn *insn, struct 
riscv_fusion_mem_info *info)
   return true;
 }
 
+/* Extract an add-type instruction followed by an integer load or store that
+   uses the add result as an undisplaced address.  */
+
+static bool
+riscv_fuse_add_mem_p (rtx_insn *prev, rtx_insn *curr,
+                       rtx *add_set_out, rtx *mem_set_out,
+                       struct riscv_fusion_mem_info *mem)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
+    return false;
+
+  rtx add_dest = SET_DEST (prev_set);
+  if (!riscv_insn_is_add_type_p (prev)
+      || !riscv_fuse_mem_p (curr, mem)
+      || mem->fp_p
+      || mem->addr.type != ADDRESS_REG
+      || !CONST_INT_P (mem->addr.offset)
+      || INTVAL (mem->addr.offset) != 0
+      || !riscv_fuse_same_reg_p (add_dest, mem->addr.reg))
+    return false;
+
+  if (add_set_out)
+    *add_set_out = prev_set;
+  if (mem_set_out)
+    *mem_set_out = curr_set;
+  return true;
+}
+
 /* Check the common RTL for ZEXTW, ZEXTWS and ZEXTH fusion.  */
 
 static bool
@@ -684,38 +750,27 @@ riscv_fuse_zexth (rtx_insn *prev, rtx_insn *curr)
 }
 
 /* Check for RISCV_FUSE_LDINDEXED fusion.
-   prev (add) == (set (reg rd1)
-                     (plus (reg rs1) (reg rs2)))
+   prev (one of the following):
+     (add) == (set (reg rd1) (plus (reg rs1) (reg rs2)))
+     (addw) == (set (reg rd1) (sign_extend (plus:SI (reg rs1)
+                                                   (reg rs2))))
+     (add.uw) == (set (reg rd1) (plus (zero_extend (reg rs1))
+                                     (reg rs2)))
    curr (one of the following):
      (load) == (set (reg rd2) (mem (reg rd1)))
      (load) == (set (reg rd2)
                    (any_extend (mem (reg rd1))))
 
    Constraints:
-     rd1 == rd2.  */
+     offset == 0.  */
 
 static bool
 riscv_fuse_ldindexed (rtx_insn *prev, rtx_insn *curr)
 {
-  rtx prev_set, curr_set;
-  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
-    return false;
-
-  if (!riscv_fuse_same_dest_p (prev_set, curr_set))
-    return false;
-
   struct riscv_fusion_mem_info mem;
 
-  if (riscv_fuse_mem_p (curr, &mem)
-      && mem.type != SCHED_FUSION_ST
-      && !mem.fp_p
-      && mem.addr.type == ADDRESS_REG
-      && INTVAL (mem.addr.offset) == 0
-      && riscv_set_is_add_p (prev_set)
-      && riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set)))
-    return true;
-
-  return false;
+  return (riscv_fuse_add_mem_p (prev, curr, NULL, NULL, &mem)
+         && mem.type != SCHED_FUSION_ST);
 }
 
 /* Check for RISCV_FUSE_EXPANDED_LD fusion.
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c 
b/gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c
new file mode 100644
index 00000000000..699e5369c24
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64g -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LDINDEXED" 1 "sched2" { xfail 
*-*-* } } } */
+
+typedef long int64_t;
+
+extern void use_addr (void *);
+
+/* add + ld should fuse.  */
+int64_t
+test_add_ld (int64_t *base, long off)
+{
+  int64_t *p = (int64_t *) ((char *) base + off);
+  int64_t v = *p;
+  use_addr (p);
+  return v;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-addw-adduw-ldst.c 
b/gcc/testsuite/gcc.target/riscv/fusion-addw-adduw-ldst.c
new file mode 100644
index 00000000000..9cb8dbcc457
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-addw-adduw-ldst.c
@@ -0,0 +1,67 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc_zba -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LDINDEXED" 2 "sched2" { xfail 
*-*-* } } } */
+
+/* addw + ld should fuse.  */
+long __RTL (startwith ("sched2"))
+test_addw_ld (void)
+{
+(function "test_addw_ld"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      ;; addw a0, a1, a2
+      (cinsn 3 (set (reg:DI a0)
+                    (sign_extend:DI
+                      (plus:SI (reg:SI a1)
+                               (reg:SI a2)))))
+      ;; ld a1, 0(a0)
+      (cinsn 4 (set (reg:DI a1)
+                    (mem:DI (reg:DI a0) [0  S8 A64])))
+      (cinsn 5 (use (reg/i:DI a1)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a1)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_addw_ld"
+}
+
+/* add.uw + ld should fuse.  */
+long __RTL (startwith ("sched2"))
+test_adduw_ld (void)
+{
+(function "test_adduw_ld"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      ;; add.uw a0, a1, a2
+      (cinsn 3 (set (reg:DI a0)
+                    (plus:DI (zero_extend:DI (reg:SI a1))
+                             (reg:DI a2))))
+      ;; ld a1, 0(a0)
+      (cinsn 4 (set (reg:DI a1)
+                    (mem:DI (reg:DI a0) [0  S8 A64])))
+      (cinsn 5 (use (reg/i:DI a1)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a1)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_adduw_ld"
+}
diff --git 
a/gcc/testsuite/gcc.target/riscv/fusion-load-dest-equal-rtl-add-ldst.c 
b/gcc/testsuite/gcc.target/riscv/fusion-load-dest-equal-rtl-add-ldst.c
new file mode 100644
index 00000000000..824b78b461c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-load-dest-equal-rtl-add-ldst.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -mtune=xt-c9501fdvt 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LDINDEXED" 1 "sched2" { xfail 
*-*-* } } } */
+
+/* add and load writing the same register should fuse.  */
+long __RTL (startwith ("sched2"))
+test_add_load_same_destination (void)
+{
+(function "test_add_load_same_destination"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a0)
+                    (plus:DI (reg:DI a1)
+                             (reg:DI a2))))
+      (cinsn 4 (set (reg:DI a0)
+                    (mem:DI (reg:DI a0) [0  S8 A64])))
+      (cinsn 5 (use (reg:DI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_add_load_same_destination"
+}
-- 
2.52.0

Reply via email to