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