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

Reply via email to