From: Loeka Rogge <[email protected]>

Add the RHX-100 macro-op fusion cases to the table in riscv-fusion.cc
and enable them from the tune's fusible_ops field.  Also split BFEXT into
SRLI / SRAI.

Fused_MAC only matches when the product is rs1 of the add:
  mul rd,...; add rd,rd,rs

I1->I2 means fusion only in that order; I1<->I2 means the two
instructions are interchangeable with regards to order.

RHX-100 enables:

  RISCV_FUSE_MULT_ADD       (riscv_fuse_mult_add, I1->I2)
      I1: mul rd,...; I2: add rd,rd,rs3 reading the mul result in rd
      (on RV64, mulw+addw with the result sign-extended to DImode).
  RISCV_FUSE_LI_BRANCH      (riscv_fuse_li_branch, I1->I2)
      I1: li rd; I2: conditional branch that uses rd.
  RISCV_FUSE_LI_STORE       (riscv_fuse_li_store, I1->I2)
      I1: li rt; I2: store of rt.
  RISCV_FUSE_LUI_ST         (riscv_fuse_lui_st, I1<->I2)
      I1/I2: lui and store.
  RISCV_FUSE_LUI_LD_REV     (riscv_fuse_lui_ld_reversible, I1<->I2)
      I1/I2: independent load and lui with distinct destinations.
  RISCV_FUSE_LS_UPDATE      (riscv_fuse_ls_update, I1<->I2)
      I1/I2: load or store with an addi that updates the access base.
  RISCV_FUSE_BFEXT_SRLI     (riscv_fuse_bfext_srli, I1->I2)
      I1: slli rd,rs; I2: srli rd,rd (bitfield extract).
  RISCV_FUSE_ADJACENT_LOAD  (riscv_fuse_adjacent_load, I1<->I2)
      I1/I2: two loads, same base, offsets |off1-off2| == XLEN/8.
  RISCV_FUSE_ADJACENT_STORE (riscv_fuse_adjacent_store, I1<->I2)
      I1/I2: two stores, same base, offsets |off1-off2| == XLEN/8.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (LUI_NONZERO_OPERAND): New macro.
        (riscv_fusion_enabled_p): Removed static to use in bfext cost.
        (riscv_fuse_sets_p): Add allow_condjump_p parameter.
        (riscv_insn_is_mv_li_p): Use split riscv_insn_is_li_p and
        riscv_insn_is_mv_p.
        (riscv_insn_is_li_p): New function.
        (riscv_insn_is_mv_p): New function.
        (riscv_fuse_shift_pair_p): Change allow_arithmetic_p argument
        to hard arithmetic_p requirement.
        (riscv_fuse_bfext_srli): New function.
        (riscv_fuse_bfext): Removed.
        (riscv_fuse_bfext_srai): New function.
        (riscv_ls_update_pair_p): New function.
        (riscv_fuse_mult_add): New function.
        (riscv_fuse_li_branch): New function.
        (riscv_fuse_adjacent_load): New function.
        (riscv_fuse_adjacent_store): New function.
        (riscv_fuse_ls_update): New function.
        (riscv_lui_st_pair_p): New function.
        (riscv_fuse_lui_st): New function.
        (riscv_fuse_li_store): New function.
        (riscv_fuse_lui_ld_independent_p): New function.
        (riscv_fuse_lui_ld_reversible): New function.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Rename
        RISCV_FUSE_BFEXT to RISCV_FUSE_BFEXT_SRLI; add the RHX-100 fusion
        bits and RISCV_FUSE_BFEXT_SRAI.
        (riscv_fusion_enabled_p): Declare.
        * config/riscv/riscv.cc (arcv_rhx100_tune_info): Set fusible_ops.
        * config/riscv/riscv.h (TARGET_ARCV_RHX100): New.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-adjacent-load.c: New test.
        * gcc.target/riscv/fusion-adjacent-store.c: New test.
        * gcc.target/riscv/fusion-bfext-srai.c: New test.
        * gcc.target/riscv/fusion-bfext.c: New test.
        * gcc.target/riscv/fusion-li-branch.c: New test.
        * gcc.target/riscv/fusion-li-store.c: New test.
        * gcc.target/riscv/fusion-ls-update-2.c: New test.
        * gcc.target/riscv/fusion-ls-update-3.c: New test.
        * gcc.target/riscv/fusion-ls-update-4.c: New test.
        * gcc.target/riscv/fusion-ls-update-5.c: New test.
        * gcc.target/riscv/fusion-ls-update.c: New test.
        * gcc.target/riscv/fusion-lui-ld.c: New test.
        * gcc.target/riscv/fusion-lui-st.c: New test.
        * gcc.target/riscv/fusion-mult-add.c: New test.

