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