Recognize matching-width SLLI/SLLIW followed by SRLI/SRLIW with the
required register dependency.  Recompose BFEXT and the new fusion from
a shared shift-pair matcher.

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

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_set_is_shift_type_p): New
        function.
        (riscv_fuse_shift_pair_p): Likewise.
        (riscv_fuse_bfext): Use riscv_fuse_shift_pair_p.
        (riscv_fuse_slli_srli): New function.
        (riscv_fusion_table): Add RISCV_FUSE_SLLI_SRLI.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
        RISCV_FUSE_SLLI_SRLI.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 170 ++++++++++++++++--
 gcc/config/riscv/riscv-protos.h               |   1 +
 .../gcc.target/riscv/fusion-slli-srli.c       |  22 +++
 .../fusion-word-positive-rtl-slli-srli.c      |  66 +++++++
 4 files changed, 249 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-slli-srli.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index a4f203347b7..ddb86b14e45 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -480,6 +480,150 @@ riscv_set_is_srai_p (rtx set)
   return riscv_set_is_shift_p (set, ASHIFTRT);
 }
 
+/* Match a scalar shift of CODE, including the equivalent RV64 word forms
+   produced for SLLIW and SRLIW.  Store whether the instruction is a word
+   form in *WORD_P and its effective shift amount in *SHIFT_AMOUNT when
+   requested.  */
+
+static bool
+riscv_set_is_shift_type_p (rtx set, rtx_code code, bool *word_p,
+                          HOST_WIDE_INT *shift_amount = NULL)
+{
+  if (riscv_set_is_shift_p (set, code))
+    {
+      rtx src = SET_SRC (set);
+      *word_p = TARGET_64BIT && GET_MODE (src) == SImode;
+      if (shift_amount)
+       *shift_amount = INTVAL (XEXP (src, 1));
+      return true;
+    }
+
+  if (!TARGET_64BIT
+      || riscv_regno (SET_DEST (set)) == INVALID_REGNUM)
+    return false;
+
+  rtx src = SET_SRC (set);
+  if (code == ASHIFT
+      && GET_CODE (src) == SIGN_EXTEND
+      && GET_MODE (src) == DImode)
+    {
+      src = XEXP (src, 0);
+      if (GET_CODE (src) == ASHIFT
+         && GET_MODE (src) == SImode
+         && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM
+         && CONST_INT_P (XEXP (src, 1)))
+       {
+         *word_p = true;
+         if (shift_amount)
+           *shift_amount = INTVAL (XEXP (src, 1)) & 0x1f;
+         return true;
+       }
+
+      if (GET_CODE (src) == AND
+         && GET_MODE (src) == SImode
+         && GET_CODE (XEXP (src, 0)) == ROTATERT
+         && riscv_regno (XEXP (XEXP (src, 0), 0)) != INVALID_REGNUM
+         && CONST_INT_P (XEXP (XEXP (src, 0), 1))
+         && CONST_INT_P (XEXP (src, 1)))
+       {
+         *word_p = true;
+         if (shift_amount)
+           *shift_amount
+             = (32 - (INTVAL (XEXP (XEXP (src, 0), 1)) & 0x1f)) & 0x1f;
+         return true;
+       }
+    }
+
+  if (code != LSHIFTRT)
+    return false;
+
+  rtx_code src_code = GET_CODE (src);
+  if ((src_code == ZERO_EXTEND || src_code == SIGN_EXTEND)
+      && GET_MODE (src) == DImode)
+    {
+      rtx_code extend_code = src_code;
+      src = XEXP (src, 0);
+      if (GET_CODE (src) == LSHIFTRT
+         && GET_MODE (src) == SImode
+         && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM
+         && CONST_INT_P (XEXP (src, 1))
+         && (extend_code == SIGN_EXTEND
+             || (INTVAL (XEXP (src, 1)) & 0x1f) != 0))
+       {
+         *word_p = true;
+         if (shift_amount)
+           *shift_amount = INTVAL (XEXP (src, 1)) & 0x1f;
+         return true;
+       }
+      return false;
+    }
+
+  if (src_code == ZERO_EXTRACT
+      && GET_MODE (src) == DImode
+      && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM
+      && CONST_INT_P (XEXP (src, 1))
+      && CONST_INT_P (XEXP (src, 2))
+      && INTVAL (XEXP (src, 2)) > 0
+      && INTVAL (XEXP (src, 1)) + INTVAL (XEXP (src, 2)) == 32)
+    {
+      *word_p = true;
+      if (shift_amount)
+       *shift_amount = INTVAL (XEXP (src, 2));
+      return true;
+    }
+
+  if (src_code == LT
+      && GET_MODE (src) == DImode
+      && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM
+      && GET_MODE (XEXP (src, 0)) == SImode
+      && XEXP (src, 1) == const0_rtx)
+    {
+      *word_p = true;
+      if (shift_amount)
+       *shift_amount = 31;
+      return true;
+    }
+
+  return false;
+}
+
+/* Match a left-shift/right-shift fusion pair.  ALLOW_WORD_P accepts the
+   equivalent RV64 word forms and requires both instructions to have the same
+   wordness.  ALLOW_ARITHMETIC_P accepts an arithmetic right shift.  */
+
+static bool
+riscv_fuse_shift_pair_p (rtx_insn *prev, rtx_insn *curr,
+                          bool allow_word_p, bool allow_arithmetic_p)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)
+      || get_attr_type (prev) != TYPE_SHIFT
+      || get_attr_type (curr) != TYPE_SHIFT)
+    return false;
+
+  bool prev_word_p = false;
+  bool curr_word_p = false;
+  bool prev_match_p
+    = (allow_word_p
+       ? riscv_set_is_shift_type_p (prev_set, ASHIFT, &prev_word_p)
+       : riscv_set_is_slli_p (prev_set));
+  bool curr_match_p
+    = (allow_word_p
+       ? riscv_set_is_shift_type_p (curr_set, LSHIFTRT, &curr_word_p)
+       : riscv_set_is_srli_p (curr_set));
+
+  if (!curr_match_p && allow_arithmetic_p)
+    curr_match_p = riscv_set_is_srai_p (curr_set);
+
+  if (!prev_match_p
+      || !curr_match_p
+      || prev_word_p != curr_word_p
+      || !riscv_fuse_same_dest_p (prev_set, curr_set, true))
+    return false;
+
+  return true;
+}
+
 /* Load/store classes used by fusion checks.  */
 enum sched_fusion_type
 {
@@ -1223,19 +1367,23 @@ riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr)
 static bool
 riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
 {
-  rtx prev_set, curr_set;
-  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
-    return false;
+  return riscv_fuse_shift_pair_p (prev, curr, false, true);
+}
 