Co-authored-by: Artemiy Volkov <[email protected]>
Co-authored-by: Michiel Derhaeg <[email protected]>
Co-authored-by: Luis Silva <[email protected]>
Signed-off-by: Loeka Rogge <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 415 ++++++++++++++++--
 gcc/config/riscv/riscv-protos.h               |  11 +-
 gcc/config/riscv/riscv.cc                     |   6 +-
 .../gcc.target/riscv/fusion-adjacent-load.c   |  12 +
 .../gcc.target/riscv/fusion-adjacent-store.c  |  13 +
 .../gcc.target/riscv/fusion-bfext-srai.c      |  18 +
 gcc/testsuite/gcc.target/riscv/fusion-bfext.c |  15 +
 .../gcc.target/riscv/fusion-li-branch.c       |  14 +
 .../gcc.target/riscv/fusion-li-store.c        |  12 +
 .../gcc.target/riscv/fusion-ls-update-2.c     |  17 +
 .../gcc.target/riscv/fusion-ls-update-3.c     |  82 ++++
 .../gcc.target/riscv/fusion-ls-update-4.c     |  53 +++
 .../gcc.target/riscv/fusion-ls-update-5.c     |  13 +
 .../gcc.target/riscv/fusion-ls-update.c       |  16 +
 .../gcc.target/riscv/fusion-lui-ld.c          |  25 ++
 .../gcc.target/riscv/fusion-lui-st.c          |  19 +
 .../gcc.target/riscv/fusion-mult-add.c        |  13 +
 17 files changed, 726 insertions(+), 28 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-adjacent-load.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-adjacent-store.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-bfext-srai.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-bfext.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-li-branch.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-li-store.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ls-update-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ls-update-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ls-update-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ls-update-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ls-update.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-lui-ld.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-lui-st.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-mult-add.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 5598bd5171a..b584908276b 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -33,6 +33,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "memmodel.h"
 #include "emit-rtl.h"
 #include "tm_p.h"
+#include "regset.h"
+#include "basic-block.h"
+#include "sched-int.h"
 #include "riscv-protos.h"
 
 /* Implement TARGET_SCHED_MACRO_FUSION_P.  Return true if target supports
@@ -44,6 +47,11 @@ riscv_macro_fusion_p (void)
   return riscv_get_fusible_ops () != RISCV_FUSE_NOTHING;
 }
 
+/* A LUI_OPERAND accepts (const_int 0), but we won't emit that as LUI.
+   Reject that case explicitly.  */
+
+#define LUI_NONZERO_OPERAND(value) ((value) != 0 && LUI_OPERAND (value))
+
 /* Return true iff the instruction fusion described by OP is enabled.  */
 
 static bool
@@ -52,13 +60,14 @@ riscv_fusion_enabled_p (enum riscv_fusion_pairs op)
   return riscv_get_fusible_ops () & op;
 }
 
-/* Extract the single sets from PREV and CURR.  Reject conditional jumps,
-   which cannot form a fusion pair.  */
+/* Extract the single sets from PREV and CURR.  Unless ALLOW_CONDJUMP_P,
+   reject conditional jumps.  */
 
 static bool
 riscv_fuse_sets_p (rtx_insn *prev, rtx_insn *curr,
                     rtx *prev_set_out = NULL,
-                    rtx *curr_set_out = NULL)
+                    rtx *curr_set_out = NULL,
+                    bool allow_condjump_p = false)
 {
   rtx prev_set = single_set (prev);
   rtx curr_set = single_set (curr);
@@ -66,7 +75,8 @@ riscv_fuse_sets_p (rtx_insn *prev, rtx_insn *curr,
     *prev_set_out = prev_set;
   if (curr_set_out)
     *curr_set_out = curr_set;
-  return prev_set && curr_set && !any_condjump_p (curr);
+  return (prev_set && curr_set
+         && (allow_condjump_p || !any_condjump_p (curr)));
 }
 
 /* Extract a word-form binary operation with code CODE:
@@ -357,12 +367,10 @@ riscv_insn_is_add_type_p (rtx_insn *insn, bool *word_p = 
NULL)
 }
 
 /* Matches an mv or li instruction:
-     (set (reg rd) (reg rs1))
-   or:
      (set (reg rd) (const_int imm12)).  */
 
 static bool
-riscv_insn_is_mv_li_p (rtx_insn *insn)
+riscv_insn_is_li_p (rtx_insn *insn)
 {
   rtx set = single_set (insn);
   if (!set)
@@ -379,12 +387,6 @@ riscv_insn_is_mv_li_p (rtx_insn *insn)
     return false;
 
   enum attr_move_type move_type = get_attr_move_type (insn);
-  if (move_type == MOVE_TYPE_MOVE
-      && REG_P (src)
-      && (!HARD_REGISTER_P (src)
-         || GP_REG_P (REGNO (src))))
-    return true;
-
   if (move_type == MOVE_TYPE_CONST
       && CONST_INT_P (src)
       /* Distinguish an ADDI-based LI from other single-insn constants.  */
@@ -394,6 +396,47 @@ riscv_insn_is_mv_li_p (rtx_insn *insn)
   return false;
 }
 
+/* Matches an mv or li instruction:
+     (set (reg rd) (reg rs1)).  */
+
+static bool
+riscv_insn_is_mv_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  rtx dest = SET_DEST (set);
+  rtx src = SET_SRC (set);
+
+  /* Pseudos are valid GPR candidates before register allocation.  */
+  if (get_attr_type (insn) != TYPE_MOVE
+      || get_attr_length (insn) > 4
+      || !REG_P (dest)
+      || (HARD_REGISTER_P (dest) && !GP_REG_P (REGNO (dest))))
+    return false;
+
+  enum attr_move_type move_type = get_attr_move_type (insn);
+  if (move_type == MOVE_TYPE_MOVE
+      && REG_P (src)
+      && (!HARD_REGISTER_P (src)
+         || GP_REG_P (REGNO (src))))
+    return true;
+
+  return false;
+}
+
+/* Matches an mv or li instruction:
+     (set (reg rd) (reg rs1))
+   or:
+     (set (reg rd) (const_int imm12)).  */
+
+static bool
+riscv_insn_is_mv_li_p (rtx_insn *insn)
+{
+  return riscv_insn_is_mv_p (insn) || riscv_insn_is_li_p (insn);
+}
+
 /* Matches an addi-type instruction:
      (set (reg rd) (plus (reg rs1) (const_int imm12)))
    or:
@@ -600,11 +643,12 @@ riscv_set_is_shift_type_p (rtx set, rtx_code code, bool 
*word_p,
 
 /* Match a left-shift/right-shift fusion pair.  ALLOW_WORD_P accepts the
    equivalent RV64 word forms and requires both instructions to have the same
-   wordness.  ALLOW_ARITHMETIC_P accepts an arithmetic right shift.  */
+   wordness.  ARITHMETIC_P accepts only an arithmetic right shift,
+   otherwise only a logical right shift is accepted.  */
 
 static bool
 riscv_fuse_shift_pair_p (rtx_insn *prev, rtx_insn *curr,
-                          bool allow_word_p, bool allow_arithmetic_p)
+                          bool allow_word_p, bool arithmetic_p)
 {
   rtx prev_set, curr_set;
   if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)
