@@ -32,8 +32,17 @@ 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 "insn-attr.h"
+#include "sched-int.h"
#include "riscv-protos.h"
+/* 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))
+
/* Implement TARGET_SCHED_MACRO_FUSION_P. Return true if target supports
instruction fusion of some sort. */
@@ -45,7 +54,7 @@ riscv_macro_fusion_p (void)
/* Return true iff the instruction fusion described by OP is enabled. */
-static bool
+bool
riscv_fusion_enabled_p (enum riscv_fusion_pairs op)
{
return riscv_get_fusible_ops () & op;
@@ -147,6 +156,247 @@ riscv_set_is_shNadduw_p (rtx set)
&& REG_P (SET_DEST (set)));
}
+/* Return TRUE if two memory operands can be fused based on their addresses.
+ Checks if MEM0 and MEM1 have the same base register with adjacent offsets,
+ making them suitable for fusion (e.g., adjacent load/store pairs). */
+
+static bool
+riscv_adjacent_memops_p (rtx mem0, rtx mem1, bool is_load)
+{
+ rtx base0, base1, tmp;
+ HOST_WIDE_INT off0 = 0, off1 = 0;
+
+ if (GET_CODE (mem0) == SIGN_EXTEND || GET_CODE (mem0) == ZERO_EXTEND)
+ mem0 = XEXP (mem0, 0);
+
+ if (GET_CODE (mem1) == SIGN_EXTEND || GET_CODE (mem1) == ZERO_EXTEND)
+ mem1 = XEXP (mem1, 0);
+
+ if (!MEM_P (mem0) || !MEM_P (mem1))
+ return false;
+
+ if (GET_MODE (mem0) != GET_MODE (mem1))
+ return false;
+
+ /* Check if the mode is allowed for ARC-V fusion restrictions.
+ Loads: allow SI, HI, and QI modes.
+ Stores: allow only SI mode. */
+ if (TARGET_ARCV_RHX100)
+ {
+ machine_mode mode = GET_MODE (mem0);
+ bool mode_allowed = ((is_load
+ && (mode == SImode
+ || mode == HImode
+ || mode == QImode))
+ || (!is_load && mode == SImode));
+
+ if (!mode_allowed)
+ return false;
+ }
+
+ rtx mem_addr0 = XEXP (mem0, 0);
+ rtx mem_addr1 = XEXP (mem1, 0);
+
+ if (GET_CODE (mem_addr0) == PLUS)
+ {
+ base0 = XEXP (mem_addr0, 0);
+ tmp = XEXP (mem_addr0, 1);
+ if (!REG_P (base0) || !CONST_INT_P (tmp))
+ return false;
+ off0 = INTVAL (tmp);
+ }
+ else if (REG_P (mem_addr0))
+ base0 = mem_addr0;
+ else
+ return false;
+
+ if (GET_CODE (mem_addr1) == PLUS)
+ {
+ base1 = XEXP (mem_addr1, 0);
+ tmp = XEXP (mem_addr1, 1);
+ if (!REG_P (base1) || !CONST_INT_P (tmp))
+ return false;
+ off1 = INTVAL (tmp);
+ }
+ else if (REG_P (mem_addr1))
+ base1 = mem_addr1;
+ else
+ return false;
+
+ /* Check if we have the same base. */
+ if (REGNO (base0) != REGNO (base1))
+ return false;
+
+ /* Fuse adjacent aligned addresses. */
+ if ((off0 % GET_MODE_SIZE (GET_MODE (mem0)).to_constant () == 0)
+ && (abs (off1 - off0) == GET_MODE_SIZE (GET_MODE (mem0)).to_constant ()))
+ return true;
+
+ return false;
+}
+
+/* Return true if CURR should not be fused with PREV because CURR and the
+ next fusible insn form a better adjacent load/store pair. */
+
+static bool
+riscv_defer_for_adjacent_memop_p (rtx_insn *curr)
+{
+ rtx_insn *next = next_nonnote_nondebug_insn_bb (curr);
+ if (!next)
+ return false;
+
+ rtx curr_set = single_set (curr);
+ rtx next_set = single_set (next);
+ if (!curr_set || !next_set)
+ return false;
+
+ if (riscv_fusion_enabled_p (RISCV_FUSE_ADJACENT_LOAD)
+ && get_attr_type (curr) == TYPE_LOAD
+ && get_attr_type (next) == TYPE_LOAD
+ && riscv_adjacent_memops_p (SET_SRC (curr_set), SET_SRC (next_set), true))
+ return true;
+
+ return riscv_fusion_enabled_p (RISCV_FUSE_ADJACENT_STORE)
+ && get_attr_type (curr) == TYPE_STORE
+ && get_attr_type (next) == TYPE_STORE
+ && riscv_adjacent_memops_p (SET_DEST (curr_set),
+ SET_DEST (next_set), 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 prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set)
+ return false;
+
+ enum attr_type p_type = get_attr_type (prev);
+ if (!(p_type == TYPE_LOAD || p_type == TYPE_STORE))
+ 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);
+ rtx c_dest = SET_DEST (curr_set);
+
+ if (CONSTANT_P (c_src))
+ return false;
+
+ int c_rs1 = INVALID_REGNUM;
+ int c_rs2 = INVALID_REGNUM;
+
+ if (REG_P (c_src))
+ c_rs1 = 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);
+ if (SUBREG_P (x))
+ x = SUBREG_REG (x);
+ if (!REG_P (x))
+ continue;
+
+ if (c_rs1 == (int) INVALID_REGNUM)
+ c_rs1 = REGNO (x);
+ else
+ {
+ c_rs2 = REGNO (x);
+ break;
+ }
+ }
+
+ if (c_rs1 == (int) INVALID_REGNUM)
+ return false;
+ }
+
+ switch (p_type)
+ {
+ case TYPE_LOAD:
+ {
+ if (!REG_P (c_dest))
+ return false;
+ int c_rd = REGNO (c_dest);
+
+ rtx p_mem = SET_SRC (prev_set);
+
+ if (!MEM_P (p_mem))
+ return false;
+
+ rtx p_dest = SET_DEST (prev_set);
+ rtx base, offset;
+ if (!extract_base_offset_in_addr (p_mem, &base, &offset)
+ || !REG_P (p_dest))
+ return false;
+
+ int p_rs = REGNO (base);
+ int p_rd = REGNO (p_dest);
+
+ return (p_rs == c_rs1
+ && p_rs != p_rd
+ && p_rd != c_rd
+ && !reg_overlap_mentioned_p (p_dest, c_src));
+ }
+
+ case TYPE_STORE:
+ {
+ rtx p_mem = SET_DEST (prev_set);
+ if (!MEM_P (p_mem))
+ return false;
+
+ rtx base, offset;
+ if (!extract_base_offset_in_addr (p_mem, &base, &offset))
+ return false;
+
+ int p_rs = REGNO (base);
+
+ if (p_rs != c_rs1)
+ return false;
+
+ if (c_rs2 == (int) INVALID_REGNUM)
+ return true;
+
+ rtx data = SET_SRC (prev_set);
+ if (!REG_P (data))
+ return false;
+
+ int p_rs2 = REGNO (data);
+ return p_rs2 == c_rs2;
+ }
+
+ default:
+ return false;
+ }
+}
+
/* 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)))
@@ -406,26 +656,38 @@ riscv_fuse_auipc_addi (rtx_insn *prev, rtx_insn *curr)
return false;
}
-/* Check for RISCV_FUSE_LUI_LD fusion.
- prev (lui) == (set (reg:DI rD) (const_int UPPER_IMM_20))
- curr (ld) == (set (reg:DI rD) (mem:DI (plus:DI (reg:DI rD) ...))) */
+/* Common lui+ld fusion matcher.
+ SAME_DEST_REGS: if true, require same dest (via same_dest_p); if false,
+ require different dests via REGNO (same_dest_p is always true before reload).
+ MATCH_INPUT_REG: if true, load must use the lui dest as base; if false,
+ any load after a lui is enough (Fused_LD_LUI). */
static bool
-riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr)
+riscv_fuse_lui_ld_pair_p (rtx_insn *prev, rtx_insn *curr,
+ bool same_dest_regs, bool match_input_reg)
{
rtx prev_set = single_set (prev);
rtx curr_set = single_set (curr);
if (!prev_set || !curr_set || any_condjump_p (curr))
return false;
- if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+ if (same_dest_regs)
+ {
+ if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+ return false;
+ }
+ else if (!REG_P (SET_DEST (prev_set)) || !REG_P (SET_DEST (curr_set))
+ || REGNO (SET_DEST (prev_set)) == REGNO (SET_DEST (curr_set)))
return false;
- /* A LUI_OPERAND accepts (const_int 0), but we won't emit that as LUI.
- Reject that case explicitly. */
+ if (!match_input_reg)
+ return ((GET_CODE (SET_SRC (prev_set)) == HIGH
+ || (CONST_INT_P (SET_SRC (prev_set))
+ && LUI_NONZERO_OPERAND (INTVAL (SET_SRC (prev_set)))))
+ && get_attr_type (curr) == TYPE_LOAD);
+
if (CONST_INT_P (SET_SRC (prev_set))
- && SET_SRC (prev_set) != CONST0_RTX (GET_MODE (SET_DEST (prev_set)))
- && LUI_OPERAND (INTVAL (SET_SRC (prev_set)))
+ && LUI_NONZERO_OPERAND (INTVAL (SET_SRC (prev_set)))
&& MEM_P (SET_SRC (curr_set))
&& SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set)))
&& GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS
@@ -457,6 +719,18 @@ riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr)
return false;
}
+/* Check for RISCV_FUSE_LUI_LD fusion.
+ prev (lui) == (set (reg:DI rD) (const_int UPPER_IMM_20))
+ curr (ld) == (set (reg:DI rD) (mem:DI (plus:DI (reg:DI rD) ...))) */
+
+static bool
+riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_lui_ld_pair_p (prev, curr,
+ /* same_dest_regs */ true,
+ /* match_input_reg */ true);
+}
+
/* Check for RISCV_FUSE_AUIPC_LD fusion.
prev (auipc) == (set (reg:DI rD) (unspec:DI UNSPEC_AUIPC))
curr (ld) == (set (reg:DI rD) (mem:DI (plus:DI (reg:DI rD) ...))) */
@@ -597,12 +871,13 @@ riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr)
return false;
}
-/* Check for RISCV_FUSE_BFEXT fusion.
- prev (slli) == (set (reg:DI rD) (ashift:DI (reg:DI rS) (const_int)))
- curr (srli) == (set (reg:DI rD) (lshiftrt:DI (reg:DI rD) (const_int))) */
+/* Check for RISCV_FUSE_BFEXT_SRLI / RISCV_FUSE_BFEXT_SRAI fusion.
+ prev (slli) == (set (reg rD) (ashift (reg rS) (const_int)))
+ curr (srli/srai) == (set (reg rD)
+ (lshiftrt/ashiftrt (reg rD) (const_int))) */
static bool
-riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
+riscv_fuse_bfext_shift_p (rtx_insn *prev, rtx_insn *curr, enum rtx_code shift)
{
rtx prev_set = single_set (prev);
rtx curr_set = single_set (curr);
@@ -613,10 +888,10 @@ riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
return false;
if (GET_CODE (SET_SRC (prev_set)) == ASHIFT
- && (GET_CODE (SET_SRC (curr_set)) == LSHIFTRT
- || GET_CODE (SET_SRC (curr_set)) == ASHIFTRT)
+ && GET_CODE (SET_SRC (curr_set)) == shift
&& REG_P (SET_DEST (prev_set))
&& REG_P (SET_DEST (curr_set))
+ && REG_P (XEXP (SET_SRC (curr_set), 0))
&& REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))
&& CONST_INT_P (XEXP (SET_SRC (prev_set), 1))
&& CONST_INT_P (XEXP (SET_SRC (curr_set), 1)))
@@ -625,6 +900,26 @@ riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
return false;
}
+/* Check for RISCV_FUSE_BFEXT_SRLI fusion.
+ prev (slli) == (set (reg rD) (ashift (reg rS) (const_int)))
+ curr (srli) == (set (reg rD) (lshiftrt (reg rD) (const_int))) */
+
+static bool
+riscv_fuse_bfext_srli (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_bfext_shift_p (prev, curr, LSHIFTRT);
+}
+
+/* Check for RISCV_FUSE_BFEXT_SRAI fusion.
+ prev (slli) == (set (reg rD) (ashift (reg rS) (const_int)))
+ curr (srai) == (set (reg rD) (ashiftrt (reg rD) (const_int))) */
+
+static bool
+riscv_fuse_bfext_srai (rtx_insn *prev, rtx_insn *curr)
+{
+ return riscv_fuse_bfext_shift_p (prev, curr, ASHIFTRT);
+}
+
/* Check for RISCV_FUSE_B_ALUI fusion.
Covers orc.b+not, ctz+andi, sub+smax, neg+smax patterns. */
@@ -691,6 +986,239 @@ riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr)
return false;
}
+/* 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 = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set)
+ return false;
+
+ if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+ return false;
+
+ rtx prev_src = SET_SRC (prev_set);
+ rtx curr_src = SET_SRC (curr_set);
+ if (GET_CODE (prev_src) == SIGN_EXTEND
+ && GET_MODE (prev_src) == DImode)
+ prev_src = XEXP (prev_src, 0);
+ if (GET_CODE (curr_src) == SIGN_EXTEND
+ && GET_MODE (curr_src) == DImode)
+ curr_src = XEXP (curr_src, 0);
+
+ if (GET_CODE (prev_src) != MULT || GET_MODE (prev_src) != SImode
+ || GET_CODE (curr_src) != PLUS || GET_MODE (curr_src) != SImode)
+ return false;
+
+ rtx mult_dest = SET_DEST (prev_set);
+ if (!REG_P (mult_dest))
+ return false;
+ unsigned int mult_dest_regno = REGNO (mult_dest);
+
+ if (REG_P (XEXP (curr_src, 0))
+ && REGNO (XEXP (curr_src, 0)) == mult_dest_regno)
+ return true;
+
+ return false;
+}
+
+/* Check for RISCV_FUSE_LI_BRANCH fusion.
+ prev (li) == (set (reg:DI rD) (const_int N))
+ curr (branch) == conditional branch using rD */
+
+static bool
+riscv_fuse_li_branch (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set)
+ return false;
+
+ if (get_attr_type (prev) != TYPE_MOVE
+ || get_attr_move_type (prev) != MOVE_TYPE_CONST)
+ return false;
+
+ if (!any_condjump_p (curr))
+ return false;
+
+ rtx prev_dest = SET_DEST (prev_set);
+ if (!REG_P (prev_dest))
+ return false;
+
+ /* Check if the loaded register is used in the branch condition. */
+ rtx cond = XEXP (SET_SRC (curr_set), 0);
+ unsigned int prev_dest_regno = REGNO (prev_dest);
+ if ((REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == prev_dest_regno)
+ || (REG_P (XEXP (cond, 1)) && REGNO (XEXP (cond, 1)) == prev_dest_regno))
+ return true;
+
+ return false;
+}
+
+/* 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 + 4 or OFF2 == OFF1 - 4 */
+
+static bool
+riscv_fuse_adjacent_load (rtx_insn *prev, rtx_insn *curr)
+{
+ /* Do not fuse loads/stores before sched2. */
+ if (!reload_completed || sched_fusion)
+ return false;
+
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ if (get_attr_type (prev) != TYPE_LOAD
+ || get_attr_type (curr) != TYPE_LOAD)
+ return false;
+
+ return riscv_adjacent_memops_p (SET_SRC (prev_set), SET_SRC (curr_set), true);
+}
+
+/* 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)
+{
+ /* Do not fuse loads/stores before sched2. */
+ if (!reload_completed || sched_fusion)
+ return false;
+
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ if (get_attr_type (prev) != TYPE_STORE
+ || get_attr_type (curr) != TYPE_STORE)
+ return false;
+
+ return riscv_adjacent_memops_p (SET_DEST (prev_set),
+ SET_DEST (curr_set), false);
+}
+
+/* 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 (!reload_completed || sched_fusion)
+ return false;
+
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ if (riscv_defer_for_adjacent_memop_p (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)
+{
+ return (get_attr_type (store) == TYPE_STORE
+ && REG_P (SET_DEST (lui_set))
+ && ((get_attr_type (lui) == TYPE_MOVE
+ && GET_CODE (SET_SRC (lui_set)) == HIGH)
+ || (CONST_INT_P (SET_SRC (lui_set))
+ && LUI_NONZERO_OPERAND (INTVAL (SET_SRC (lui_set))))));
+}
+
+/* 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)
+{
+ if (!reload_completed || sched_fusion)
+ return false;
+
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ if (riscv_defer_for_adjacent_memop_p (curr))
+ 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 IMM))
+ 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)
+{
+ if (!reload_completed || sched_fusion)
+ return false;
+
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+ if (!prev_set || !curr_set || any_condjump_p (curr))
+ return false;
+
+ if (riscv_defer_for_adjacent_memop_p (curr))
+ return false;
+
+ if (get_attr_type (prev) == TYPE_MOVE
+ && get_attr_move_type (prev) == MOVE_TYPE_CONST
+ && get_attr_type (curr) == TYPE_STORE
+ && REG_P (SET_SRC (curr_set)) && REG_P (SET_DEST (prev_set))
+ && REGNO (SET_SRC (curr_set)) == REGNO (SET_DEST (prev_set)))
+ return true;
+
+ return false;
+}
+
+/* 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_pair_p (prev, curr,
+ /* same_dest_regs */ false,
+ /* match_input_reg */ false)
+ || riscv_fuse_lui_ld_pair_p (curr, prev,
+ /* same_dest_regs */ false,
+ /* match_input_reg */ false));
+}
+
/* Type for a fusion checker function. Takes the two candidate insns
and returns true if they should be fused. */
@@ -737,10 +1265,28 @@ static const struct riscv_fusion_entry riscv_fusion_table[] =
riscv_fuse_cache_aligned_std, "RISCV_FUSE_CACHE_ALIGNED_STD" },
{ RISCV_FUSE_ALIGNED_STD,
riscv_fuse_aligned_std, "RISCV_FUSE_ALIGNED_STD" },
- { RISCV_FUSE_BFEXT,
- riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
+ { RISCV_FUSE_BFEXT_SRLI,
+ riscv_fuse_bfext_srli, "RISCV_FUSE_BFEXT_SRLI" },
+ { RISCV_FUSE_BFEXT_SRAI,
+ riscv_fuse_bfext_srai, "RISCV_FUSE_BFEXT_SRAI" },
{ RISCV_FUSE_B_ALUI,
riscv_fuse_b_alui, "RISCV_FUSE_B_ALUI" },
+ { 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" },
};
/* Implement TARGET_SCHED_MACRO_FUSION_PAIR_P. Return true if PREV and CURR
@@ -864,13 +864,23 @@ enum riscv_fusion_pairs
RISCV_FUSE_LDPREINCREMENT = (1 << 8),
RISCV_FUSE_ALIGNED_STD = (1 << 9),
RISCV_FUSE_CACHE_ALIGNED_STD = (1 << 10),
- RISCV_FUSE_BFEXT = (1 << 11),
+ RISCV_FUSE_BFEXT_SRLI = (1 << 11),
RISCV_FUSE_EXPANDED_LD = (1 << 12),
RISCV_FUSE_B_ALUI = (1 << 13),
+ RISCV_FUSE_MULT_ADD = (1 << 14),
+ RISCV_FUSE_LI_BRANCH = (1 << 15),
+ RISCV_FUSE_ADJACENT_LOAD = (1 << 16),
+ RISCV_FUSE_ADJACENT_STORE = (1 << 17),
+ RISCV_FUSE_LS_UPDATE = (1 << 18),
+ RISCV_FUSE_LUI_ST = (1 << 19),
+ RISCV_FUSE_LI_STORE = (1 << 20),
+ RISCV_FUSE_LUI_LD_REV = (1 << 21),
+ RISCV_FUSE_BFEXT_SRAI = (1 << 22),
};
extern bool riscv_macro_fusion_p (void);
extern bool riscv_macro_fusion_pair_p (rtx_insn *, rtx_insn *);
+extern bool riscv_fusion_enabled_p (enum riscv_fusion_pairs);
extern unsigned int riscv_get_fusible_ops (void);
/* Routines implemented in thead.cc. */
@@ -980,7 +980,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 */
@@ -978,6 +978,10 @@ extern enum riscv_cc get_riscv_cc (const rtx use);
|| (riscv_microarchitecture == sifive_p600) \
|| (riscv_microarchitecture == andes_45_series))
+/* True if the target is ARC-V RHX100. */
+#define TARGET_ARCV_RHX100 \
+ (riscv_microarchitecture == arcv_rhx100)
+
/* True if the target supports misaligned vector loads and stores. */
#define TARGET_VECTOR_MISALIGN_SUPPORTED \
riscv_vector_unaligned_access_p
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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 *-*-* } } } */
new file mode 100644
@@ -0,0 +1,81 @@
+/* { 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 */
+
+/* { dg-final { scan-assembler {\madd\M} } } */
+/* { dg-final { scan-assembler {\maddi\M} } } */
+/* { dg-final { scan-assembler {\msub\M} } } */
+/* { dg-final { scan-assembler {\mand\M} } } */
+/* { dg-final { scan-assembler {\mandi\M} } } */
+/* { dg-final { scan-assembler {\mor\M} } } */
+/* { dg-final { scan-assembler {\mori\M} } } */
+/* { dg-final { scan-assembler {\mxor\M} } } */
+/* { dg-final { scan-assembler {\mxori\M} } } */
+/* { dg-final { scan-assembler {\msll\M} } } */
+/* { dg-final { scan-assembler {\mslli\M} } } */
+/* { dg-final { scan-assembler {\msrl\M} } } */
+/* { dg-final { scan-assembler {\msrli\M} } } */
+/* { dg-final { scan-assembler {\msra\M} } } */
+/* { dg-final { scan-assembler {\msrai\M} } } */
+/* { dg-final { scan-assembler {\mslt\M} } } */
+/* { dg-final { scan-assembler {\mslti\M} } } */
+/* { dg-final { scan-assembler {\msltu\M} } } */
+/* { dg-final { scan-assembler {\msltiu\M} } } */
+/* { dg-final { scan-assembler {\mmin\M} } } */
+/* { dg-final { scan-assembler {\mmax\M} } } */
+/* { dg-final { scan-assembler {\mminu\M} } } */
+/* { dg-final { scan-assembler {\mmaxu\M} } } */
+/* { dg-final { scan-assembler {\mclz\M} } } */
+/* { dg-final { scan-assembler {\mctz\M} } } */
+/* { dg-final { scan-assembler {\mrev8\M} } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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 *-*-* } } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */