[PATCH v2 1/4] RISC-V: Implement riscv_macro_fusion_pair_p for ARC-V RHX-100 series
Luis Silva <[email protected]> Wed, 5 Aug 2026 11:02:09 +0100
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <LO6P265MB6224C605086BBA0608FAAB29B4D32@LO6P265MB6224.GBRP265.PROD.OUTLOOK.COM> |
From: Luis Silva <[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. Pairs that compete with adjacent load/store (ls-update, lui-st, li-store) bail out when the next insn is a better adjacent memop candidate. Those, plus adjacent load/store themselves, wait until after reload. 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 adjacent by GET_MODE_SIZE. RISCV_FUSE_ADJACENT_STORE (riscv_fuse_adjacent_store, I1<->I2) I1/I2: two stores, same base, offsets adjacent by GET_MODE_SIZE. Also rename RISCV_FUSE_BFEXT to RISCV_FUSE_BFEXT_SRLI and add RISCV_FUSE_BFEXT_SRAI for other tunes. gcc/ChangeLog: * config/riscv/riscv-fusion.cc (LUI_NONZERO_OPERAND): New macro. (riscv_fusion_enabled_p): Make non-static. (riscv_adjacent_memops_p): New function. (riscv_defer_for_adjacent_memop_p): New function. (riscv_ls_update_pair_p): New function. (riscv_fuse_lui_ld_pair_p): New function. (riscv_fuse_lui_ld): Use riscv_fuse_lui_ld_pair_p. (riscv_fuse_bfext_shift_p): New function. (riscv_fuse_bfext_srli): New function. (riscv_fuse_bfext_srai): 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_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: Loeka Rogge <[email protected]> Signed-off-by: Luis Silva <[email protected]> --- gcc/config/riscv/riscv-fusion.cc | 582 +++++++++++++++++- gcc/config/riscv/riscv-protos.h | 12 +- gcc/config/riscv/riscv.cc | 6 +- gcc/config/riscv/riscv.h | 4 + .../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 | 81 +++ .../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 + 18 files changed, 905 insertions(+), 20 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 4846dc0dfa2..98df3521029 100644 --- a/gcc/config/riscv/riscv-fusion.cc +++ b/gcc/config/riscv/riscv-fusion.cc @@ -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 diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 3843110ab6c..a94c37fa052 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -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. */ diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index e319ccaecad..aabd12aa040 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.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 */ diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h index 4ba6ad260e5..b01301659e1 100644 --- a/gcc/config/riscv/riscv.h +++ b/gcc/config/riscv/riscv.h @@ -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 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..5e4ba9bac21 --- /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..9731dedc4f3 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/fusion-ls-update-3.c @@ -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} } } */ 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