-  if (!riscv_fuse_same_dest_p (prev_set, curr_set, true))
-    return false;
+/* Check for RISCV_FUSE_SLLI_SRLI fusion.
+   prev (slli/slliw) == (set (reg rd1) (ashift (reg rs1)
+                                             (const_int shamt1)))
+   curr (srli/srliw) == (set (reg rd2) (lshiftrt (reg rd1)
+                                               (const_int shamt2)))
 
-  if (riscv_set_is_slli_p (prev_set)
-      && (riscv_set_is_srli_p (curr_set)
-         || riscv_set_is_srai_p (curr_set)))
-    return true;
+   Constraints:
+     rd1 == rd2
+     both instructions are word forms or both are non-word forms.  */
 
-  return false;
+static bool
+riscv_fuse_slli_srli (rtx_insn *prev, rtx_insn *curr)
+{
+  return riscv_fuse_shift_pair_p (prev, curr, true, false);
 }
 
 /* Check for RISCV_FUSE_B_ALUI fusion.
@@ -1506,6 +1654,8 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_aligned_std, "RISCV_FUSE_ALIGNED_STD" },
   { RISCV_FUSE_BFEXT,
     riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
+  { RISCV_FUSE_SLLI_SRLI,
+    riscv_fuse_slli_srli, "RISCV_FUSE_SLLI_SRLI" },
   { 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 46f148a9311..2321307484c 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -871,6 +871,7 @@ enum riscv_fusion_pairs
   RISCV_FUSE_ADD_ANDI = HOST_WIDE_INT_1U << 15,
   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,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c 
b/gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c
new file mode 100644
index 00000000000..5a0665e934a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c
@@ -0,0 +1,22 @@
+/* { 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" } */
+/* No tune enables these fusion pairs yet.  */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_SLLI_SRLI" 2 "sched2" { xfail 
*-*-* } } } */
+
+typedef unsigned long uint64_t;
+typedef unsigned int uint32_t;
+
+/* slli + srli should fuse.  */
+uint64_t
+test_slli_srli (uint64_t a)
+{
+  return (a << 4) >> 8;
+}
+
+/* slliw + srliw should fuse.  */
+uint32_t
+test_slliw_srliw (uint32_t a)
+{
+  return (a << 4) >> 8;
+}
diff --git 
a/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-slli-srli.c 
b/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-slli-srli.c
new file mode 100644
index 00000000000..d227891501f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-word-positive-rtl-slli-srli.c
@@ -0,0 +1,66 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc_zbb -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_SLLI_SRLI" 2 "sched2" { xfail 
*-*-* } } } */
+
+/* Raw SImode slli followed by raw SImode srli should fuse.  */
+int __RTL (startwith ("sched2"))
+test_raw_slliw_srliw (void)
+{
+(function "test_raw_slliw_srliw"
+  (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)
+                    (ashift:SI (reg:SI a1)
+                               (const_int 4))))
+      (cinsn 4 (set (reg:SI a0)
+                    (lshiftrt:SI (reg:SI a0)
+                                 (const_int 8))))
+      (cinsn 5 (use (reg/i:SI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:SI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_raw_slliw_srliw"
+}
+/* Rotate-and-mask slliw followed by a sign-bit srliw should fuse.  */
+long __RTL (startwith ("sched2"))
+test_slliw_srliw_31 (void)
+{
+(function "test_slliw_srliw_31"
+  (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)
+                    (sign_extend:DI
+                      (and:SI
+                        (rotatert:SI (reg:SI a1)
+                                     (const_int 28))
+                        (const_int -16)))))
+      (cinsn 4 (set (reg:DI a0)
+                    (lt:DI (reg:SI a0)
+                           (const_int 0))))
+      (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_slliw_srliw_31"
+}
-- 
2.52.0

Reply via email to