@@ -618,13 +662,14 @@ riscv_fuse_shift_pair_p (rtx_insn *prev, rtx_insn *curr,
     = (allow_word_p
        ? riscv_set_is_shift_type_p (prev_set, ASHIFT, &prev_word_p)
        : riscv_set_is_slli_p (prev_set));
-  bool curr_match_p
-    = (allow_word_p
-       ? riscv_set_is_shift_type_p (curr_set, LSHIFTRT, &curr_word_p)
-       : riscv_set_is_srli_p (curr_set));
+  bool curr_match_p = false;
 
-  if (!curr_match_p && allow_arithmetic_p)
+  if (arithmetic_p)
     curr_match_p = riscv_set_is_srai_p (curr_set);
+  else
+    curr_match_p = (allow_word_p
+               ? riscv_set_is_shift_type_p (curr_set, LSHIFTRT, &curr_word_p)
+               : riscv_set_is_srli_p (curr_set));
 
   if (!prev_match_p
       || !curr_match_p
@@ -814,7 +859,13 @@ static const struct riscv_fusion_mem_rule 
riscv_fusion_mem_rules[] =
     true, false, RISCV_FUSION_DEC, 1 },
   /* Allow 1-, 2-, 4- and 8-byte stores.  */
   { RISCV_FUSE_ALIGNED_STD, (1U << 1) | (1U << 2) | (1U << 4) | (1U << 8),
-    SCHED_FUSION_ST, false, true, RISCV_FUSION_ANY, 2 }
+    SCHED_FUSION_ST, false, true, RISCV_FUSION_ANY, 2 },
+  { RISCV_FUSE_ADJACENT_LOAD, (1U << 1) | (1U << 2) | (1U << 4),
+    SCHED_FUSION_LD | SCHED_FUSION_LD_SIGN_EXTEND
+    | SCHED_FUSION_LD_ZERO_EXTEND,
+    false, false, RISCV_FUSION_ANY, 1 },
+  { RISCV_FUSE_ADJACENT_STORE, (1U << 4),
+    SCHED_FUSION_ST, false, false, RISCV_FUSION_ANY, 1 }
 };
 
 /* Return true if MEM satisfies RULE's single-instruction constraints.
@@ -1730,13 +1781,27 @@ riscv_fuse_fldfst_pair_dec (rtx_insn *prev, rtx_insn 
*curr)
   return riscv_fuse_mem_pair_p (prev, curr, RISCV_FUSE_FLDFST_PAIR_DEC);
 }
 
-/* Check for RISCV_FUSE_BFEXT fusion.
+/* Check for RISCV_FUSE_BFEXT_SRLI fusion.
    prev (slli) == (set (reg rd1)
                       (ashift (reg rs1) (const_int shamt1)))
-   curr (one of the following):
+   curr:
      (srli) == (set (reg rd2)
                    (lshiftrt (reg rd1)
                              (const_int shamt2)))
+
+   Constraints:
+     rd1 == rd2.  */
+
+static bool
+riscv_fuse_bfext_srli (rtx_insn *prev, rtx_insn *curr)
+{
+  return riscv_fuse_shift_pair_p (prev, curr, false, false);
+}
+
+/* Check for RISCV_FUSE_BFEXT_SRAI fusion.
+   prev (slli) == (set (reg rd1)
+                      (ashift (reg rs1) (const_int shamt1)))
+   curr:
      (srai) == (set (reg rd2)
                    (ashiftrt (reg rd1)
                              (const_int shamt2)))
@@ -1745,7 +1810,7 @@ riscv_fuse_fldfst_pair_dec (rtx_insn *prev, rtx_insn 
*curr)
      rd1 == rd2.  */
 
 static bool
-riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
+riscv_fuse_bfext_srai (rtx_insn *prev, rtx_insn *curr)
 {
   return riscv_fuse_shift_pair_p (prev, curr, false, true);
 }
@@ -2020,6 +2085,286 @@ riscv_fuse_logic_logic (rtx_insn *prev, rtx_insn *curr)
   return false;
 }
 
