Recognize SRLI/SRLIW followed by a matching-width ADD-type operation
when the normalized shift amount is two and the required
producer-consumer dependency holds.  Reuse the common shift classifier
and report wordness from the existing add classifier.

Leave the fusion disabled by default and XFAIL its positive dump
checks until a CPU enables it.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_insn_is_add_type_p): Optionally
        report whether the instruction is a word form.
        (riscv_fuse_srli_add): New function.
        (riscv_fusion_table): Add RISCV_FUSE_SRLI_ADD.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
        RISCV_FUSE_SRLI_ADD.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-srli-add.c: New test.
        * gcc.target/riscv/fusion-word-positive-rtl-srli-add.c: Likewise.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 60 +++++++++++++++--
 gcc/config/riscv/riscv-protos.h               |  1 +
 .../gcc.target/riscv/fusion-srli-add.c        | 32 +++++++++
 .../riscv/fusion-word-positive-rtl-srli-add.c | 67 +++++++++++++++++++
 4 files changed, 153 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-srli-add.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-srli-add.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index ddb86b14e45..c24fb29cbaf 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -328,21 +328,30 @@ riscv_set_is_addw_p (rtx set)
 
 /* Matches an add-type instruction:
      (set (reg rd) (plus (reg rs1) (reg rs2)))
-   or an accepted addw or add.uw RTL form.  */
+   or an accepted addw or add.uw RTL form.  Store whether the instruction is
+   a word form in *WORD_P when requested.  */
 
 static bool
-riscv_insn_is_add_type_p (rtx_insn *insn)
+riscv_insn_is_add_type_p (rtx_insn *insn, bool *word_p = NULL)
 {
   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)));
+  bool is_word_p = false;
+  if (type == TYPE_ARITH && riscv_set_is_add_p (set))
+    is_word_p = TARGET_64BIT && GET_MODE (SET_SRC (set)) == SImode;
+  else if (type == TYPE_ARITH && riscv_set_is_addw_p (set))
+    is_word_p = true;
+  else if (!TARGET_64BIT
+          || type != TYPE_BITMANIP
+          || !riscv_set_is_adduw_p (set))
+    return false;
+
+  if (word_p)
+    *word_p = is_word_p;
+  return true;
 }
 
 /* Matches an mv or li instruction:
@@ -1386,6 +1395,41 @@ riscv_fuse_slli_srli (rtx_insn *prev, rtx_insn *curr)
   return riscv_fuse_shift_pair_p (prev, curr, true, false);
 }
 
+/* Check for RISCV_FUSE_SRLI_ADD fusion.
+   prev (srli/srliw) == (set (reg rd1) (lshiftrt (reg rs1)
+                                                (const_int 2)))
+   curr (one of the following):
+     (add) == (set (reg rd2) (plus (reg rd1) (reg rs2)))
+     (addw) == (set (reg rd2)
+                 (sign_extend (plus (reg rd1) (reg rs2))))
+     (add.uw) == (set (reg rd2) (plus (zero_extend (reg rd1))
+                                  (reg rs2)))
+
+   Constraints:
+     rd1 == rd2
+     both instructions are word forms or both are non-word forms.  */
+
+static bool
+riscv_fuse_srli_add (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)
+      || get_attr_type (prev) != TYPE_SHIFT)
+    return false;
+
+  bool shift_word_p, add_word_p;
+  HOST_WIDE_INT shift_amount;
+  if (!riscv_set_is_shift_type_p (prev_set, LSHIFTRT, &shift_word_p,
+                                 &shift_amount)
+      || !riscv_insn_is_add_type_p (curr, &add_word_p)
+      || shift_word_p != add_word_p
+      || !riscv_fuse_same_dest_p (prev_set, curr_set, true))
+    return false;
+
+  unsigned int mask = shift_word_p ? 0x1f : (TARGET_64BIT ? 0x3f : 0x1f);
+  return (shift_amount & mask) == 2;
+}
+
 /* Check for RISCV_FUSE_B_ALUI fusion.
    prev/curr (one of the following pairs):
      prev (orc.b) == (set (reg rd1)
@@ -1656,6 +1700,8 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
   { RISCV_FUSE_SLLI_SRLI,
     riscv_fuse_slli_srli, "RISCV_FUSE_SLLI_SRLI" },
+  { RISCV_FUSE_SRLI_ADD,
+    riscv_fuse_srli_add, "RISCV_FUSE_SRLI_ADD" },
   { RISCV_FUSE_B_ALUI,
     riscv_fuse_b_alui, "RISCV_FUSE_B_ALUI" },
   { RISCV_FUSE_SUB_SEQZ,
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 2321307484c..c107a574ad6 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -872,6 +872,7 @@ enum riscv_fusion_pairs
   RISCV_FUSE_ANDI_ADD = HOST_WIDE_INT_1U << 16,
   RISCV_FUSE_LOGIC_LOGIC = HOST_WIDE_INT_1U << 17,
   RISCV_FUSE_SLLI_SRLI = HOST_WIDE_INT_1U << 18,
+  RISCV_FUSE_SRLI_ADD = HOST_WIDE_INT_1U << 19,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-srli-add.c 
b/gcc/testsuite/gcc.target/riscv/fusion-srli-add.c
new file mode 100644
index 00000000000..0fe58cbb785
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-srli-add.c
@@ -0,0 +1,32 @@
+/* { 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" } */
+/* No tune enables these fusion pairs yet.  */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_SRLI_ADD" 3 "sched2" { xfail 
*-*-* } } } */
+
+typedef long int64_t;
+typedef unsigned long uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+/* srli by 2 followed by add should fuse.  */
+int64_t
+test_srli_add (uint64_t a, int64_t b)
+{
+  return (int64_t) (a >> 2) + b;
+}
+
+/* srliw by 2 followed by addw should fuse.  */
+int32_t
+test_srliw_addw (uint32_t a, int32_t b)
+{
+  return (int32_t) (a >> 2) + b;
+}
+
+/* srli by 2 followed by add.uw should fuse.  */
+uint64_t
+test_srli_adduw (uint64_t a, uint64_t b)
+{
+  return (uint64_t) (uint32_t) (a >> 2) + b;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-srli-add.c 
b/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-srli-add.c
new file mode 100644
index 00000000000..a05e4e83752
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-srli-add.c
@@ -0,0 +1,67 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_SRLI_ADD" 2 "sched2" { xfail 
*-*-* } } } */
+
+/* Raw SImode srli followed by a same-source addw should fuse.  */
+long __RTL (startwith ("sched2"))
+test_raw_srliw_extended_addw (void)
+{
+(function "test_raw_srliw_extended_addw"
+  (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:SI a0)
+                    (lshiftrt:SI (reg:SI a1)
+                                 (const_int 2))))
+      (cinsn 4 (set (reg:DI a0)
+                    (sign_extend:DI
+                      (plus:SI (reg:SI a0)
+                               (reg:SI a0)))))
+      (cinsn 5 (use (reg/i: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_raw_srliw_extended_addw"
+}
+/* Zero-extended srliw followed by addw expressed via truncate.  */
+long __RTL (startwith ("sched2"))
+test_lowpart_srliw_addw (void)
+{
+(function "test_lowpart_srliw_addw"
+  (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)
+                    (zero_extend:DI
+                      (lshiftrt:SI (reg:SI a1)
+                                   (const_int 2)))))
+      (cinsn 4 (set (reg:DI a0)
+                    (sign_extend:DI
+                      (truncate:SI
+                        (plus:DI (reg:DI a0)
+                                 (reg:DI a2))))))
+      (cinsn 5 (use (reg/i: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_lowpart_srliw_addw"
+}
-- 
2.52.0

Reply via email to