[PATCH v5 09/18] RISC-V: Add SLLI_SRLI macro-fusion recognition

Jin Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.