+/* Return true if PREV and CURR constitute an ordered load/store + op/opimm
+   pair, for the purposes of macro-op fusion.
+   This is a more general form that combines load+arith and store+arith.  */
+
+static bool
+riscv_ls_update_pair_p (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, NULL, &curr_set))
+    return false;
+
+  enum attr_type c_type = get_attr_type (curr);
+  if (!(c_type == TYPE_ARITH
+       || c_type == TYPE_LOGICAL
+       || c_type == TYPE_SHIFT
+       || c_type == TYPE_SLT
+       || c_type == TYPE_BITMANIP
+       || c_type == TYPE_MIN
+       || c_type == TYPE_MAX
+       || c_type == TYPE_MINU
+       || c_type == TYPE_MAXU
+       || c_type == TYPE_CLZ
+       || c_type == TYPE_CTZ
+       || c_type == TYPE_MOVE))
+    return false;
+
+  rtx c_src = SET_SRC (curr_set);
+  if (CONSTANT_P (c_src))
+    return false;
+
+  unsigned int c_rs1 = INVALID_REGNUM;
+  unsigned int c_rs2 = INVALID_REGNUM;
+
+  if (REG_P (c_src) || SUBREG_P (c_src))
+    c_rs1 = riscv_regno (c_src);
+  else
+    {
+      rtx op = c_src;
+      if (GET_CODE (op) == NOT && BINARY_P (XEXP (op, 0)))
+       op = XEXP (op, 0);
+
+      const char *fmt = GET_RTX_FORMAT (GET_CODE (op));
+      for (int i = 0; i < GET_RTX_LENGTH (GET_CODE (op)); i++)
+       {
+         if (fmt[i] != 'e')
+           continue;
+
+         rtx x = XEXP (op, i);
+         if (GET_CODE (x) == NOT)
+           x = XEXP (x, 0);
+         unsigned int regno = riscv_regno (x);
+         if (regno == INVALID_REGNUM)
+           continue;
+
+         if (c_rs1 == INVALID_REGNUM)
+           c_rs1 = regno;
+         else
+           {
+             c_rs2 = regno;
+             break;
+           }
+       }
+    }
+
+  if (c_rs1 == INVALID_REGNUM)
+    return false;
+
+  struct riscv_fusion_mem_info mem;
+  if (!riscv_fuse_mem_p (prev, &mem) || mem.fp_p)
+    return false;
+
+  unsigned int base_regno = riscv_regno (mem.addr.reg);
+  if (base_regno != c_rs1)
+    return false;
+
+  if (mem.type != SCHED_FUSION_ST)
+    {
+      unsigned int p_rd = riscv_regno (mem.reg);
+      unsigned int c_rd = riscv_regno (SET_DEST (curr_set));
+      return (p_rd != INVALID_REGNUM
+             && c_rd != INVALID_REGNUM
+             && p_rd != base_regno
+             && p_rd != c_rd
+             && !reg_overlap_mentioned_p (mem.reg, c_src));
+    }
+
+  if (c_rs2 == INVALID_REGNUM)
+    return true;
+
+  return riscv_regno (mem.reg) == c_rs2;
+}
+
+/* Check for RISCV_FUSE_MULT_ADD fusion.
+   On RV32:
+   prev (mul) == (set (reg rD) (mult:SI (reg:SI rS1) (reg:SI rS2)))
+   curr (add) == (set (reg rD) (plus:SI (reg:SI rD) (reg:SI rS3)))
+
+   Matches mulw+addw for RV64, not mul+add:
+   prev (mul) == (set (reg:DI rD) (sign_extend:DI (mult:SI ...)))
+   curr (add) == (set (reg:DI rD) (sign_extend:DI (plus:SI ...)))  */
+
+static bool
+riscv_fuse_mult_add (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
+    return false;
+
+  rtx prev_src = SET_SRC (prev_set);
+  rtx curr_src = SET_SRC (curr_set);
+  rtx word_src;
+  if (riscv_set_extract_word_binary_p (prev_set, MULT, &word_src))
+    prev_src = word_src;
+  if (riscv_set_extract_word_binary_p (curr_set, PLUS, &word_src))
+    curr_src = word_src;
+
+  if (GET_CODE (prev_src) != MULT || GET_MODE (prev_src) != SImode
+      || GET_CODE (curr_src) != PLUS || GET_MODE (curr_src) != SImode)
+    return false;
+
+  if (!riscv_fuse_same_dest_p (prev_set, curr_set))
+    return false;
+
+  return riscv_fuse_same_reg_p (XEXP (curr_src, 0), SET_DEST (prev_set));
+}
+
+/* Check for RISCV_FUSE_LI_BRANCH fusion.
+   prev (li)     == (set (reg:DI rD) (const_int imm12))
+   curr (branch) == conditional branch using rD  */
+
+static bool
+riscv_fuse_li_branch (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set, true))
+    return false;
+
+  if (!riscv_insn_is_li_p (prev))
+    return false;
+
+  if (!any_condjump_p (curr))
+    return false;
+
+  /* Check if the loaded register is used in the branch condition.  */
+  rtx cond = XEXP (SET_SRC (curr_set), 0);
+  return (riscv_fuse_same_reg_p (XEXP (cond, 0), SET_DEST (prev_set))
+         || riscv_fuse_same_reg_p (XEXP (cond, 1), SET_DEST (prev_set)));
+}
+
+/* Check for RISCV_FUSE_ADJACENT_LOAD fusion.
+   prev (ld) == (set (reg:SI rD1)
+                    (mem:SI (plus:DI (reg:DI rB) (const_int OFF1))))
+   curr (ld) == (set (reg:SI rD2)
+                    (mem:SI (plus:DI (reg:DI rB) (const_int OFF2))))
+   where OFF2 == OFF1 + MODE_SIZE or OFF2 == OFF1 - MODE_SIZE  */
+
+static bool
+riscv_fuse_adjacent_load (rtx_insn *prev, rtx_insn *curr)
+{
+  return riscv_fuse_mem_pair_p (prev, curr, RISCV_FUSE_ADJACENT_LOAD);
+}
+
+/* Check for RISCV_FUSE_ADJACENT_STORE fusion.
+   prev (st) == (set (mem:SI (plus:DI (reg:DI rB) (const_int OFF1)))
+                    (reg:SI rS1))
+   curr (st) == (set (mem:SI (plus:DI (reg:DI rB) (const_int OFF2)))
+                    (reg:SI rS2))
+   where OFF2 == OFF1 + MODE_SIZE or OFF2 == OFF1 - MODE_SIZE  */
+
+static bool
+riscv_fuse_adjacent_store (rtx_insn *prev, rtx_insn *curr)
+{
+  return riscv_fuse_mem_pair_p (prev, curr, RISCV_FUSE_ADJACENT_STORE);
+}
+
+/* Check for RISCV_FUSE_LS_UPDATE fusion (load/store with address update).
+   prev (ld)   == (set (reg:DI rD) (mem:DI (reg:DI rA)))
+   curr (addi) == (set (reg:DI rA) (plus:DI (reg:DI rA) (const_int)))
+   OR
+   prev (addi) == (set (reg:DI rA) (plus:DI (reg:DI rA) (const_int)))
+   curr (st)   == (set (mem:DI (reg:DI rA)) (reg:DI rS))  */
+
+static bool
+riscv_fuse_ls_update (rtx_insn *prev, rtx_insn *curr)
+{
+  if (!riscv_fuse_sets_p (prev, curr))
+    return false;
+
+  return (riscv_ls_update_pair_p (prev, curr)
+         || riscv_ls_update_pair_p (curr, prev));
+}
+
+static bool
+riscv_lui_st_pair_p (rtx_insn *lui, rtx_insn *store, rtx lui_set)
+{
+  struct riscv_fusion_mem_info mem;
+  if (riscv_regno (SET_DEST (lui_set)) == INVALID_REGNUM
+      || !riscv_fuse_mem_p (store, &mem)
+      || mem.type != SCHED_FUSION_ST
+      || mem.fp_p)
+    return false;
+
+  rtx src = SET_SRC (lui_set);
+  return ((get_attr_type (lui) == TYPE_MOVE && GET_CODE (src) == HIGH)
+         || (CONST_INT_P (src)
+             && LUI_NONZERO_OPERAND (INTVAL (src))));
+}
+
+/* Check for RISCV_FUSE_LUI_ST fusion.
+   prev (lui) == (set (reg:DI rD) (const_int UPPER_IMM_20))
+   curr (st)  == (set (mem:DI (plus:DI (reg:DI rX) (const_int))) (reg:DI rS))
+   OR (reversed)
+   prev (st)  == (set (mem:DI (plus:DI (reg:DI rX) (const_int))) (reg:DI rS))
+   curr (lui) == (set (reg:DI rD) (const_int UPPER_IMM_20))  */
+
+static bool
+riscv_fuse_lui_st (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
+    return false;
+
+  return (riscv_lui_st_pair_p (prev, curr, prev_set)
+         || riscv_lui_st_pair_p (curr, prev, curr_set));
+}
+
+/* Check for RISCV_FUSE_LI_STORE fusion.
+   prev (li) == (set (reg:DI rT) (const_int imm12))
+   curr (st) == (set (mem:DI (plus:DI (reg:DI rB) (const_int))) (reg:DI rT))  
*/
+
+static bool
+riscv_fuse_li_store (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, NULL))
+    return false;
+
+  struct riscv_fusion_mem_info mem;
+  if (riscv_insn_is_li_p (prev)
+      && riscv_fuse_mem_p (curr, &mem)
+      && mem.type == SCHED_FUSION_ST
+      && !mem.fp_p
+      && riscv_fuse_same_reg_p (mem.reg, SET_DEST (prev_set)))
+    return true;
+
+  return false;
+}
+
+/* Independent LUI and load: distinct destinations, either order.
+   The load is not required to use the LUI result as its address.  */
+
+static bool
+riscv_fuse_lui_ld_independent_p (rtx_insn *lui, rtx_insn *load)
+{
+  rtx lui_set;
+  if (!riscv_fuse_sets_p (lui, load, &lui_set, NULL))
+    return false;
+
+  struct riscv_fusion_mem_info mem;
+  if (!riscv_fuse_mem_p (load, &mem)
+      || mem.type == SCHED_FUSION_ST
+      || mem.fp_p
+      || riscv_fuse_same_reg_p (SET_DEST (lui_set), mem.reg))
+    return false;
+
+  rtx src = SET_SRC (lui_set);
+  return (GET_CODE (src) == HIGH
+         || (CONST_INT_P (src)
+             && LUI_NONZERO_OPERAND (INTVAL (src))));
+}
+
+/* Check for RISCV_FUSE_LUI_LD_REV fusion (reversible).  */
+
+static bool
+riscv_fuse_lui_ld_reversible (rtx_insn *prev, rtx_insn *curr)
+{
+  return (riscv_fuse_lui_ld_independent_p (prev, curr)
+         || riscv_fuse_lui_ld_independent_p (curr, prev));
+}
+
 /* Type for a fusion checker function.  Takes the two candidate insns
    and returns true if they should be fused.  */
 
@@ -2081,8 +2426,6 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     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" },
-  { RISCV_FUSE_BFEXT,
-    riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
   { RISCV_FUSE_SLLI_SRLI,
     riscv_fuse_slli_srli, "RISCV_FUSE_SLLI_SRLI" },
   { RISCV_FUSE_SRLI_ADD,
@@ -2097,6 +2440,26 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_andi_add, "RISCV_FUSE_ANDI_ADD" },
   { RISCV_FUSE_LOGIC_LOGIC,
     riscv_fuse_logic_logic, "RISCV_FUSE_LOGIC_LOGIC" },
+  { RISCV_FUSE_MULT_ADD,
+    riscv_fuse_mult_add, "RISCV_FUSE_MULT_ADD" },
+  { RISCV_FUSE_LI_BRANCH,
+    riscv_fuse_li_branch, "RISCV_FUSE_LI_BRANCH" },
+  { RISCV_FUSE_ADJACENT_LOAD,
+    riscv_fuse_adjacent_load, "RISCV_FUSE_ADJACENT_LOAD" },
+  { RISCV_FUSE_ADJACENT_STORE,
+    riscv_fuse_adjacent_store, "RISCV_FUSE_ADJACENT_STORE" },
+  { RISCV_FUSE_LS_UPDATE,
+    riscv_fuse_ls_update, "RISCV_FUSE_LS_UPDATE" },
+  { RISCV_FUSE_LUI_ST,
+    riscv_fuse_lui_st, "RISCV_FUSE_LUI_ST" },
+  { RISCV_FUSE_LI_STORE,
+    riscv_fuse_li_store, "RISCV_FUSE_LI_STORE" },
+  { RISCV_FUSE_LUI_LD_REV,
+    riscv_fuse_lui_ld_reversible, "RISCV_FUSE_LUI_LD_REV" },
+  { RISCV_FUSE_BFEXT_SRLI,
+    riscv_fuse_bfext_srli, "RISCV_FUSE_BFEXT_SRLI" },
+  { RISCV_FUSE_BFEXT_SRAI,
+    riscv_fuse_bfext_srai, "RISCV_FUSE_BFEXT_SRAI" },
 };
 
 /* Return the name of fusion operation OP.  */
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index b72623984b0..5c0cd3dfedb 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -866,7 +866,7 @@ enum riscv_fusion_pairs
   RISCV_FUSE_AUIPC_LD = HOST_WIDE_INT_1U << 7,
   RISCV_FUSE_LDPREINCREMENT = HOST_WIDE_INT_1U << 8,
   RISCV_FUSE_ALIGNED_STD = HOST_WIDE_INT_1U << 9,
