Add 16 new macro-fusion pair recognizers supported by the XuanTie
C950 microarchitecture. The 16 new fusion flags are defined in
riscv_fusion_pairs:
RISCV_FUSE_SUB_SEQZ - sub/subw + seqz/snez
RISCV_FUSE_ADD_ANDI - add/addi type + andi
RISCV_FUSE_ANDI_ADD - andi + add/addi type
RISCV_FUSE_LOGIC_LOGIC - logic + logic (at most 2 src regs)
RISCV_FUSE_SLLI_SRLI - slli[w] + srli[w]
RISCV_FUSE_SRLI_ADD - srli + add / srliw + addw
RISCV_FUSE_PREINDEX_LD/ST - addi (self-inc) + load/store
RISCV_FUSE_POSTINDEX_LD/ST - load/store + addi (self-inc)
RISCV_FUSE_LDST_PAIR_INC/DEC - consecutive integer load/store pair
RISCV_FUSE_FLDFST_PAIR_INC/DEC - consecutive FP load/store pair
RISCV_FUSE_ADD_LD/ST - add + integer load/store (imm=0)
No CPU is wired to enable these flags yet; that is done in a
subsequent patch. Xfail tests for xt-c9501fdvt are included.
gcc/ChangeLog:
* config/riscv/riscv-fusion.cc (riscv_src_regno): New.
(riscv_fusion_same_dest_p): Add used_in_src_p parameter;
check source dependency when true.
(riscv_set_is_addw_p): New.
(riscv_set_is_addiw_p): New.
(riscv_set_is_add_addi_p): New; compose from primitives.
(riscv_set_is_andi_type_p): New.
(riscv_set_is_logical_type_p): New; optional src0/src1 out.
(riscv_set_is_slli_p): New.
(riscv_set_is_slliw_p): New.
(riscv_set_is_srli_p): New.
(riscv_set_is_srliw_p): New; recognize two RTL forms.
(sched_fusion_type): New enum, moved into riscv-fusion.cc.
(riscv_fusion_load_store): New; classify load/store insns.
(riscv_fuse_sub_seqz): New.
(riscv_fuse_add_andi): New.
(riscv_fuse_andi_add): New.
(riscv_fuse_logic_logic): New.
(riscv_fuse_slli_srli): New.
(riscv_fuse_srli_add): New.
(riscv_fuse_preindex_ldst_1): New.
(riscv_fuse_preindex_ld): New.
(riscv_fuse_preindex_st): New.
(riscv_fuse_postindex_ldst_1): New.
(riscv_fuse_postindex_ld): New.
(riscv_fuse_postindex_st): New.
(riscv_fuse_ldst_pair_1): New; handle integer and FP
load/store pairs via fp_p parameter.
(riscv_fuse_ldst_pair_inc): New.
(riscv_fuse_ldst_pair_dec): New.
(riscv_fuse_fldfst_pair_inc): New.
(riscv_fuse_fldfst_pair_dec): New.
(riscv_fuse_add_ldst_1): New.
(riscv_fuse_add_ld): New.
(riscv_fuse_add_st): New.
(riscv_fusion_table): Add entries for all 16 new checkers.
* config/riscv/riscv-protos.h (sched_fusion_type): Remove;
moved to riscv-fusion.cc.
(riscv_fusion_pairs): Add RISCV_FUSE_SUB_SEQZ,
RISCV_FUSE_ADD_ANDI, RISCV_FUSE_ANDI_ADD,
RISCV_FUSE_LOGIC_LOGIC, RISCV_FUSE_SLLI_SRLI,
RISCV_FUSE_SRLI_ADD, RISCV_FUSE_PREINDEX_LD,
RISCV_FUSE_PREINDEX_ST, RISCV_FUSE_POSTINDEX_LD,
RISCV_FUSE_POSTINDEX_ST, RISCV_FUSE_LDST_PAIR_INC,
RISCV_FUSE_LDST_PAIR_DEC, RISCV_FUSE_FLDFST_PAIR_INC,
RISCV_FUSE_FLDFST_PAIR_DEC, RISCV_FUSE_ADD_LD,
RISCV_FUSE_ADD_ST.
(riscv_vector_mode_p): Declare extern.
(riscv_classify_address): Declare extern.
* config/riscv/riscv.cc (riscv_vector_mode_p): Make extern.
(riscv_classify_address): Make extern.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/fusion-sub-seqz-snez.c: New test.
* gcc.target/riscv/fusion-add-addi-andi.c: New test.
* gcc.target/riscv/fusion-andi-add-addi.c: New test.
* gcc.target/riscv/fusion-srli-add.c: New test.
* gcc.target/riscv/fusion-logic-logic.c: New test.
* gcc.target/riscv/fusion-slli-srli.c: New test.
* gcc.target/riscv/fusion-preindex-ldst.c: New test.
* gcc.target/riscv/fusion-postindex-ldst.c: New test.
* gcc.target/riscv/fusion-ldst-pair-inc.c: New test.
* gcc.target/riscv/fusion-ldst-pair-dec.c: New test.
* gcc.target/riscv/fusion-fldfst-pair-inc.c: New test.
* gcc.target/riscv/fusion-fldfst-pair-dec.c: New test.
* gcc.target/riscv/fusion-add-ldst.c: New test.
---
gcc/config/riscv/riscv-fusion.cc | 1049 ++++++++++++++++-
gcc/config/riscv/riscv-protos.h | 21 +
gcc/config/riscv/riscv.cc | 2 +-
.../gcc.target/riscv/fusion-add-addi-andi.c | 17 +
.../gcc.target/riscv/fusion-add-ldst.c | 38 +
.../gcc.target/riscv/fusion-andi-add-addi.c | 17 +
.../gcc.target/riscv/fusion-fldfst-pair-dec.c | 15 +
.../gcc.target/riscv/fusion-fldfst-pair-inc.c | 15 +
.../gcc.target/riscv/fusion-ldst-pair-dec.c | 16 +
.../gcc.target/riscv/fusion-ldst-pair-inc.c | 16 +
.../gcc.target/riscv/fusion-logic-logic.c | 60 +
.../gcc.target/riscv/fusion-postindex-ldst.c | 44 +
.../gcc.target/riscv/fusion-preindex-ldst.c | 44 +
.../gcc.target/riscv/fusion-slli-srli.c | 15 +
.../gcc.target/riscv/fusion-srli-add.c | 18 +
.../gcc.target/riscv/fusion-sub-seqz-snez.c | 14 +
16 files changed, 1397 insertions(+), 4 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-dec.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-inc.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-dec.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-postindex-ldst.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-preindex-ldst.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-srli-add.c
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-sub-seqz-snez.c
diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 4846dc0dfa2..68bbdfd6a2c 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -51,13 +51,33 @@ riscv_fusion_enabled_p (enum riscv_fusion_pairs op)
return riscv_get_fusible_ops () & op;
}
+/* Return the base register number of a store data source.
+ Handles REG, SUBREG, and const0_rtx. Returns INVALID_REGNUM
+ when the source has no register (e.g. const0_rtx), meaning no
+ register conflict can arise. */
+
+static unsigned int
+riscv_src_regno (rtx src)
+{
+ if (REG_P (src))
+ return REGNO (src);
+ if (SUBREG_P (src))
+ return REGNO (SUBREG_REG (src));
+ return INVALID_REGNUM;
+}
+
/* Return true if PREV_SET and CURR_SET satisfy the same-dest constraint
required by most fusion rules: when we are past register allocation
(i.e. can_create_pseudo_p () is false), the destination registers of
- the two sets must be the same physical register. */
+ the two sets must be the same physical register.
+
+ When USED_IN_SRC_P is true, additionally check that the first source
+ operand of CURR_SET references the shared destination register.
+ SIGN_EXTEND/ZERO_EXTEND wrappers are stripped before checking. */
static bool
-riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set)
+riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set,
+ bool used_in_src_p = false)
{
if (can_create_pseudo_p ())
return true;
@@ -69,7 +89,26 @@ riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set)
? REGNO (SET_DEST (curr_set))
: FIRST_PSEUDO_REGISTER);
- return prev_dest_regno == curr_dest_regno;
+ if (prev_dest_regno != curr_dest_regno)
+ return false;
+
+ if (!used_in_src_p)
+ return true;
+
+ rtx inner = SET_SRC (curr_set);
+ while (GET_CODE (inner) == SIGN_EXTEND
+ || GET_CODE (inner) == ZERO_EXTEND)
+ inner = XEXP (inner, 0);
+
+ if (GET_CODE (inner) == NOT)
+ return REG_P (XEXP (inner, 0))
+ && REGNO (XEXP (inner, 0)) == prev_dest_regno;
+
+ if (BINARY_P (inner))
+ return REG_P (XEXP (inner, 0))
+ && REGNO (XEXP (inner, 0)) == prev_dest_regno;
+
+ return reg_mentioned_p (SET_DEST (prev_set), SET_SRC (curr_set));
}
/* Matches an add:
@@ -110,6 +149,52 @@ riscv_set_is_adduw_p (rtx set)
&& REG_P (SET_DEST (set)));
}
+/* Matches an addw:
+ (set (reg:DI rd)
+ (sign_extend:DI
+ (plus:SI (reg:SI rs1) (reg:SI rs2)))) */
+
+static bool
+riscv_set_is_addw_p (rtx set)
+{
+ return (TARGET_64BIT
+ && GET_CODE (SET_SRC (set)) == SIGN_EXTEND
+ && GET_CODE (XEXP (SET_SRC (set), 0)) == PLUS
+ && GET_MODE (XEXP (SET_SRC (set), 0)) == E_SImode
+ && REG_P (XEXP (XEXP (SET_SRC (set), 0), 0))
+ && REG_P (XEXP (XEXP (SET_SRC (set), 0), 1))
+ && REG_P (SET_DEST (set)));
+}
+
+/* Matches an addiw:
+ (set (reg:DI rd)
+ (sign_extend:DI
+ (plus:SI (reg:SI rs1) (const_int imm)))) */
+
+static bool
+riscv_set_is_addiw_p (rtx set)
+{
+ return (TARGET_64BIT
+ && GET_CODE (SET_SRC (set)) == SIGN_EXTEND
+ && GET_CODE (XEXP (SET_SRC (set), 0)) == PLUS
+ && GET_MODE (XEXP (SET_SRC (set), 0)) == E_SImode
+ && REG_P (XEXP (XEXP (SET_SRC (set), 0), 0))
+ && CONST_INT_P (XEXP (XEXP (SET_SRC (set), 0), 1))
+ && REG_P (SET_DEST (set)));
+}
+
+/* Matches add/addi/addw/addiw/add.uw. */
+
+static bool
+riscv_set_is_add_addi_p (rtx set)
+{
+ return (riscv_set_is_add_p (set)
+ || riscv_set_is_addi_p (set)
+ || riscv_set_is_addw_p (set)
+ || riscv_set_is_addiw_p (set)
+ || riscv_set_is_adduw_p (set));
+}
+
/* Matches a shNadd:
(set (reg:DI rd)
(plus:DI (ashift:DI (reg:DI rs1) (const_int N)) (reg:DI rS2)) */
@@ -147,6 +232,246 @@ riscv_set_is_shNadduw_p (rtx set)
&& REG_P (SET_DEST (set)));
}
+/* Matches an andi:
+ (set (reg rd) (and (reg rs1) (const_int imm))) */
+
+static bool
+riscv_set_is_andi_type_p (rtx set)
+{
+ rtx src = SET_SRC (set);
+
+ return (GET_CODE (src) == AND
+ && CONST_INT_P (XEXP (src, 1))
+ && REG_P (SET_DEST (set)));
+}
+
+/* Matches a logic-type instruction (and, or, xor, andn, orn, xorn,
+ andi, ori, xori, not; andn/orn/xorn require ZBB). When SRC0/SRC1
+ are non-NULL, extracts the source operands. *SRC1 is NULL_RTX for
+ the one-operand NOT form. */
+
+static bool
+riscv_set_is_logical_type_p (rtx set,
+ rtx *src0 = NULL,
+ rtx *src1 = NULL)
+{
+ rtx src = SET_SRC (set);
+ rtx_code code = GET_CODE (src);
+ rtx dummy0, dummy1;
+ if (!src0) src0 = &dummy0;
+ if (!src1) src1 = &dummy1;
+
+ if (code == AND || code == IOR || code == XOR)
+ {
+ if (REG_P (XEXP (src, 0))
+ && (REG_P (XEXP (src, 1))
+ || CONST_INT_P (XEXP (src, 1))))
+ {
+ *src0 = XEXP (src, 0);
+ *src1 = XEXP (src, 1);
+ return true;
+ }
+ if (!TARGET_ZBB)
+ return false;
+ rtx sub = XEXP (src, 0);
+ *src0 = XEXP (src, 1);
+ *src1 = XEXP (sub, 0);
+ return GET_CODE (sub) == NOT && REG_P (*src0) && REG_P (*src1);
+ }
+
+ if (code == NOT)
+ {
+ rtx sub = XEXP (src, 0);
+ if (REG_P (sub))
+ {
+ *src0 = sub;
+ return true;
+ }
+ if (SUBREG_P (sub) || !TARGET_ZBB)
+ return false;
+ *src0 = XEXP (sub, 0);
+ *src1 = XEXP (sub, 1);
+ return GET_CODE (sub) == XOR && REG_P (*src0) && REG_P (*src1);
+ }
+
+ return false;
+}
+
+/* Matches an slli:
+ (set (reg rd) (ashift (reg rs1) (const_int imm))) */
+
+static bool
+riscv_set_is_slli_p (rtx set)
+{
+ rtx src = SET_SRC (set);
+ rtx_code code = GET_CODE (src);
+ machine_mode mode = GET_MODE (src);
+
+ if (code != ASHIFT || !CONST_INT_P (XEXP (src, 1)))
+ return false;
+
+ if ((TARGET_64BIT && mode == DImode) || mode == SImode)
+ return REG_P (SET_DEST (set));
+
+ return false;
+}
+
+/* Matches an slliw:
+ (set (reg:DI rd)
+ (sign_extend:DI
+ (ashift:SI (reg:SI rs1)
+ (const_int imm)))) */
+
+static bool
+riscv_set_is_slliw_p (rtx set)
+{
+ rtx src = SET_SRC (set);
+ rtx_code code = GET_CODE (src);
+ machine_mode mode;
+
+ if (!TARGET_64BIT || code != SIGN_EXTEND)
+ return false;
+
+ src = XEXP (src, 0);
+ code = GET_CODE (src);
+ mode = GET_MODE (src);
+ if (code != ASHIFT || !CONST_INT_P (XEXP (src, 1))
+ || mode != SImode)
+ return false;
+
+ return REG_P (SET_DEST (set));
+}
+
+/* Matches an srli:
+ (set (reg rd) (lshiftrt (reg rs1) (const_int imm))) */
+
+static bool
+riscv_set_is_srli_p (rtx set)
+{
+ rtx src = SET_SRC (set);
+ rtx_code code = GET_CODE (src);
+ machine_mode mode = GET_MODE (src);
+
+ if (code != LSHIFTRT || !CONST_INT_P (XEXP (src, 1)))
+ return false;
+
+ if ((TARGET_64BIT && mode == DImode) || mode == SImode)
+ return REG_P (SET_DEST (set));
+
+ return false;
+}
+
+/* Matches an srliw. Two RTL forms:
+ (set (reg:DI rd)
+ (zero_extend:DI (lshiftrt:SI (reg:SI rs1) (const_int imm))))
+ (set (reg:DI rd) (zero_extract:DI (reg rs1) (width) (pos)))
+ where width + pos == 32. */
+
+static bool
+riscv_set_is_srliw_p (rtx set)
+{
+ rtx src = SET_SRC (set);
+ rtx_code code = GET_CODE (src);
+ machine_mode mode;
+
+ /* Form 1: (zero_extend:DI (lshiftrt:SI reg imm)). */
+ if (code == ZERO_EXTEND)
+ {
+ src = XEXP (src, 0);
+ code = GET_CODE (src);
+ mode = GET_MODE (src);
+ if (code == LSHIFTRT && CONST_INT_P (XEXP (src, 1))
+ && mode == SImode)
+ return REG_P (SET_DEST (set));
+ return false;
+ }
+
+ /* Form 2: (zero_extract:DI reg width pos).
+ This is equivalent to srliw when width + pos == 32. */
+ if (code == ZERO_EXTRACT && GET_MODE (src) == DImode
+ && REG_P (XEXP (src, 0))
+ && CONST_INT_P (XEXP (src, 1))
+ && CONST_INT_P (XEXP (src, 2))
+ && (INTVAL (XEXP (src, 1))
+ + INTVAL (XEXP (src, 2)) == 32))
+ return REG_P (SET_DEST (set));
+
+ return false;
+}
+
+/* Scheduling fusion type for load/store instructions. */
+enum sched_fusion_type
+{
+ SCHED_FUSION_NONE = 0,
+ SCHED_FUSION_LD_SIGN_EXTEND,
+ SCHED_FUSION_LD_ZERO_EXTEND,
+ SCHED_FUSION_LD,
+ SCHED_FUSION_ST,
+ SCHED_FUSION_NUM
+};
+
+/* If INSN is a load or store of address in the form of base+offset,
+ extract the two parts and set to ADDR and MODE. Return scheduling
+ fusion type this INSN is. */
+
+static enum sched_fusion_type
+riscv_fusion_load_store (const rtx_insn *insn,
+ struct riscv_address_info *addr,
+ machine_mode *mode)
+{
+ rtx x, dest, src;
+ enum sched_fusion_type fusion = SCHED_FUSION_LD;
+
+ gcc_assert (INSN_P (insn));
+ x = PATTERN (insn);
+ if (GET_CODE (x) != SET)
+ return SCHED_FUSION_NONE;
+
+ src = SET_SRC (x);
+ dest = SET_DEST (x);
+
+ if (GET_CODE (src) == SIGN_EXTEND)
+ {
+ fusion = SCHED_FUSION_LD_SIGN_EXTEND;
+ src = XEXP (src, 0);
+ if (GET_CODE (src) != MEM)
+ return SCHED_FUSION_NONE;
+ }
+ else if (GET_CODE (src) == ZERO_EXTEND)
+ {
+ fusion = SCHED_FUSION_LD_ZERO_EXTEND;
+ src = XEXP (src, 0);
+ if (GET_CODE (src) != MEM)
+ return SCHED_FUSION_NONE;
+ }
+
+ /* Currently, RVV load/store is not considered. */
+ if (riscv_vector_mode_p (GET_MODE (dest))
+ || riscv_vector_mode_p (GET_MODE (src)))
+ return SCHED_FUSION_NONE;
+
+ if (MEM_P (src) && REG_P (dest))
+ {
+ *mode = GET_MODE (src);
+ if (!riscv_classify_address (addr, XEXP (src, 0),
+ GET_MODE (src), false))
+ return SCHED_FUSION_NONE;
+ }
+ else if (MEM_P (dest)
+ && (REG_P (src) || SUBREG_P (src) || src == const0_rtx))
+ {
+ fusion = SCHED_FUSION_ST;
+ *mode = GET_MODE (dest);
+ if (!riscv_classify_address (addr, XEXP (dest, 0),
+ GET_MODE (dest), false))
+ return SCHED_FUSION_NONE;
+ }
+ else
+ return SCHED_FUSION_NONE;
+
+ return fusion;
+}
+
/* Check for RISCV_FUSE_ZEXTW and RISCV_FUSE_ZEXTWS fusion.
prev (slli) == (set (reg:DI rD) (ashift:DI (reg:DI rS) (const_int 32)))
curr (srli) == (set (reg:DI rD) (lshiftrt:DI (reg:DI rD) (const_int N)))
@@ -691,6 +1016,692 @@ riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr)
return false;
}
+/* Check for RISCV_FUSE_SUB_SEQZ fusion.
+ Try:
+ prev (sub) == (set (reg x2) (minus (x3, x4)))
+ curr (seqz) == (set (reg x2) (eq (x2, 0)))
+ or
+ prev (subw) == (set (reg x2) (sign_extend (minus (x3, x4))))
+ curr (snez) == (set (reg x2) (ne (x2, 0)))
+
+ Constraints:
+ x2 != x0. */
+
+static bool
+riscv_fuse_sub_seqz (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ rtx curr_src = SET_SRC (curr_set);
+ rtx_code prev_code = GET_CODE (prev_src);
+ rtx_code curr_code = GET_CODE (curr_src);
+
+ if (!(prev_code == MINUS
+ || (prev_code == SIGN_EXTEND
+ && GET_CODE (XEXP (prev_src, 0)) == MINUS)))
+ return false;
+
+ if ((curr_code == EQ || curr_code == NE)
+ && REG_P (prev_dest) && REG_P (curr_dest)
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && REG_P (XEXP (curr_src, 0))
+ && REGNO (prev_dest) == REGNO (curr_dest)
+ && REGNO (XEXP (curr_src, 0)) == REGNO (curr_dest)
+ && XEXP (curr_src, 1) == const0_rtx)
+ return true;
+
+ return false;
+}
+
+/* Check for RISCV_FUSE_ADD_ANDI fusion.
+ Try:
+ prev (add) == (set (reg x2) (plus (x3, x4)))
+ curr (andi) == (set (reg x2) (and (x2, imm)))
+ or
+ prev (addi) == (set (reg x2) (plus (x3, imm0)))
+ curr (andi) == (set (reg x2) (and (x2, imm)))
+
+ Constraints:
+ x2 != x0. */
+
+static bool
+riscv_fuse_add_andi (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+
+ if (riscv_set_is_add_addi_p (prev_set)
+ && riscv_set_is_andi_type_p (curr_set)
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && riscv_fusion_same_dest_p (prev_set, curr_set, true))
+ return true;
+
+ return false;
+}
+
+/* Shared body for the add + load/store fusion checkers. ISLOAD_P
+ selects the load (true) or store (false) variant. */
+
+static bool
+riscv_fuse_add_ldst_1 (rtx_insn *prev, rtx_insn *curr, bool isload_p)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ rtx curr_src = SET_SRC (curr_set);
+ struct riscv_address_info curr_addr;
+ machine_mode mode;
+ enum sched_fusion_type curr_fusion;
+
+ curr_fusion = riscv_fusion_load_store (curr, &curr_addr, &mode);
+ if (curr_fusion == SCHED_FUSION_NONE)
+ return false;
+
+ if ((isload_p && curr_fusion == SCHED_FUSION_ST)
+ || (!isload_p && curr_fusion != SCHED_FUSION_ST))
+ return false;
+
+ if (!(curr_addr.type == ADDRESS_REG
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && CONST_INT_P (curr_addr.offset)
+ && INTVAL (curr_addr.offset) == 0
+ && REG_P (prev_dest)
+ && (riscv_set_is_add_p (prev_set)
+ || riscv_set_is_addw_p (prev_set)
+ || riscv_set_is_adduw_p (prev_set))
+ && REGNO (prev_dest) == REGNO (curr_addr.reg)
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && (isload_p
+ ? REGNO (curr_dest) != GP_REG_FIRST
+ : true)))
+ return false;
+
+ if (isload_p)
+ return REGNO (prev_dest) != REGNO (curr_dest);
+
+ /* Store src may be const0_rtx or SUBREG; use riscv_src_regno. */
+ return REGNO (prev_dest) != riscv_src_regno (curr_src);
+}
+
+/* Check for RISCV_FUSE_ADD_LD fusion.
+ Try:
+ prev (add) == (set (reg x2) (plus (x3, x4)))
+ curr (ld) == (set (reg x5) (mem (x2, 0)))
+ or
+ prev (addw) == (set (reg x2) (sign_extend (plus (x3, x4))))
+ curr (ld) == (set (reg x5) (mem (x2, 0)))
+ or
+ prev (add.uw) == (set (reg x2) (plus (zero_extend (x3), x4)))
+ curr (ld) == (set (reg x5) (mem (x2, 0)))
+
+ Constraints:
+ x2 != x0
+ x5 != x0
+ x2 != x5. */
+
+static bool
+riscv_fuse_add_ld (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_add_ldst_1 (prev, curr, true);
+}
+
+/* Check for RISCV_FUSE_ADD_ST fusion.
+ Try:
+ prev (add) == (set (reg x2) (plus (x3, x4)))
+ curr (st) == (set (mem (x2, 0)) (reg x5))
+ or
+ prev (addw) == (set (reg x2) (sign_extend (plus (x3, x4))))
+ curr (st) == (set (mem (x2, 0)) (reg x5))
+ or
+ prev (add.uw) == (set (reg x2) (plus (zero_extend (x3), x4)))
+ curr (st) == (set (mem (x2, 0)) (reg x5))
+
+ Constraints:
+ x2 != x0
+ x2 != x5. */
+
+static bool
+riscv_fuse_add_st (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_add_ldst_1 (prev, curr, false);
+}
+
+/* Check for RISCV_FUSE_ANDI_ADD fusion.
+ Try:
+ prev (andi) == (set (reg x2) (and (x3, imm0)))
+ curr (add) == (set (reg x2) (plus (x2, x4)))
+ or
+ prev (andi) == (set (reg x2) (and (x3, imm0)))
+ curr (addi) == (set (reg x2) (plus (x2, imm1)))
+
+ Constraints:
+ x2 != x0
+ x2 != x4. */
+
+static bool
+riscv_fuse_andi_add (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx curr_src = SET_SRC (curr_set);
+
+ if (!riscv_set_is_andi_type_p (prev_set)
+ || !riscv_set_is_add_addi_p (curr_set)
+ || REGNO (prev_dest) == GP_REG_FIRST
+ || !riscv_fusion_same_dest_p (prev_set, curr_set, true))
+ return false;
+
+ rtx add_expr = curr_src;
+ if (GET_CODE (add_expr) == SIGN_EXTEND)
+ add_expr = XEXP (add_expr, 0);
+ if (GET_CODE (add_expr) == PLUS
+ && REG_P (XEXP (add_expr, 0))
+ && REG_P (XEXP (add_expr, 1))
+ && REGNO (XEXP (add_expr, 0))
+ == REGNO (XEXP (add_expr, 1)))
+ return false;
+
+ return true;
+}
+
+/* Check for RISCV_FUSE_LOGIC_LOGIC fusion.
+ Try:
+ prev (logic) == (set (reg x2) (op1 (x3, x4|imm0)))
+ curr (logic) == (set (reg x2) (op2 (x2, x5|imm1)))
+
+ Constraints:
+ op1/op2 in {and, andn, or, orn, xor, xorn, andi, ori, xori, not}
+ andn/orn/xorn require ZBB
+ x2 != x0. */
+
+static bool
+riscv_fuse_logic_logic (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+
+ rtx curr_src0 = NULL_RTX, curr_src1 = NULL_RTX;
+ if (!riscv_set_is_logical_type_p (prev_set)
+ || !riscv_set_is_logical_type_p (curr_set, &curr_src0,
+ &curr_src1))
+ return false;
+
+ if (REG_P (prev_dest) && REG_P (curr_dest)
+ && REG_P (curr_src0)
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && REGNO (prev_dest) == REGNO (curr_dest)
+ && REGNO (prev_dest) == REGNO (curr_src0)
+ && ((curr_src1 == NULL_RTX)
+ || !REG_P (curr_src1)
+ || (REGNO (prev_dest)
+ != REGNO (curr_src1)))
+ && !(curr_src1 != NULL_RTX
+ && REG_P (curr_src0)
+ && REG_P (curr_src1)
+ && REGNO (curr_src0)
+ == REGNO (curr_src1)))
+ return true;
+
+ return false;
+}
+
+/* Check for RISCV_FUSE_SLLI_SRLI fusion.
+ Try:
+ prev (slli) == (set (reg x2) (ashift (x3, N)))
+ curr (srli) == (set (reg x2) (lshiftrt (x2, M)))
+ or
+ prev (slliw) == (set (reg x2) (sign_extend (ashift (x3, N))))
+ curr (srliw) == (set (reg x2) (zero_extend (lshiftrt (x2, M))))
+
+ Constraints:
+ x2 != x0. */
+
+static bool
+riscv_fuse_slli_srli (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+
+ if (((riscv_set_is_slli_p (prev_set)
+ && riscv_set_is_srli_p (curr_set))
+ || (riscv_set_is_slliw_p (prev_set)
+ && riscv_set_is_srliw_p (curr_set)))
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && riscv_fusion_same_dest_p (prev_set, curr_set, true))
+ return true;
+
+ return false;
+}
+
+/* Check for RISCV_FUSE_SRLI_ADD fusion.
+ Try:
+ prev (srli) == (set (reg x2) (lshiftrt (x3, N)))
+ curr (add) == (set (reg x2) (plus (x2, x4)))
+ or
+ prev (srliw) == (set (reg x2) (zero_extend (lshiftrt (x3, N))))
+ curr (addw) == (set (reg x2) (sign_extend (plus (x2, x4))))
+
+ Constraints:
+ x2 != x0
+ x2 != x4
+ N == 2
+ word+word or non-word+non-word. */
+
+static bool
+riscv_fuse_srli_add (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_src = SET_SRC (curr_set);
+
+ /* srli + add (non-word). */
+ if (riscv_set_is_srli_p (prev_set)
+ && (riscv_set_is_add_p (curr_set)
+ || riscv_set_is_adduw_p (curr_set))
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && riscv_fusion_same_dest_p (prev_set, curr_set, true)
+ && CONST_INT_P (XEXP (prev_src, 1))
+ && INTVAL (XEXP (prev_src, 1)) == 2
+ && GET_MODE (prev_src) == GET_MODE (curr_src))
+ {
+ if (!(REG_P (XEXP (curr_src, 0))
+ && REG_P (XEXP (curr_src, 1))
+ && REGNO (XEXP (curr_src, 0))
+ == REGNO (XEXP (curr_src, 1))))
+ return true;
+ }
+
+ /* srliw + addw. */
+ if (riscv_set_is_srliw_p (prev_set)
+ && riscv_set_is_addw_p (curr_set)
+ && REGNO (prev_dest) != GP_REG_FIRST
+ && riscv_fusion_same_dest_p (prev_set, curr_set, true))
+ {
+ HOST_WIDE_INT shift_amt = -1;
+ if (GET_CODE (prev_src) == ZERO_EXTEND
+ && GET_CODE (XEXP (prev_src, 0)) == LSHIFTRT
+ && CONST_INT_P (XEXP (XEXP (prev_src, 0), 1)))
+ shift_amt = INTVAL (XEXP (XEXP (prev_src, 0), 1));
+ else if (GET_CODE (prev_src) == ZERO_EXTRACT
+ && CONST_INT_P (XEXP (prev_src, 2)))
+ shift_amt = INTVAL (XEXP (prev_src, 2));
+
+ if (shift_amt == 2)
+ {
+ rtx addw_inner = XEXP (curr_src, 0);
+ if (REGNO (XEXP (addw_inner, 0))
+ != REGNO (XEXP (addw_inner, 1)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Shared body for the pre-index addi + load/store fusion checkers.
+ ISLOAD_P selects the load (true) or store (false) variant. */
+
+static bool
+riscv_fuse_preindex_ldst_1 (rtx_insn *prev,
+ rtx_insn *curr,
+ bool isload_p)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ rtx curr_src = SET_SRC (curr_set);
+ rtx_code prev_code = GET_CODE (prev_src);
+ struct riscv_address_info curr_addr;
+ machine_mode mode;
+ enum sched_fusion_type curr_fusion;
+
+ if (!((prev_code == PLUS && CONST_INT_P (XEXP (prev_src, 1))
+ && GET_MODE (prev_src) != E_SImode
+ && REG_P (XEXP (prev_src, 0)))
+ || prev_code == LO_SUM))
+ return false;
+
+ curr_fusion = riscv_fusion_load_store (curr, &curr_addr, &mode);
+ if (curr_fusion == SCHED_FUSION_NONE)
+ return false;
+
+ if ((isload_p && curr_fusion == SCHED_FUSION_ST)
+ || (!isload_p && curr_fusion != SCHED_FUSION_ST))
+ return false;
+
+ if (!(curr_addr.type == ADDRESS_REG
+ || curr_addr.type == ADDRESS_LO_SUM))
+ return false;
+
+ if (!REG_P (prev_dest)
+ || REGNO (prev_dest) == GP_REG_FIRST
+ || (isload_p && REGNO (curr_dest) == GP_REG_FIRST)
+ || REGNO (XEXP (prev_src, 0)) != REGNO (prev_dest)
+ || REGNO (curr_addr.reg) != REGNO (prev_dest))
+ return false;
+
+ if (isload_p)
+ return REGNO (curr_addr.reg) != REGNO (curr_dest);
+
+ /* Store src may be const0_rtx or SUBREG; use riscv_src_regno. */
+ return REGNO (curr_addr.reg) != riscv_src_regno (curr_src);
+}
+
+/* Check for RISCV_FUSE_PREINDEX_LD fusion.
+ Try:
+ prev (addi) == (set (reg x2) (plus (x2, imm)))
+ curr (ld) == (set (reg x4) (mem (x2, offset)))
+ or
+ prev (addi) == (set (reg x2) (lo_sum (x2, symbol)))
+ curr (ld) == (set (reg x4) (mem (x2, offset)))
+
+ Constraints:
+ x2 != x0
+ x4 != x0
+ x2 != x4. */
+
+static bool
+riscv_fuse_preindex_ld (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_preindex_ldst_1 (prev, curr, true);
+}
+
+/* Check for RISCV_FUSE_PREINDEX_ST fusion.
+ Try:
+ prev (addi) == (set (reg x2) (plus (x2, imm)))
+ curr (st) == (set (mem (x2, offset)) (reg x5))
+ or
+ prev (addi) == (set (reg x2) (lo_sum (x2, symbol)))
+ curr (st) == (set (mem (x2, offset)) (reg x5))
+
+ Constraints:
+ x2 != x0
+ x2 != x5. */
+
+static bool
+riscv_fuse_preindex_st (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_preindex_ldst_1 (prev, curr, false);
+}
+
+/* Shared body for the post-index load/store + addi fusion checkers.
+ ISLOAD_P selects the load (true) or store (false) variant. */
+
+static bool
+riscv_fuse_postindex_ldst_1 (rtx_insn *prev,
+ rtx_insn *curr,
+ bool isload_p)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ rtx curr_src = SET_SRC (curr_set);
+ rtx_code curr_code = GET_CODE (curr_src);
+ struct riscv_address_info prev_addr;
+ machine_mode mode;
+ enum sched_fusion_type prev_fusion;
+
+ prev_fusion = riscv_fusion_load_store (prev, &prev_addr, &mode);
+ if (prev_fusion == SCHED_FUSION_NONE)
+ return false;
+
+ if ((isload_p && prev_fusion == SCHED_FUSION_ST)
+ || (!isload_p && prev_fusion != SCHED_FUSION_ST))
+ return false;
+
+ if (!(prev_addr.type == ADDRESS_REG
+ || prev_addr.type == ADDRESS_LO_SUM))
+ return false;
+
+ if ((isload_p && REGNO (prev_dest) == GP_REG_FIRST)
+ || (!isload_p
+ && REGNO (prev_addr.reg) == riscv_src_regno (prev_src))
+ || (isload_p
+ && REGNO (prev_addr.reg) == REGNO (prev_dest)))
+ return false;
+
+ if (!(((curr_code == PLUS
+ && CONST_INT_P (XEXP (curr_src, 1))
+ && GET_MODE (curr_src) != E_SImode))
+ || curr_code == LO_SUM))
+ return false;
+
+ return (REG_P (curr_dest)
+ && REGNO (curr_dest) != GP_REG_FIRST
+ && REGNO (prev_addr.reg) == REGNO (curr_dest)
+ && REG_P (XEXP (curr_src, 0))
+ && REGNO (XEXP (curr_src, 0)) == REGNO (prev_addr.reg));
+}
+
+/* Check for RISCV_FUSE_POSTINDEX_LD fusion.
+ Try:
+ prev (ld) == (set (reg x4) (mem (x2, offset)))
+ curr (addi) == (set (reg x2) (plus (x3, imm)))
+ or
+ prev (ld) == (set (reg x4) (mem (x2, offset)))
+ curr (addi) == (set (reg x2) (lo_sum (x3, symbol)))
+
+ Constraints:
+ x2 == x3
+ x2 != x0
+ x4 != x0, x4 != x2. */
+
+static bool
+riscv_fuse_postindex_ld (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_postindex_ldst_1 (prev, curr, true);
+}
+
+/* Check for RISCV_FUSE_POSTINDEX_ST fusion.
+ Try:
+ prev (st) == (set (mem (x2, offset)) (reg x5))
+ curr (addi) == (set (reg x2) (plus (x3, imm)))
+ or
+ prev (st) == (set (mem (x2, offset)) (reg x5))
+ curr (addi) == (set (reg x2) (lo_sum (x3, symbol)))
+
+ Constraints:
+ x2 == x3
+ x2 != x0
+ x2 != x5. */
+
+static bool
+riscv_fuse_postindex_st (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_postindex_ldst_1 (prev, curr, false);
+}
+
+/* Shared body for the load/store pair fusion checkers. INC_P selects
+ the incrementing (true) or decrementing (false) variant. FP_P
+ selects the floating-point (FLD/FST, single or double precision)
+ variant (true) or the integer (LD/ST, word or double-word) variant
+ (false). */
+
+static bool
+riscv_fuse_ldst_pair_1 (rtx_insn *prev, rtx_insn *curr, bool inc_p, bool fp_p)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ rtx curr_src = SET_SRC (curr_set);
+ struct riscv_address_info prev_addr, curr_addr;
+ machine_mode mode, mode2;
+ enum sched_fusion_type prev_fusion, curr_fusion;
+ bool isload;
+
+ prev_fusion = riscv_fusion_load_store (prev, &prev_addr, &mode);
+ if (prev_fusion == SCHED_FUSION_NONE)
+ return false;
+
+ isload = prev_fusion != SCHED_FUSION_ST;
+ curr_fusion = riscv_fusion_load_store (curr, &curr_addr, &mode2);
+
+ if (!(curr_fusion != SCHED_FUSION_NONE
+ && isload == (curr_fusion != SCHED_FUSION_ST)
+ && (fp_p
+ ? (mode == E_SFmode || mode == E_DFmode)
+ : (mode == E_DImode || mode == E_SImode))
+ && mode == mode2
+ && prev_fusion != SCHED_FUSION_LD_ZERO_EXTEND
+ && curr_fusion != SCHED_FUSION_LD_ZERO_EXTEND
+ && prev_addr.type == ADDRESS_REG
+ && curr_addr.type == ADDRESS_REG
+ && REGNO (prev_addr.reg) == REGNO (curr_addr.reg)))
+ return false;
+
+ if (isload
+ && (REGNO (prev_dest) == GP_REG_FIRST
+ || REGNO (curr_dest) == GP_REG_FIRST
+ || REGNO (prev_dest) == REGNO (curr_dest)))
+ return false;
+
+ if (isload)
+ {
+ if (REGNO (prev_addr.reg) == REGNO (prev_dest))
+ return false;
+ }
+ else
+ {
+ if (REGNO (prev_addr.reg) == riscv_src_regno (prev_src))
+ return false;
+ if (REGNO (curr_addr.reg) == riscv_src_regno (curr_src))
+ return false;
+ }
+
+ HOST_WIDE_INT diff = inc_p
+ ? (INTVAL (curr_addr.offset)
+ - INTVAL (prev_addr.offset))
+ : (INTVAL (prev_addr.offset)
+ - INTVAL (curr_addr.offset));
+ if (diff == GET_MODE_SIZE (mode).to_constant ())
+ return true;
+
+ return false;
+}
+
+/* Check for RISCV_FUSE_LDST_PAIR_INC fusion.
+ Try:
+ prev (ld) == (set (reg x4) (mem (x2, off0)))
+ curr (ld) == (set (reg x5) (mem (x2, off1)))
+ or
+ prev (st) == (set (mem (x2, off0)) (reg x4))
+ curr (st) == (set (mem (x2, off1)) (reg x5))
+
+ Constraints:
+ off1 - off0 == mode_size (only word/double word)
+ ld: x4 != x0, x5 != x0, x4 != x5, x4 != x2
+ st: x4 != x2, x5 != x2. */
+
+static bool
+riscv_fuse_ldst_pair_inc (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_ldst_pair_1 (prev, curr, true, false);
+}
+
+/* Check for RISCV_FUSE_LDST_PAIR_DEC fusion.
+ Try:
+ prev (ld) == (set (reg x4) (mem (x2, off0)))
+ curr (ld) == (set (reg x5) (mem (x2, off1)))
+ or
+ prev (st) == (set (mem (x2, off0)) (reg x4))
+ curr (st) == (set (mem (x2, off1)) (reg x5))
+
+ Constraints:
+ off0 - off1 == mode_size (only word/double word)
+ ld: x4 != x0, x5 != x0, x4 != x5, x4 != x2
+ st: x4 != x2, x5 != x2. */
+
+static bool
+riscv_fuse_ldst_pair_dec (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_ldst_pair_1 (prev, curr, false, false);
+}
+
+/* Check for RISCV_FUSE_FLDFST_PAIR_INC fusion.
+ Try:
+ prev (fld) == (set (reg f0) (mem (x2, off0)))
+ curr (fld) == (set (reg f1) (mem (x2, off1)))
+ or
+ prev (fst) == (set (mem (x2, off0)) (reg f0))
+ curr (fst) == (set (mem (x2, off1)) (reg f1))
+
+ Constraints:
+ off1 - off0 == mode_size (only single/double float)
+ ld: f0 != f1, f0 != x2
+ st: f0 != x2, f1 != x2. */
+
+static bool
+riscv_fuse_fldfst_pair_inc (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_ldst_pair_1 (prev, curr, true, true);
+}
+
+/* Check for RISCV_FUSE_FLDFST_PAIR_DEC fusion.
+ Try:
+ prev (fld) == (set (reg f0) (mem (x2, off0)))
+ curr (fld) == (set (reg f1) (mem (x2, off1)))
+ or
+ prev (fst) == (set (mem (x2, off0)) (reg f0))
+ curr (fst) == (set (mem (x2, off1)) (reg f1))
+
+ Constraints:
+ off0 - off1 == mode_size (only single/double float)
+ ld: f0 != f1, f0 != x2
+ st: f0 != x2, f1 != x2. */
+
+static bool
+riscv_fuse_fldfst_pair_dec (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_ldst_pair_1 (prev, curr, false, true);
+}
+
/* Type for a fusion checker function. Takes the two candidate insns
and returns true if they should be fused. */
@@ -741,6 +1752,38 @@ static const struct riscv_fusion_entry
riscv_fusion_table[] =
riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
{ RISCV_FUSE_B_ALUI,
riscv_fuse_b_alui, "RISCV_FUSE_B_ALUI" },
+ { RISCV_FUSE_SUB_SEQZ,
+ riscv_fuse_sub_seqz, "RISCV_FUSE_SUB_SEQZ" },
+ { RISCV_FUSE_ADD_ANDI,
+ riscv_fuse_add_andi, "RISCV_FUSE_ADD_ANDI" },
+ { RISCV_FUSE_ADD_LD,
+ riscv_fuse_add_ld, "RISCV_FUSE_ADD_LD" },
+ { RISCV_FUSE_ADD_ST,
+ riscv_fuse_add_st, "RISCV_FUSE_ADD_ST" },
+ { RISCV_FUSE_ANDI_ADD,
+ riscv_fuse_andi_add, "RISCV_FUSE_ANDI_ADD" },
+ { RISCV_FUSE_LOGIC_LOGIC,
+ riscv_fuse_logic_logic, "RISCV_FUSE_LOGIC_LOGIC" },
+ { 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_PREINDEX_LD,
+ riscv_fuse_preindex_ld, "RISCV_FUSE_PREINDEX_LD" },
+ { RISCV_FUSE_PREINDEX_ST,
+ riscv_fuse_preindex_st, "RISCV_FUSE_PREINDEX_ST" },
+ { RISCV_FUSE_POSTINDEX_LD,
+ riscv_fuse_postindex_ld, "RISCV_FUSE_POSTINDEX_LD" },
+ { RISCV_FUSE_POSTINDEX_ST,
+ riscv_fuse_postindex_st, "RISCV_FUSE_POSTINDEX_ST" },
+ { RISCV_FUSE_LDST_PAIR_INC,
+ riscv_fuse_ldst_pair_inc, "RISCV_FUSE_LDST_PAIR_INC" },
+ { RISCV_FUSE_LDST_PAIR_DEC,
+ riscv_fuse_ldst_pair_dec, "RISCV_FUSE_LDST_PAIR_DEC" },
+ { RISCV_FUSE_FLDFST_PAIR_INC,
+ riscv_fuse_fldfst_pair_inc, "RISCV_FUSE_FLDFST_PAIR_INC" },
+ { RISCV_FUSE_FLDFST_PAIR_DEC,
+ riscv_fuse_fldfst_pair_dec, "RISCV_FUSE_FLDFST_PAIR_DEC" },
};
/* Implement TARGET_SCHED_MACRO_FUSION_PAIR_P. Return true if PREV and CURR
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index e7bce0db7a3..7a7ec9bedc6 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -175,6 +175,9 @@ extern poly_uint64 riscv_regmode_natural_size
(machine_mode);
extern bool riscv_vla_mode_p (machine_mode);
extern bool riscv_tuple_mode_p (machine_mode);
extern bool riscv_vls_mode_p (machine_mode);
+extern bool riscv_vector_mode_p (machine_mode);
+extern bool riscv_widen_overlap_ok (unsigned int, machine_mode,
+ unsigned int, machine_mode);
extern int riscv_get_v_regno_alignment (machine_mode);
extern bool riscv_shamt_matches_mask_p (int, HOST_WIDE_INT);
extern void riscv_subword_address (rtx, rtx *, rtx *, rtx *, rtx *);
@@ -866,11 +869,29 @@ enum riscv_fusion_pairs
RISCV_FUSE_BFEXT = (1 << 11),
RISCV_FUSE_EXPANDED_LD = (1 << 12),
RISCV_FUSE_B_ALUI = (1 << 13),
+ RISCV_FUSE_SUB_SEQZ = (1 << 14),
+ RISCV_FUSE_ADD_LD = (1 << 15),
+ RISCV_FUSE_ADD_ST = (1 << 16),
+ RISCV_FUSE_ADD_ANDI = (1 << 17),
+ RISCV_FUSE_ANDI_ADD = (1 << 18),
+ RISCV_FUSE_LOGIC_LOGIC = (1 << 19),
+ RISCV_FUSE_SLLI_SRLI = (1 << 20),
+ RISCV_FUSE_SRLI_ADD = (1 << 21),
+ RISCV_FUSE_PREINDEX_LD = (1 << 22),
+ RISCV_FUSE_PREINDEX_ST = (1 << 23),
+ RISCV_FUSE_POSTINDEX_LD = (1 << 24),
+ RISCV_FUSE_POSTINDEX_ST = (1 << 25),
+ RISCV_FUSE_LDST_PAIR_INC = (1 << 26),
+ RISCV_FUSE_LDST_PAIR_DEC = (1 << 27),
+ RISCV_FUSE_FLDFST_PAIR_INC = (1 << 28),
+ RISCV_FUSE_FLDFST_PAIR_DEC = (1 << 29),
};
extern bool riscv_macro_fusion_p (void);
extern bool riscv_macro_fusion_pair_p (rtx_insn *, rtx_insn *);
extern unsigned int riscv_get_fusible_ops (void);
+extern bool riscv_classify_address (struct riscv_address_info *, rtx,
+ machine_mode, bool);
/* Routines implemented in thead.cc. */
extern bool extract_base_offset_in_addr (rtx, rtx *, rtx *);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 873defc5a5b..f709a70b361 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -2650,7 +2650,7 @@ riscv_v_adjust_precision (machine_mode mode, int scale)
fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in
effect. */
-static bool
+bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
machine_mode mode, bool strict_p)
{
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
b/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
new file mode 100644
index 00000000000..2954734bc9a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
@@ -0,0 +1,17 @@
+/* Verify RISCV_FUSE_ADD_ANDI matches addi/add/addw/add.uw followed by andi.
*/
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_zba -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_ADD_ANDI. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ADD_ANDI" "sched2" { xfail *-*-* } }
} */
+
+typedef long int64_t;
+typedef unsigned long uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+int64_t test_addi_andi (int64_t a) { return (a + 16) & 0x55; }
+int64_t test_addiw_andi (int32_t a) { return ((int64_t)(a + 5)) &
0x55; }
+int64_t test_add_andi (int64_t a, int64_t b) { return (a + b) & 0x55; }
+int64_t test_addw_andi (int32_t a, int32_t b) { return ((int64_t)(a + b)) &
0x33; }
+int64_t test_adduw_andi (uint32_t a, int64_t b) { return ((uint64_t)a + b) &
0x55; }
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..50cb48a3799
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-add-ldst.c
@@ -0,0 +1,38 @@
+/* Verify RISCV_FUSE_ADD_LD and RISCV_FUSE_ADD_ST match add followed by
+ integer scalar load or store with zero offset. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_ADD_LD/ST. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ADD_LD" "sched2" { xfail *-*-* } } }
*/
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ADD_ST" "sched2" { xfail *-*-* } } }
*/
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long int64_t;
+
+extern void use_addr (void *);
+
+int8_t test_add_lb (int8_t *base, long off)
+{ int8_t *p = (int8_t *)((char *)base + off); int8_t v = *p; use_addr
(p); return v; }
+int16_t test_add_lh (int16_t *base, long off)
+{ int16_t *p = (int16_t *)((char *)base + off); int16_t v = *p; use_addr
(p); return v; }
+int32_t test_add_lw (int32_t *base, long off)
+{ int32_t *p = (int32_t *)((char *)base + off); int32_t v = *p; use_addr
(p); return v; }
+uint8_t test_add_lbu (uint8_t *base, long off)
+{ uint8_t *p = (uint8_t *)((char *)base + off); uint8_t v = *p; use_addr
(p); return v; }
+uint16_t test_add_lhu (uint16_t *base, long off)
+{ uint16_t *p = (uint16_t *)((char *)base + off); uint16_t v = *p; use_addr
(p); return v; }
+uint32_t test_add_lwu (uint32_t *base, long off)
+{ uint32_t *p = (uint32_t *)((char *)base + off); uint32_t v = *p; use_addr
(p); return v; }
+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; }
+
+void test_add_sb (int8_t *base, long off, int8_t v) { *(int8_t *)((char
*)base + off) = v; }
+void test_add_sh (int16_t *base, long off, int16_t v) { *(int16_t *)((char
*)base + off) = v; }
+void test_add_sw (int32_t *base, long off, int32_t v) { *(int32_t *)((char
*)base + off) = v; }
+void test_add_sd (int64_t *base, long off, int64_t v) { *(int64_t *)((char
*)base + off) = v; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
b/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
new file mode 100644
index 00000000000..75c74dfd6bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
@@ -0,0 +1,17 @@
+/* Verify RISCV_FUSE_ANDI_ADD matches andi followed by addi/add/addw/add.uw.
*/
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_zba -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_ANDI_ADD. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ANDI_ADD" "sched2" { xfail *-*-* } }
} */
+
+typedef long int64_t;
+typedef unsigned long uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+int64_t test_andi_addi (int64_t a) { return (a & 0x55) + 1; }
+int64_t test_andi_addiw (int64_t a) { return
(int64_t)((int32_t)(a & 0x55) + 5); }
+int64_t test_andi_add (int64_t a, int64_t b) { return (a & 0x55) + b; }
+int64_t test_andi_addw (int64_t a, int32_t b) { return
(int64_t)((int32_t)(a & 0x55) + b); }
+uint64_t test_andi_adduw (uint64_t a, uint32_t b) { return (a & 0x55) +
(uint64_t)b; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-dec.c
b/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-dec.c
new file mode 100644
index 00000000000..0f728f6f3ff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-dec.c
@@ -0,0 +1,15 @@
+/* Verify RISCV_FUSE_FLDFST_PAIR_DEC matches single/double-precision
+ floating-point load or store pairs with decrementing consecutive
+ addresses. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* No tune enables RISCV_FUSE_FLDFST_PAIR_DEC yet, so the scan below is
+ expected to fail until a tune opts in via fusible_ops. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_FLDFST_PAIR_DEC" "sched2" { xfail
*-*-* } } } */
+
+float test_flw_pair_dec (float *p) { return p[1] + p[0]; }
+double test_fld_pair_dec (double *p) { return p[1] + p[0]; }
+
+void test_fsw_pair_dec (float *p, float a, float b) { p[1] = b; p[0] = a; }
+void test_fsd_pair_dec (double *p, double a, double b) { p[1] = b; p[0] = a; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-inc.c
b/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-inc.c
new file mode 100644
index 00000000000..fc16f3c8c1a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-fldfst-pair-inc.c
@@ -0,0 +1,15 @@
+/* Verify RISCV_FUSE_FLDFST_PAIR_INC matches single/double-precision
+ floating-point load or store pairs with incrementing consecutive
+ addresses. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* No tune enables RISCV_FUSE_FLDFST_PAIR_INC yet, so the scan below is
+ expected to fail until a tune opts in via fusible_ops. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_FLDFST_PAIR_INC" "sched2" { xfail
*-*-* } } } */
+
+float test_flw_pair_inc (float *p) { return p[0] + p[1]; }
+double test_fld_pair_inc (double *p) { return p[0] + p[1]; }
+
+void test_fsw_pair_inc (float *p, float a, float b) { p[0] = a; p[1] = b; }
+void test_fsd_pair_inc (double *p, double a, double b) { p[0] = a; p[1] = b; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-dec.c
b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-dec.c
new file mode 100644
index 00000000000..188d829cb52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-dec.c
@@ -0,0 +1,16 @@
+/* Verify RISCV_FUSE_LDST_PAIR_DEC matches integer word/double-word
+ load or store pairs with decrementing consecutive addresses. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_LDST_PAIR_DEC. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LDST_PAIR_DEC" "sched2" { xfail
*-*-* } } } */
+
+typedef int int32_t;
+typedef long int64_t;
+
+int64_t test_ld_pair_dec (int64_t *p) { return p[1] + p[0]; }
+int32_t test_lw_pair_dec (int32_t *p) { return p[1] + p[0]; }
+
+void test_sd_pair_dec (int64_t *p, int64_t a, int64_t b) { p[1] = b; p[0] = a;
}
+void test_sw_pair_dec (int32_t *p, int32_t a, int32_t b) { p[1] = b; p[0] = a;
}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
new file mode 100644
index 00000000000..9fe5c81ce09
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
@@ -0,0 +1,16 @@
+/* Verify RISCV_FUSE_LDST_PAIR_INC matches integer word/double-word
+ load or store pairs with incrementing consecutive addresses. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_LDST_PAIR_INC. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LDST_PAIR_INC" "sched2" { xfail
*-*-* } } } */
+
+typedef int int32_t;
+typedef long int64_t;
+
+int64_t test_ld_pair_inc (int64_t *p) { return p[0] + p[1]; }
+int32_t test_lw_pair_inc (int32_t *p) { return p[0] + p[1]; }
+
+void test_sd_pair_inc (int64_t *p, int64_t a, int64_t b) { p[0] = a; p[1] = b;
}
+void test_sw_pair_inc (int32_t *p, int32_t a, int32_t b) { p[0] = a; p[1] = b;
}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
new file mode 100644
index 00000000000..826e088f4bf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
@@ -0,0 +1,60 @@
+/* Verify RISCV_FUSE_LOGIC_LOGIC matches logic+logic pairs where at least
+ one operand is an immediate (andi/ori/xori). */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_zbb -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_LOGIC_LOGIC. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LOGIC_LOGIC" "sched2" { xfail *-*-*
} } } */
+
+typedef long int64_t;
+
+/* reg-class + imm-class */
+int64_t t_and_andi (int64_t a, int64_t b) { return (a & b) & 0x55; }
+int64_t t_and_ori (int64_t a, int64_t b) { return (a & b) | 0x33; }
+int64_t t_and_xori (int64_t a, int64_t b) { return (a & b) ^ 0x1f; }
+int64_t t_andn_andi (int64_t a, int64_t b) { return (~a & b) & 0x55; }
+int64_t t_andn_ori (int64_t a, int64_t b) { return (~a & b) | 0x33; }
+int64_t t_andn_xori (int64_t a, int64_t b) { return (~a & b) ^ 0x1f; }
+int64_t t_or_andi (int64_t a, int64_t b) { return (a | b) & 0x55; }
+int64_t t_or_ori (int64_t a, int64_t b) { return (a | b) | 0x33; }
+int64_t t_or_xori (int64_t a, int64_t b) { return (a | b) ^ 0x1f; }
+int64_t t_orn_andi (int64_t a, int64_t b) { return (~a | b) & 0x55; }
+int64_t t_orn_ori (int64_t a, int64_t b) { return (~a | b) | 0x33; }
+int64_t t_orn_xori (int64_t a, int64_t b) { return (~a | b) ^ 0x1f; }
+int64_t t_xor_andi (int64_t a, int64_t b) { return (a ^ b) & 0x55; }
+int64_t t_xor_ori (int64_t a, int64_t b) { return (a ^ b) | 0x33; }
+int64_t t_xor_xori (int64_t a, int64_t b) { return (a ^ b) ^ 0x1f; }
+int64_t t_xnor_andi (int64_t a, int64_t b) { return ~(a ^ b) & 0x55; }
+int64_t t_xnor_ori (int64_t a, int64_t b) { return ~(a ^ b) | 0x33; }
+int64_t t_xnor_xori (int64_t a, int64_t b) { return ~(a ^ b) ^ 0x1f; }
+
+/* imm-class + reg-class */
+int64_t t_andi_and (int64_t a, int64_t b) { return (a & 0x55) & b; }
+int64_t t_andi_andn (int64_t a, int64_t b) { return ~(a & 0x55) & b; }
+int64_t t_andi_or (int64_t a, int64_t b) { return (a & 0x55) | b; }
+int64_t t_andi_orn (int64_t a, int64_t b) { return ~(a & 0x55) | b; }
+int64_t t_andi_xor (int64_t a, int64_t b) { return (a & 0x55) ^ b; }
+int64_t t_andi_xnor (int64_t a, int64_t b) { return ~((a & 0x55) ^ b); }
+int64_t t_ori_and (int64_t a, int64_t b) { return (a | 0x33) & b; }
+int64_t t_ori_andn (int64_t a, int64_t b) { return ~(a | 0x33) & b; }
+int64_t t_ori_or (int64_t a, int64_t b) { return (a | 0x33) | b; }
+int64_t t_ori_orn (int64_t a, int64_t b) { return ~(a | 0x33) | b; }
+int64_t t_ori_xor (int64_t a, int64_t b) { return (a | 0x33) ^ b; }
+int64_t t_ori_xnor (int64_t a, int64_t b) { return ~((a | 0x33) ^ b); }
+int64_t t_xori_and (int64_t a, int64_t b) { return (a ^ 0x1f) & b; }
+int64_t t_xori_andn (int64_t a, int64_t b) { return ~(a ^ 0x1f) & b; }
+int64_t t_xori_or (int64_t a, int64_t b) { return (a ^ 0x1f) | b; }
+int64_t t_xori_orn (int64_t a, int64_t b) { return ~(a ^ 0x1f) | b; }
+int64_t t_xori_xor (int64_t a, int64_t b) { return (a ^ 0x1f) ^ b; }
+int64_t t_xori_xnor (int64_t a, int64_t b) { return ~((a ^ 0x1f) ^ b); }
+
+/* imm-class + imm-class */
+int64_t t_andi_andi (int64_t a) { return (a & 0x55) & 0x33; }
+int64_t t_andi_ori (int64_t a) { return (a & 0x55) | 0x33; }
+int64_t t_andi_xori (int64_t a) { return (a & 0x55) ^ 0x1f; }
+int64_t t_ori_andi (int64_t a) { return (a | 0x33) & 0x55; }
+int64_t t_ori_ori (int64_t a) { return (a | 0x33) | 0x1f; }
+int64_t t_ori_xori (int64_t a) { return (a | 0x33) ^ 0x1f; }
+int64_t t_xori_andi (int64_t a) { return (a ^ 0x1f) & 0x55; }
+int64_t t_xori_ori (int64_t a) { return (a ^ 0x1f) | 0x33; }
+int64_t t_xori_xori (int64_t a) { return (a ^ 0x1f) ^ 0x55; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-postindex-ldst.c
b/gcc/testsuite/gcc.target/riscv/fusion-postindex-ldst.c
new file mode 100644
index 00000000000..9eb56bdc16b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-postindex-ldst.c
@@ -0,0 +1,44 @@
+/* Verify RISCV_FUSE_POSTINDEX_LD and RISCV_FUSE_POSTINDEX_ST match
+ load or store followed by addi (self-increment). */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" "-funroll-loops"
} } */
+/* { dg-options "-march=rv64gc_zfh -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_POSTINDEX_LD/ST. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_POSTINDEX_LD" "sched2" { xfail *-*-*
} } } */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_POSTINDEX_ST" "sched2" { xfail *-*-*
} } } */
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long int64_t;
+
+extern void use_ptr (void *);
+
+int8_t post_lb (int8_t **pp) { int8_t *p = *pp; int8_t v = *p; *pp =
p + 1; return v; }
+uint8_t post_lbu (uint8_t **pp) { uint8_t *p = *pp; uint8_t v = *p; *pp =
p + 1; return v; }
+int16_t post_lh (int16_t **pp) { int16_t *p = *pp; int16_t v = *p; *pp =
p + 1; return v; }
+uint16_t post_lhu (uint16_t **pp) { uint16_t *p = *pp; uint16_t v = *p; *pp =
p + 1; return v; }
+int32_t post_lw (int32_t **pp) { int32_t *p = *pp; int32_t v = *p; *pp =
p + 1; return v; }
+uint32_t post_lwu (uint32_t **pp) { uint32_t *p = *pp; uint32_t v = *p; *pp =
p + 1; return v; }
+int64_t post_ld (int64_t **pp) { int64_t *p = *pp; int64_t v = *p; *pp =
p + 1; return v; }
+_Float16 post_flh (_Float16 **pp) { _Float16 *p = *pp; _Float16 v = *p; *pp =
p + 1; return v; }
+float post_flw (float **pp) { float *p = *pp; float v = *p; *pp =
p + 1; return v; }
+double post_fld (double **pp) { double *p = *pp; double v = *p; *pp =
p + 1; return v; }
+
+void post_sb (int8_t *dst, int8_t *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_sh (int16_t *dst, int16_t *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_sw (int32_t *dst, int32_t *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_sd (int64_t *dst, int64_t *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_fsh (_Float16 *dst, _Float16 *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_fsw (float *dst, float *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
+void post_fsd (double *dst, double *src, int n)
+{ for (int i = 0; i < n; i++) { *dst = src[i]; dst++; } use_ptr (dst); }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-preindex-ldst.c
b/gcc/testsuite/gcc.target/riscv/fusion-preindex-ldst.c
new file mode 100644
index 00000000000..ab9ce96daad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-preindex-ldst.c
@@ -0,0 +1,44 @@
+/* Verify RISCV_FUSE_PREINDEX_LD and RISCV_FUSE_PREINDEX_ST match
+ addi (self-increment) followed by load or store. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" "-funroll-loops"
} } */
+/* { dg-options "-march=rv64gc_zfh -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_PREINDEX_LD/ST. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_PREINDEX_LD" "sched2" { xfail *-*-*
} } } */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_PREINDEX_ST" "sched2" { xfail *-*-*
} } } */
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long int64_t;
+
+extern void use_ptr (void *);
+
+int8_t test_preindex_lb (int8_t *p) { p += 2; int8_t v = *p; use_ptr
(p); return v; }
+uint8_t test_preindex_lbu (uint8_t *p) { p += 2; uint8_t v = *p; use_ptr
(p); return v; }
+int16_t test_preindex_lh (int16_t *p) { p += 2; int16_t v = *p; use_ptr
(p); return v; }
+uint16_t test_preindex_lhu (uint16_t *p) { p += 2; uint16_t v = *p; use_ptr
(p); return v; }
+int32_t test_preindex_lw (int32_t *p) { p += 2; int32_t v = *p; use_ptr
(p); return v; }
+uint32_t test_preindex_lwu (uint32_t *p) { p += 2; uint32_t v = *p; use_ptr
(p); return v; }
+int64_t test_preindex_ld (int64_t *p) { p += 2; int64_t v = *p; use_ptr
(p); return v; }
+_Float16 test_preindex_flh (_Float16 *p) { p += 2; _Float16 v = *p; use_ptr
(p); return v; }
+float test_preindex_flw (float *p) { p += 2; float v = *p; use_ptr
(p); return v; }
+double test_preindex_fld (double *p) { p += 2; double v = *p; use_ptr
(p); return v; }
+
+void test_preindex_sb (int8_t *p, int8_t v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_sh (int16_t *p, int16_t v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_sw (int32_t *p, int32_t v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_sd (int64_t *p, int64_t v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_fsh (_Float16 *p, _Float16 v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_fsw (float *p, float v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
+void test_preindex_fsd (double *p, double v, int n)
+{ for (int i = 0; i < n; i++) { p += 2; *p = v; } }
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..bd2d20f7b2b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-slli-srli.c
@@ -0,0 +1,15 @@
+/* Verify RISCV_FUSE_SLLI_SRLI matches slli/slliw followed by srli/srliw,
+ with word/non-word consistency. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_SLLI_SRLI. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_SLLI_SRLI" "sched2" { xfail *-*-* }
} } */
+
+typedef unsigned long uint64_t;
+typedef unsigned int uint32_t;
+
+uint64_t test_slli_srli (uint64_t a) { return (a << 4) >> 8; }
+uint64_t test_slli_srli_2 (uint64_t a) { return (a << 16) >> 32; }
+uint32_t test_slliw_srliw (uint32_t a) { return (a << 4) >> 8; }
+uint32_t test_slliw_srliw_2 (uint32_t a) { return (a << 8) >> 16; }
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..0ba65fbf65d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-srli-add.c
@@ -0,0 +1,18 @@
+/* Verify RISCV_FUSE_SRLI_ADD matches srli/srliw followed by add/addw,
+ with shift amount 2 and word consistency. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_SRLI_ADD. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_SRLI_ADD" "sched2" { xfail *-*-* } }
} */
+
+typedef long int64_t;
+typedef unsigned long uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+/* srli (non-word) + add (non-word): srli result feeds src0 of add. */
+int64_t test_srli_add (uint64_t a, int64_t b) { return (int64_t)(a >> 2) +
b; }
+
+/* srliw (word) + addw (word): srliw result feeds src0 of addw. */
+int32_t test_srliw_addw (uint32_t a, int32_t b) { return (int32_t)(a >> 2) +
b; }
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-sub-seqz-snez.c
b/gcc/testsuite/gcc.target/riscv/fusion-sub-seqz-snez.c
new file mode 100644
index 00000000000..093e37db76a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-sub-seqz-snez.c
@@ -0,0 +1,14 @@
+/* Verify RISCV_FUSE_SUB_SEQZ matches sub/subw followed by seqz/snez. */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2
-fdump-rtl-sched2-details" } */
+/* xt-c9501fdvt does not yet enable RISCV_FUSE_SUB_SEQZ. */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_SUB_SEQZ" "sched2" { xfail *-*-* } }
} */
+
+typedef long int64_t;
+typedef int int32_t;
+
+int test_sub_seqz (int64_t a, int64_t b) { return (a - b) == 0; }
+int test_sub_snez (int64_t a, int64_t b) { return (a - b) != 0; }
+int test_subw_seqz (int32_t a, int32_t b) { return (a - b) == 0; }
+int test_subw_snez (int32_t a, int32_t b) { return (a - b) != 0; }
--
2.52.0