-  RISCV_FUSE_BFEXT = HOST_WIDE_INT_1U << 10,
+  RISCV_FUSE_BFEXT_SRLI = HOST_WIDE_INT_1U << 10,
   RISCV_FUSE_EXPANDED_LD = HOST_WIDE_INT_1U << 11,
   RISCV_FUSE_B_ALUI = HOST_WIDE_INT_1U << 12,
   RISCV_FUSE_SUB_SEQZ = HOST_WIDE_INT_1U << 13,
@@ -883,6 +883,15 @@ enum riscv_fusion_pairs
   RISCV_FUSE_LDST_PAIR_DEC = HOST_WIDE_INT_1U << 24,
   RISCV_FUSE_FLDFST_PAIR_INC = HOST_WIDE_INT_1U << 25,
   RISCV_FUSE_FLDFST_PAIR_DEC = HOST_WIDE_INT_1U << 26,
+  RISCV_FUSE_MULT_ADD = HOST_WIDE_INT_1U << 27,
+  RISCV_FUSE_LI_BRANCH = HOST_WIDE_INT_1U << 28,
+  RISCV_FUSE_ADJACENT_LOAD = HOST_WIDE_INT_1U << 29,
+  RISCV_FUSE_ADJACENT_STORE = HOST_WIDE_INT_1U << 30,
+  RISCV_FUSE_LS_UPDATE = HOST_WIDE_INT_1U << 31,
+  RISCV_FUSE_LUI_ST = HOST_WIDE_INT_1U << 32,
+  RISCV_FUSE_LI_STORE = HOST_WIDE_INT_1U << 33,
+  RISCV_FUSE_LUI_LD_REV = HOST_WIDE_INT_1U << 34,
+  RISCV_FUSE_BFEXT_SRAI = HOST_WIDE_INT_1U << 35,
 };
 
 /* Possible directions for a memory-pair candidate.  */
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 680aff94adf..f3065e85276 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1057,7 +1057,11 @@ static const struct riscv_tune_param 
arcv_rhx100_tune_info = {
   false,                                      /* overlap_op_by_pieces */
   true,                                               /* use_zero_stride_load 
*/
   false,                                      /* speculative_sched_vsetvl */
-  RISCV_FUSE_NOTHING,                         /* fusible_ops */
+  (RISCV_FUSE_MULT_ADD | RISCV_FUSE_LI_BRANCH
+   | RISCV_FUSE_ADJACENT_LOAD | RISCV_FUSE_ADJACENT_STORE
+   | RISCV_FUSE_LS_UPDATE | RISCV_FUSE_LUI_ST
+   | RISCV_FUSE_LI_STORE | RISCV_FUSE_BFEXT_SRLI
+   | RISCV_FUSE_LUI_LD_REV),                  /* fusible_ops */
   NULL,                                               /* vector cost */
   NULL,                                               /* function_align */
   NULL,                                               /* jump_align */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-adjacent-load.c 
b/gcc/testsuite/gcc.target/riscv/fusion-adjacent-load.c
new file mode 100644
index 00000000000..8dc2ec4b1b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-adjacent-load.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+int
+fuse_adjacent_load (int *p)
+{
+  return p[0] + p[1];
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ADJACENT_LOAD" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-adjacent-store.c 
b/gcc/testsuite/gcc.target/riscv/fusion-adjacent-store.c
new file mode 100644
index 00000000000..cf86ab271ae
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-adjacent-store.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+void
+fuse_adjacent_store (int *p, int x, int y)
+{
+  p[0] = x;
+  p[1] = y;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_ADJACENT_STORE" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-bfext-srai.c 
b/gcc/testsuite/gcc.target/riscv/fusion-bfext-srai.c
new file mode 100644
index 00000000000..df42e965926
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-bfext-srai.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+/* RHX Fused_XBFU is slli+srli only; slli+srai must not fuse.  */
+
+int
+fusion_shift_bitextract_srai (int x, int y, int z)
+{
+  int t = x << 8;
+  int a = y + z;
+  t = t >> 4;
+  return t + a;
+}
+
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_BFEXT_SRAI" "sched2" } } */
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_BFEXT_SRLI" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-bfext.c 
b/gcc/testsuite/gcc.target/riscv/fusion-bfext.c
new file mode 100644
index 00000000000..48f006c46eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-bfext.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+unsigned int
+fusion_shift_bitextract (unsigned int x, unsigned int y, unsigned int z)
+{
+    unsigned int t = x << 8;
+    unsigned int a = y + z;
+    t = t >> 4;
+    return t + a;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_BFEXT_SRLI" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-li-branch.c 
b/gcc/testsuite/gcc.target/riscv/fusion-li-branch.c
new file mode 100644
index 00000000000..793282395bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-li-branch.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+int
+fuse_li_branch (int x)
+{
+  while (x <= 3)
+    ;
+  return x;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LI_BRANCH" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-li-store.c 
b/gcc/testsuite/gcc.target/riscv/fusion-li-store.c
new file mode 100644
index 00000000000..ea69cc5f5b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-li-store.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+void
+fuse_li_store (int *p)
+{
+  *p = 42;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LI_STORE" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ls-update-2.c 
b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-2.c
new file mode 100644
index 00000000000..57363b44fa8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+void
+fuse_ls_update_rev (int *p, int n, int val)
+{
+  for (int i = 0; i < n; i++)
+    {
+      p++;
+      *p = val;
+    }
+}
+
+/* XFAIL until scheduling support for RHX-100 series is implemented.  */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" { xfail *-*-* } 
} } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ls-update-3.c 
b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-3.c
new file mode 100644
index 00000000000..1ab879fbc52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-3.c
@@ -0,0 +1,82 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im_zbb -mabi=ilp32 
-fdump-rtl-sched2" { target rv32 } } */
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv64im_zbb -mabi=lp64 
-fdump-rtl-sched2" { target rv64 } } */
+
+#if __SIZEOF_LONG__ == 8
+# define BSWAPL __builtin_bswap64
+#else
+# define BSWAPL __builtin_bswap32
+#endif
+
+#define ST(NAME, EXPR)                                 \
+  unsigned long NAME (long *p, long val)               \
+  {                                                    \
+    *p = val;                                          \
+    return (EXPR);                                     \
+  }
+
+ST (st_addi,  ((unsigned long) p + 8))                     /* arith    */
+ST (st_andi,  ((unsigned long) p & 0xff))                  /* logical  */
+ST (st_ori,   ((unsigned long) p | 0xff))                  /* logical  */
+ST (st_xori,  ((unsigned long) p ^ 0xff))                  /* logical  */
+ST (st_slli,  ((unsigned long) p << 3))                            /* shift    
*/
+ST (st_srli,  ((unsigned long) p >> 3))                            /* shift    
*/
+ST (st_srai,  ((unsigned long) ((long) p >> 3)))           /* shift    */
+ST (st_slti,  ((unsigned long) ((long) p < 99)))           /* slt      */
+ST (st_sltiu, ((unsigned long) ((unsigned long) p < 99u)))  /* slt      */
+ST (st_clz,   (__builtin_clzl ((unsigned long) p)))        /* clz      */
+ST (st_ctz,   (__builtin_ctzl ((unsigned long) p)))        /* ctz      */
+ST (st_rev8,  (BSWAPL ((unsigned long) p)))                /* bitmanip */
+
+#define LD(NAME, EXPR)                                 \
+  long NAME (long *p, unsigned long x, long *sink)     \
+  {                                                    \
+    long v = *p;                                       \
+    unsigned long q = (EXPR);                          \
+    *sink = v;                                         \
+    return (long) q;                                   \
+  }
+
+LD (ld_add,  ((unsigned long) p + x))                                       /* 
arith   */
+LD (ld_sub,  ((unsigned long) p - x))                                       /* 
arith   */
+LD (ld_and,  ((unsigned long) p & x))                                       /* 
logical */
+LD (ld_or,   ((unsigned long) p | x))                                       /* 
logical */
+LD (ld_xor,  ((unsigned long) p ^ x))                                       /* 
logical */
+LD (ld_sll,  ((unsigned long) p << (x & 31)))                               /* 
shift   */
+LD (ld_srl,  ((unsigned long) p >> (x & 31)))                               /* 
shift   */
+LD (ld_sra,  ((unsigned long) ((long) p >> (x & 31))))                      /* 
shift   */
+LD (ld_slt,  ((unsigned long) ((long) p < (long) x)))                       /* 
slt     */
+LD (ld_sltu, ((unsigned long) ((unsigned long) p < x)))                        
     /* slt     */
+LD (ld_min,  ((unsigned long) ((long) p < (long) x ? (long) p : (long) x)))  
/* min     */
+LD (ld_max,  ((unsigned long) ((long) p > (long) x ? (long) p : (long) x)))  
/* max     */
+LD (ld_minu, ((unsigned long) p < x ? (unsigned long) p : x))               /* 
minu    */
+LD (ld_maxu, ((unsigned long) p > x ? (unsigned long) p : x))               /* 
maxu    */
+
+/* Check the fusion string is present for each function.  */
+/* { dg-final { scan-rtl-dump {;; Function st_addi \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_andi \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_ori \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_xori \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_slli \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_srli \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_srai \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_slti \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_sltiu \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_clz \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_ctz \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function st_rev8 \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_add \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_sub \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_and \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_or \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_xor \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_sll \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_srl \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_sra \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_slt \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_sltu \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_min \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_max \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_minu \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
+/* { dg-final { scan-rtl-dump {;; Function ld_maxu \((?:(?!;; 
Function).)*RISCV_FUSE_LS_UPDATE} "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ls-update-4.c 
b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-4.c
new file mode 100644
index 00000000000..49aef023dff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-4.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im_zbb -mabi=ilp32 
-fdump-rtl-sched2" { target rv32 } } */
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv64im_zbb -mabi=lp64 
-fdump-rtl-sched2" { target rv64 } } */
+
+long
+ld_xnor (long *p, unsigned long x, long *sink)
+{
+  long v = *p;
+  unsigned long q = ~((unsigned long) p ^ x);
+  *sink = v;
+  return (long) q;
+}
+
+long
+ld_off (long *p, unsigned long x, long *sink)
+{
+  long v = p[1];
+  unsigned long q = (unsigned long) p + x;
+  *sink = v;
+  return (long) q;
+}
+
+unsigned long
+st_off (long *p, long val)
+{
+  p[1] = val;
+  return (unsigned long) p + 8;
+}
+
+unsigned long
+st_op_add (long *p, long d)
+{
+  *p = d;
+  return (unsigned long) p + (unsigned long) d;
+}
+
+unsigned long
+st_op_sub (long *p, long d)
+{
+  *p = d;
+  return (unsigned long) p - (unsigned long) d;
+}
+
+/* Must not fuse. */
+unsigned long
+st_op_unrel (long *p, long d, unsigned long x)
+{
+  *p = d;
+  return (unsigned long) p + x;
+}
+
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LS_UPDATE" 5 "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ls-update-5.c 
b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-5.c
new file mode 100644
index 00000000000..c0d64d77d67
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-5.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" { target { rv32 } } } */
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv64im -mabi=lp64 
-fdump-rtl-sched2" { target { rv64 } } } */
+
+int *
+fuse_ls_update_move (int *p, int *q, int x)
+{
+  *q = x;
+  return q;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ls-update.c 
b/gcc/testsuite/gcc.target/riscv/fusion-ls-update.c
new file mode 100644
index 00000000000..b71eab0fcd9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+int
+fuse_ls_update (int *p, int n)
+{
+  int sum = 0;
+  for (int i = 0; i < n; i++)
+    sum += *p++;
+  return sum;
+}
+
+/* XFAIL until scheduling support for RHX-100 series is implemented.  */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" { xfail *-*-* } 
} } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-ld.c 
b/gcc/testsuite/gcc.target/riscv/fusion-lui-ld.c
new file mode 100644
index 00000000000..58dbe4393a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-ld.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+extern int g1, g2;
+extern volatile int g3, g4;
+
+int
+fuse_lui_ld (void)
+{
+  return g1 + g2;
+}
+
+int
+fuse_lui_ld_rev (volatile int *p, int val)
+{
+  int a = *p;
+  int b = g3;
+  int c = p[1];
+  int d = g4;
+  return a + b + c + d + val;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LUI_LD_REV" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-st.c 
b/gcc/testsuite/gcc.target/riscv/fusion-lui-st.c
new file mode 100644
index 00000000000..54c38d7fa33
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-st.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv32 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" } */
+
+volatile int g1, g2, g3;
+
+void
+fuse_store_lui (volatile int *p, int val)
+{
+  *p = val;
+  g1 = val;
+  p[1] = val;
+  g2 = val;
+  p[2] = val;
+  g3 = val;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LUI_ST" "sched2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-mult-add.c 
b/gcc/testsuite/gcc.target/riscv/fusion-mult-add.c
new file mode 100644
index 00000000000..d182a65aa46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-mult-add.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-O3" "-Oz" "-Os" } } 
*/
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv32im -mabi=ilp32 
-fdump-rtl-sched2" { target { rv32 } } } */
+/* { dg-options "-O2 -mtune=arc-v-rhx-100-series -march=rv64im -mabi=lp64 
-fdump-rtl-sched2" { target { rv64 } } } */
+
+int
+fuse_mult_add_op0 (int a, int b, int c)
+{
+  int m = a * b;
+  return m + c;
+}
+
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_MULT_ADD" "sched2" } } */
-- 
2.47.3

Reply via email to