[PATCH v5 02/18] RISC-V: Refine macro-fusion helper predicates
Jin Ma <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
Factor common RTL parsing out of the existing macro-fusion recognizers. Preserve aggressive destination overmatching before register allocation while checking real producer-consumer dependencies in both scheduling passes. Use parameterized arithmetic and memory classifiers so later fusion recognizers can share structural matching without duplicating microarchitecture-specific constraints. gcc/ChangeLog: * config/riscv/riscv-fusion.cc: Include insn-attr.h. (riscv_fuse_sets_p): New function. (riscv_set_extract_word_binary_p): Likewise. (riscv_regno): Likewise. (riscv_fuse_same_reg_p): Likewise. (riscv_fuse_same_dest_p): Rename from riscv_fusion_same_dest_p and handle hard-register SUBREGs. Preserve preallocation destination overmatching and optionally check the first-source dependency. (riscv_set_is_addiw_p): New function. (riscv_insn_is_mv_li_p): Likewise. (riscv_insn_is_addi_type_p): Likewise. (riscv_set_is_shift_p): Likewise. (riscv_set_is_slli_p): Likewise. (riscv_set_is_srli_p): Likewise. (riscv_set_is_srai_p): Likewise. (enum sched_fusion_type): New enum. (riscv_fusion_mem_info): New struct. (riscv_fuse_extract_address): New function. (riscv_fuse_mem_p): Likewise. (riscv_fuse_zext_common): Require RV64 and use the common helpers. (riscv_fuse_zextw): Update accordingly. (riscv_fuse_zextws): Likewise. (riscv_fuse_zexth): Likewise. (riscv_fuse_ldindexed): Use the common helpers. (riscv_fuse_expanded_ld): Likewise. (riscv_fuse_ldpreincrement): Likewise. Check the update dependency. (riscv_fuse_lui_addi): Reuse the ADDI matcher, check the source dependency, and reject zero-valued LUI patterns. (riscv_fuse_auipc_addi): Reuse the ADDI matcher and check the source dependency. (riscv_fuse_lui_ld): Use the common helpers. (riscv_fuse_auipc_ld): Likewise. Check the address dependency. (riscv_fuse_aligned_std): Use the common helpers. (riscv_fuse_bfext): Likewise. (riscv_fuse_b_alui): Likewise. gcc/testsuite/ChangeLog: * gcc.target/riscv/fusion-lui-addi-prera-rtl.c: New test. Signed-off-by: Jin Ma <[email protected]> --- gcc/config/riscv/riscv-fusion.cc | 847 ++++++++++++------ .../riscv/fusion-lui-addi-prera-rtl.c | 63 ++ 2 files changed, 648 insertions(+), 262 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-lui-addi-prera-rtl.c diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc index 2ce9b965974..88735b580f1 100644 --- a/gcc/config/riscv/riscv-fusion.cc +++ b/gcc/config/riscv/riscv-fusion.cc @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "rtl.h" #include "regs.h" #include "insn-config.h" +#include "insn-attr.h" #include "recog.h" #include "function.h" #include "memmodel.h" @@ -51,25 +52,160 @@ riscv_fusion_enabled_p (enum riscv_fusion_pairs op) return riscv_get_fusible_ops () & op; } -/* Return true if PREV_SET and CURR_SET satisfy the same-dest constraint - required by most fusion rules: when we are past register allocation - (i.e. can_create_pseudo_p () is false), the destination registers of - the two sets must be the same physical register. */ +/* Extract the single sets from PREV and CURR. Reject conditional jumps, + which cannot form a fusion pair. */ static bool -riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set) +riscv_fuse_sets_p (rtx_insn *prev, rtx_insn *curr, + rtx *prev_set_out = NULL, + rtx *curr_set_out = NULL) { + rtx prev_set = single_set (prev); + rtx curr_set = single_set (curr); + if (prev_set_out) + *prev_set_out = prev_set; + if (curr_set_out) + *curr_set_out = curr_set; + return prev_set && curr_set && !any_condjump_p (curr); +} + +/* Extract a word-form binary operation with code CODE: + (set (reg:DI rd) + (sign_extend:DI (op:SI operand1 operand2))) + or + (set (reg:DI rd) + (sign_extend:DI (truncate:SI (op:DI operand1 operand2)))) + or + (set (reg:DI rd) + (sign_extend:DI (subreg:SI (op:DI operand1 operand2) 0))) + Store the binary operation in *BINARY_SRC. */ + +static bool +riscv_set_extract_word_binary_p (rtx set, rtx_code code, rtx *binary_src) +{ + if (!TARGET_64BIT) + return false; + + rtx src = SET_SRC (set); + *binary_src = NULL_RTX; + + if (GET_CODE (src) != SIGN_EXTEND + || GET_MODE (src) != DImode) + return false; + + src = XEXP (src, 0); + if (GET_CODE (src) == code && GET_MODE (src) == SImode) + { + *binary_src = src; + return true; + } + + if (GET_MODE (src) != SImode) + return false; + + if (GET_CODE (src) == TRUNCATE) + src = XEXP (src, 0); + else if (SUBREG_P (src) && subreg_lowpart_p (src)) + src = SUBREG_REG (src); + else + return false; + + if (GET_CODE (src) == code && GET_MODE (src) == DImode) + { + *binary_src = src; + return true; + } + + return false; +} + +/* Return a comparable register number for X, accounting for hard-register + SUBREG offsets, or INVALID_REGNUM. */ + +static unsigned int +riscv_regno (rtx x) +{ + int regno = true_regnum (x); + if (regno >= 0 + && (can_create_pseudo_p () || regno < FIRST_PSEUDO_REGISTER)) + return regno; + + /* Before allocation, use an unassigned pseudo's identity. Treat every + SUBREG of the pseudo alike so that matching remains deliberately + permissive until the final hard register is known. */ if (can_create_pseudo_p ()) - return true; + { + rtx reg = SUBREG_P (x) ? SUBREG_REG (x) : x; + if (REG_P (reg) && !HARD_REGISTER_P (reg)) + return REGNO (reg); + } + + return INVALID_REGNUM; +} + +/* Return true if X and Y refer to the same comparable register. */ - unsigned int prev_dest_regno = (REG_P (SET_DEST (prev_set)) - ? REGNO (SET_DEST (prev_set)) - : FIRST_PSEUDO_REGISTER); - unsigned int curr_dest_regno = (REG_P (SET_DEST (curr_set)) - ? REGNO (SET_DEST (curr_set)) - : FIRST_PSEUDO_REGISTER); +static bool +riscv_fuse_same_reg_p (rtx x, rtx y) +{ + unsigned int x_regno = riscv_regno (x); + unsigned int y_regno = riscv_regno (y); - return prev_dest_regno == curr_dest_regno; + return (x_regno != INVALID_REGNUM + && y_regno != INVALID_REGNUM + && x_regno == y_regno); +} + +/* Return true if PREV_SET and CURR_SET satisfy the same-destination + constraint. Before allocation, defer the destination-register comparison + so that the scheduler can keep potential fusion pairs together. If + USED_IN_SRC_P, always require CURR_SET to use PREV_SET's destination as its + first source operand. After allocation, also require the hard-register + destinations to match. */ + +static bool +riscv_fuse_same_dest_p (rtx prev_set, rtx curr_set, + bool used_in_src_p = false) +{ + rtx prev_dest = SET_DEST (prev_set); + rtx curr_dest = SET_DEST (curr_set); + unsigned int prev_dest_regno = riscv_regno (prev_dest); + if (prev_dest_regno == INVALID_REGNUM + || riscv_regno (curr_dest) == INVALID_REGNUM) + return false; + + if (used_in_src_p) + { + rtx src = SET_SRC (curr_set); + rtx word_add_src; + if (riscv_set_extract_word_binary_p (curr_set, PLUS, + &word_add_src)) + src = XEXP (word_add_src, 0); + else + { + while (GET_CODE (src) == SIGN_EXTEND + || GET_CODE (src) == ZERO_EXTEND) + src = XEXP (src, 0); + + if (GET_CODE (src) == NOT + || BINARY_P (src) + || GET_CODE (src) == LO_SUM + || GET_CODE (src) == ZERO_EXTRACT) + src = XEXP (src, 0); + + while (GET_CODE (src) == SIGN_EXTEND + || GET_CODE (src) == ZERO_EXTEND) + src = XEXP (src, 0); + } + + if (!riscv_fuse_same_reg_p (src, prev_dest)) + return false; + } + + if (can_create_pseudo_p ()) + return true; + + return riscv_fuse_same_reg_p (prev_dest, curr_dest); } /* Matches an add: @@ -147,28 +283,324 @@ riscv_set_is_shNadduw_p (rtx set) && REG_P (SET_DEST (set))); } +/* Matches an addiw: + (set (reg:DI rd) + (sign_extend:DI (plus:SI (reg:SI rs1) (const_int imm12)))) + or an equivalent word-add RTL form. */ + +static bool +riscv_set_is_addiw_p (rtx set, rtx *src0 = NULL) +{ + if (!TARGET_64BIT) + return false; + + rtx src; + if (riscv_set_extract_word_binary_p (set, PLUS, &src) + && REG_P (XEXP (src, 0)) + && CONST_INT_P (XEXP (src, 1)) + && REG_P (SET_DEST (set))) + { + if (src0) + *src0 = XEXP (src, 0); + 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) +{ + 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; + + if (move_type == MOVE_TYPE_CONST + && CONST_INT_P (src) + /* Distinguish an ADDI-based LI from other single-insn constants. */ + && SMALL_OPERAND (INTVAL (src))) + return true; + + return false; +} + +/* Matches an addi-type instruction: + (set (reg rd) (plus (reg rs1) (const_int imm12))) + or: + (set (reg rd) (lo_sum (reg rs1) symbol)) + or an accepted addiw, mv, or li form. ALLOW_WORD_P controls whether + ADDIW is accepted. Store the register source in *SRC0 when available. */ + +static bool +riscv_insn_is_addi_type_p (rtx_insn *insn, bool allow_word_p = true, + rtx *src0 = NULL) +{ + rtx set = single_set (insn); + if (!set) + return false; + + rtx src = SET_SRC (set); + if (riscv_insn_is_mv_li_p (insn)) + { + if (src0 && REG_P (src)) + *src0 = src; + return true; + } + + if (get_attr_type (insn) != TYPE_ARITH) + return false; + + if (GET_CODE (src) == LO_SUM + && GET_MODE (src) == Pmode + && REG_P (XEXP (src, 0))) + { + if (src0) + *src0 = XEXP (src, 0); + return true; + } + + if (riscv_set_is_addi_p (set)) + { + if (!allow_word_p && GET_MODE (src) != Pmode) + return false; + if (src0) + *src0 = XEXP (src, 0); + return true; + } + + if (allow_word_p + && riscv_set_is_addiw_p (set, src0)) + return true; + + return false; +} + +/* Return true if SET is a non-wrapped scalar shift of CODE. */ + +static bool +riscv_set_is_shift_p (rtx set, rtx_code code) +{ + rtx src = SET_SRC (set); + machine_mode mode = GET_MODE (src); + + if (GET_CODE (src) != code || !CONST_INT_P (XEXP (src, 1))) + return false; + + if (((TARGET_64BIT && mode == DImode) || mode == SImode) + && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM + && REG_P (SET_DEST (set))) + return true; + + return false; +} + +/* Matches an slli: + (set (reg rd) (ashift (reg rs1) (const_int shamt))). */ + +static bool +riscv_set_is_slli_p (rtx set) +{ + return riscv_set_is_shift_p (set, ASHIFT); +} + +/* Matches an srli: + (set (reg rd) (lshiftrt (reg rs1) (const_int shamt))). */ + +static bool +riscv_set_is_srli_p (rtx set) +{ + return riscv_set_is_shift_p (set, LSHIFTRT); +} + +/* Matches an srai: + (set (reg rd) (ashiftrt (reg rs1) (const_int shamt))). */ + +static bool +riscv_set_is_srai_p (rtx set) +{ + return riscv_set_is_shift_p (set, ASHIFTRT); +} + +/* Load/store classes used by fusion checks. */ +enum sched_fusion_type +{ + SCHED_FUSION_LD_SIGN_EXTEND = 0, + SCHED_FUSION_LD_ZERO_EXTEND, + SCHED_FUSION_LD, + SCHED_FUSION_ST +}; + +/* Fusion-relevant information about a scalar load or store. */ + +struct riscv_fusion_mem_info +{ + enum sched_fusion_type type; + struct riscv_address_info addr; + machine_mode mode; + bool fp_p; +}; + +/* Extract fusion-relevant information from scalar load/store address X. */ + +static bool +riscv_fuse_extract_address (rtx x, struct riscv_address_info *addr) +{ + switch (GET_CODE (x)) + { + case REG: + case SUBREG: + addr->type = ADDRESS_REG; + addr->reg = x; + addr->offset = const0_rtx; + break; + + case PLUS: + if (!CONST_INT_P (XEXP (x, 1))) + return false; + + addr->type = ADDRESS_REG; + addr->reg = XEXP (x, 0); + addr->offset = XEXP (x, 1); + break; + + case LO_SUM: + addr->type = ADDRESS_LO_SUM; + addr->reg = XEXP (x, 0); + addr->offset = XEXP (x, 1); + break; + + default: + return false; + } + + return riscv_regno (addr->reg) != INVALID_REGNUM; +} + +/* Extract a scalar integer or floating-point load/store into *INFO: + (set (reg rd) (mem addr)) + or: + (set (reg rd) (any_extend (mem addr))) + or: + (set (reg frd) (mem addr)) + or: + (set (mem addr) (reg rs1)) + or: + (set (mem addr) (const_int 0)) + or: + (set (mem addr) (reg frs1)). */ + +static bool +riscv_fuse_mem_p (rtx_insn *insn, struct riscv_fusion_mem_info *info) +{ + gcc_assert (INSN_P (insn)); + + rtx set = single_set (insn); + if (!set) + return false; + + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + info->type = SCHED_FUSION_LD; + + if (GET_CODE (src) == SIGN_EXTEND + || GET_CODE (src) == ZERO_EXTEND) + { + info->type = (GET_CODE (src) == SIGN_EXTEND + ? SCHED_FUSION_LD_SIGN_EXTEND + : SCHED_FUSION_LD_ZERO_EXTEND); + src = XEXP (src, 0); + if (!MEM_P (src)) + return false; + } + + /* Exclude RVV loads and stores. */ + if (riscv_vector_mode_p (GET_MODE (dest)) + || riscv_vector_mode_p (GET_MODE (src))) + return false; + + if (!MEM_P (src) && !MEM_P (dest)) + return false; + + rtx mem = MEM_P (src) ? src : dest; + poly_uint64 mode_size = GET_MODE_SIZE (GET_MODE (mem)); + /* Reject wide moves that split into multiple instructions, except for + single 64-bit FPR or Zilsd moves on RV32. */ + if (get_attr_length (insn) > 4 + && known_gt (mode_size, 0U + UNITS_PER_WORD) + && (!known_eq (mode_size, 8U) + || riscv_split_64bit_move_p (dest, src))) + return false; + + bool isload_p = MEM_P (src); + if (isload_p) + { + if (riscv_regno (dest) == INVALID_REGNUM) + return false; + } + else + { + if (src != const0_rtx && riscv_regno (src) == INVALID_REGNUM) + return false; + info->type = SCHED_FUSION_ST; + } + + info->mode = GET_MODE (mem); + if (!riscv_fuse_extract_address (XEXP (mem, 0), &info->addr)) + return false; + + enum attr_type type = get_attr_type (insn); + if (type == (isload_p ? TYPE_LOAD : TYPE_STORE)) + info->fp_p = false; + else if (type == (isload_p ? TYPE_FPLOAD : TYPE_FPSTORE)) + info->fp_p = true; + else + return false; + + return true; +} + /* Check the common RTL for ZEXTW, ZEXTWS and ZEXTH fusion. */ static bool riscv_fuse_zext_common (rtx_insn *prev, rtx_insn *curr, - int shl_amount, bool zextws_p) + int shl_amount, bool zextws_p) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + if (!TARGET_64BIT) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (GET_CODE (SET_SRC (prev_set)) == ASHIFT - && GET_CODE (SET_SRC (curr_set)) == LSHIFTRT - && 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 (curr_set)) - && CONST_INT_P (XEXP (SET_SRC (prev_set), 1)) - && CONST_INT_P (XEXP (SET_SRC (curr_set), 1)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set, true)) + return false; + + if (riscv_set_is_slli_p (prev_set) + && riscv_set_is_srli_p (curr_set) + && GET_MODE (SET_SRC (prev_set)) == DImode + && GET_MODE (SET_SRC (curr_set)) == DImode && INTVAL (XEXP (SET_SRC (prev_set), 1)) == shl_amount && (zextws_p ? INTVAL (XEXP (SET_SRC (curr_set), 1)) < shl_amount @@ -178,6 +610,8 @@ riscv_fuse_zext_common (rtx_insn *prev, rtx_insn *curr, return false; } +/* Fusion recognizers. */ + /* Check for RISCV_FUSE_ZEXTW fusion. prev (slli) == (set (reg:DI rd1) (ashift:DI (reg:DI rs1) (const_int 32))) @@ -237,35 +671,22 @@ riscv_fuse_zexth (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_ldindexed (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set)) return false; - if (MEM_P (SET_SRC (curr_set)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && REG_P (XEXP (SET_SRC (curr_set), 0)) - && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set)) - && GET_CODE (SET_SRC (prev_set)) == PLUS - && REG_P (XEXP (SET_SRC (prev_set), 0)) - && REG_P (XEXP (SET_SRC (prev_set), 1))) - return true; + struct riscv_fusion_mem_info mem; - /* curr (lw) == (set (reg:DI rd2) - (any_extend:DI (mem:SUBX (reg:DI rd1)))). */ - if ((GET_CODE (SET_SRC (curr_set)) == SIGN_EXTEND - || (GET_CODE (SET_SRC (curr_set)) == ZERO_EXTEND)) - && MEM_P (XEXP (SET_SRC (curr_set), 0)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - == REGNO (SET_DEST (prev_set))) - && GET_CODE (SET_SRC (prev_set)) == PLUS - && REG_P (XEXP (SET_SRC (prev_set), 0)) - && REG_P (XEXP (SET_SRC (prev_set), 1))) + if (riscv_fuse_mem_p (curr, &mem) + && mem.type != SCHED_FUSION_ST + && !mem.fp_p + && mem.addr.type == ADDRESS_REG + && INTVAL (mem.addr.offset) == 0 + && riscv_set_is_add_p (prev_set) + && riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set))) return true; return false; @@ -299,72 +720,29 @@ riscv_fuse_ldindexed (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_expanded_ld (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set)) return false; - /* Match a load with displacement: - curr (load) == (set (reg rd2) - (mem (plus (reg rd1) (const_int offset)))) */ - if (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 - && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - == REGNO (SET_DEST (prev_set)))) - { - if (riscv_set_is_add_p (prev_set) - || riscv_set_is_addi_p (prev_set) - || riscv_set_is_shNadd_p (prev_set)) - return true; - } - - /* Match a load without displacement: - curr (load) == (set (reg rd2) (mem (reg rd1))). */ - if (MEM_P (SET_SRC (curr_set)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && REG_P (XEXP (SET_SRC (curr_set), 0)) - && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))) - { - if (riscv_set_is_addi_p (prev_set) - || riscv_set_is_shNadd_p (prev_set)) - return true; - } + struct riscv_fusion_mem_info mem; + if (!riscv_fuse_mem_p (curr, &mem) + || mem.type == SCHED_FUSION_ST + || mem.fp_p + || mem.addr.type != ADDRESS_REG + || !riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set))) + return false; - /* Match lw with displacement. */ - if ((GET_CODE (SET_SRC (curr_set)) == SIGN_EXTEND - || (GET_CODE (SET_SRC (curr_set)) == ZERO_EXTEND)) - && MEM_P (XEXP (SET_SRC (curr_set), 0)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && GET_CODE (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) == PLUS - && REG_P (XEXP (XEXP (XEXP (SET_SRC (curr_set), 0), 0), 0)) - && (REGNO (XEXP (XEXP (XEXP (SET_SRC (curr_set), 0), 0), 0)) - == REGNO (SET_DEST (prev_set)))) - { - if (riscv_set_is_adduw_p (prev_set) - || riscv_set_is_shNadduw_p (prev_set)) - return true; - } + bool displaced_p = INTVAL (mem.addr.offset) != 0; + if (mem.type == SCHED_FUSION_LD) + return ((displaced_p && riscv_set_is_add_p (prev_set)) + || riscv_set_is_addi_p (prev_set) + || riscv_set_is_shNadd_p (prev_set)); - /* Match lw without displacement. */ - if ((GET_CODE (SET_SRC (curr_set)) == SIGN_EXTEND - || (GET_CODE (SET_SRC (curr_set)) == ZERO_EXTEND)) - && MEM_P (XEXP (SET_SRC (curr_set), 0)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - == REGNO (SET_DEST (prev_set)))) - { - if (riscv_set_is_adduw_p (prev_set) - || riscv_set_is_shNadduw_p (prev_set)) - return true; - } - - return false; + return (riscv_set_is_adduw_p (prev_set) + || riscv_set_is_shNadduw_p (prev_set)); } /* Check for RISCV_FUSE_LDPREINCREMENT fusion. @@ -378,21 +756,24 @@ riscv_fuse_expanded_ld (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_ldpreincrement (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set)) return false; - if (MEM_P (SET_SRC (curr_set)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && REG_P (XEXP (SET_SRC (curr_set), 0)) - && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set)) - && GET_CODE (SET_SRC (prev_set)) == PLUS - && REG_P (XEXP (SET_SRC (prev_set), 0)) - && CONST_INT_P (XEXP (SET_SRC (prev_set), 1))) + struct riscv_fusion_mem_info mem; + + if (riscv_fuse_mem_p (curr, &mem) + && mem.type == SCHED_FUSION_LD + && !mem.fp_p + && mem.addr.type == ADDRESS_REG + && INTVAL (mem.addr.offset) == 0 + && riscv_set_is_addi_p (prev_set) + && riscv_fuse_same_reg_p (XEXP (SET_SRC (prev_set), 0), + SET_DEST (prev_set)) + && riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set))) return true; return false; @@ -403,33 +784,31 @@ riscv_fuse_ldpreincrement (rtx_insn *prev, rtx_insn *curr) (lui) == (set (reg rd1) (const_int imm20)) (lui) == (set (reg rd1) (high symbol1)) curr (one of the following): - (addi) == (set (reg rd2) - (plus (reg rd1) (const_int imm12))) + (addi/addiw) == (set (reg rd2) + (plus (reg rd1) (const_int imm12))) (addi) == (set (reg rd2) (lo_sum (reg rd1) symbol2)) + (self-mv) == (set (reg rd2) (reg rd1)) Constraints: rd1 == rd2 - rd1 != x0 imm20 != 0 for the constant form. */ static bool riscv_fuse_lui_addi (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set, true)) return false; - if ((GET_CODE (SET_SRC (curr_set)) == LO_SUM - || (GET_CODE (SET_SRC (curr_set)) == PLUS - && CONST_INT_P (XEXP (SET_SRC (curr_set), 1)) - && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (curr_set), 1))))) + if (riscv_insn_is_addi_type_p (curr) && (GET_CODE (SET_SRC (prev_set)) == HIGH || (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)))))) return true; @@ -443,28 +822,24 @@ riscv_fuse_lui_addi (rtx_insn *prev, rtx_insn *curr) (plus (reg rd1) (const_int imm12))) (addi) == (set (reg rd2) (lo_sum (reg rd1) symbol)) + (self-mv) == (set (reg rd2) (reg rd1)) Constraints: - rd1 == rd2 - rd1 != x0. */ + rd1 == rd2. */ static bool riscv_fuse_auipc_addi (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set, true)) return false; - if (GET_CODE (SET_SRC (prev_set)) == UNSPEC - && XINT (SET_SRC (prev_set), 1) == UNSPEC_AUIPC - && (GET_CODE (SET_SRC (curr_set)) == LO_SUM - || (GET_CODE (SET_SRC (curr_set)) == PLUS - && CONST_INT_P (XEXP (SET_SRC (curr_set), 1)) - && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (curr_set), 1)))))) + if (riscv_insn_is_addi_type_p (curr, false) + && GET_CODE (SET_SRC (prev_set)) == UNSPEC + && XINT (SET_SRC (prev_set), 1) == UNSPEC_AUIPC) return true; return false; @@ -489,12 +864,18 @@ riscv_fuse_auipc_addi (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) + return false; + + if (!riscv_fuse_same_dest_p (prev_set, curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + struct riscv_fusion_mem_info mem; + if (!riscv_fuse_mem_p (curr, &mem) + || mem.type == SCHED_FUSION_ST + || mem.fp_p + || !riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set))) return false; /* A LUI_OPERAND accepts (const_int 0), but we won't emit that as LUI. @@ -502,32 +883,13 @@ riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr) 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))) - && 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 - && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - == REGNO (SET_DEST (prev_set)))) - return true; - - if (GET_CODE (SET_SRC (prev_set)) == HIGH - && MEM_P (SET_SRC (curr_set)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == LO_SUM - && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) - == REGNO (SET_DEST (prev_set)))) + && mem.type == SCHED_FUSION_LD + && mem.addr.type == ADDRESS_REG + && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS) return true; if (GET_CODE (SET_SRC (prev_set)) == HIGH - && (GET_CODE (SET_SRC (curr_set)) == SIGN_EXTEND - || GET_CODE (SET_SRC (curr_set)) == ZERO_EXTEND) - && MEM_P (XEXP (SET_SRC (curr_set), 0)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && (GET_CODE (XEXP (XEXP (SET_SRC (curr_set), 0), 0)) == LO_SUM - && REG_P (XEXP (XEXP (XEXP (SET_SRC (curr_set), 0), 0), 0)) - && (REGNO (XEXP (XEXP (XEXP (SET_SRC (curr_set), 0), 0), 0)) - == REGNO (SET_DEST (prev_set))))) + && mem.addr.type == ADDRESS_LO_SUM) return true; return false; @@ -543,19 +905,23 @@ riscv_fuse_lui_ld (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_auipc_ld (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set)) return false; + struct riscv_fusion_mem_info mem; + if (GET_CODE (SET_SRC (prev_set)) == UNSPEC && XINT (SET_SRC (prev_set), 1) == UNSPEC_AUIPC - && 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) + && riscv_fuse_mem_p (curr, &mem) + && mem.type == SCHED_FUSION_LD + && !mem.fp_p + && mem.addr.type == ADDRESS_REG + && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS + && riscv_fuse_same_reg_p (mem.addr.reg, SET_DEST (prev_set))) return true; return false; @@ -573,50 +939,34 @@ riscv_fuse_auipc_ld (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + if (!riscv_fuse_sets_p (prev, curr)) return false; - if (MEM_P (SET_DEST (prev_set)) - && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set))) - && MEM_P (SET_DEST (curr_set)) - /* Stores must have the same width. */ - && GET_MODE (SET_DEST (curr_set)) == GET_MODE (SET_DEST (prev_set))) - { - rtx base_prev, base_curr, offset_prev, offset_curr; - unsigned mode_size; - - extract_base_offset_in_addr (SET_DEST (prev_set), - &base_prev, &offset_prev); - extract_base_offset_in_addr (SET_DEST (curr_set), - &base_curr, &offset_curr); - - /* Proceed only if we find both bases, both bases - are registers and bases are the same register. */ - if (base_prev != NULL_RTX && base_curr != NULL_RTX - && REG_P (base_prev) && REG_P (base_curr) - && REGNO (base_prev) == REGNO (base_curr)) - { - machine_mode mode = GET_MODE (SET_DEST (curr_set)); - mode_size = estimated_poly_value (GET_MODE_SIZE (mode)); - - HOST_WIDE_INT offset_prev_int = INTVAL (offset_prev); - HOST_WIDE_INT offset_curr_int = INTVAL (offset_curr); - - /* Get the smaller offset into OFFSET_PREV_INT. */ - if (offset_prev_int > offset_curr_int) - std::swap (offset_prev_int, offset_curr_int); - - /* We've normalized, so we need to check that the lower - address is aligned to 2X the size of the object. The - higher address must be the lower address plus the - size of the object. */ - if (((offset_prev_int % (2 * mode_size)) == 0) - && offset_prev_int + mode_size == offset_curr_int) - return true; - } - } + struct riscv_fusion_mem_info prev_mem, curr_mem; + + if (!riscv_fuse_mem_p (prev, &prev_mem) + || !riscv_fuse_mem_p (curr, &curr_mem) + || prev_mem.type != SCHED_FUSION_ST + || curr_mem.type != SCHED_FUSION_ST + || prev_mem.fp_p + || curr_mem.fp_p + || !SCALAR_INT_MODE_P (prev_mem.mode) + || prev_mem.mode != curr_mem.mode + || prev_mem.addr.type != ADDRESS_REG + || curr_mem.addr.type != ADDRESS_REG + || !riscv_fuse_same_reg_p (prev_mem.addr.reg, curr_mem.addr.reg)) + return false; + + unsigned int mode_size + = estimated_poly_value (GET_MODE_SIZE (curr_mem.mode)); + HOST_WIDE_INT prev_offset = INTVAL (prev_mem.addr.offset); + HOST_WIDE_INT curr_offset = INTVAL (curr_mem.addr.offset); + if (prev_offset > curr_offset) + std::swap (prev_offset, curr_offset); + + if (prev_offset % (2 * mode_size) == 0 + && prev_offset + mode_size == curr_offset) + return true; return false; } @@ -638,22 +988,16 @@ riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set, true)) return false; - if (GET_CODE (SET_SRC (prev_set)) == ASHIFT - && (GET_CODE (SET_SRC (curr_set)) == LSHIFTRT - || GET_CODE (SET_SRC (curr_set)) == ASHIFTRT) - && REG_P (SET_DEST (prev_set)) - && REG_P (SET_DEST (curr_set)) - && 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))) + if (riscv_set_is_slli_p (prev_set) + && (riscv_set_is_srli_p (curr_set) + || riscv_set_is_srai_p (curr_set))) return true; return false; @@ -684,33 +1028,24 @@ riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr) static bool riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr) { - rtx prev_set = single_set (prev); - rtx curr_set = single_set (curr); - if (!prev_set || !curr_set || any_condjump_p (curr)) + rtx prev_set, curr_set; + if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set)) return false; - if (!riscv_fusion_same_dest_p (prev_set, curr_set)) + if (!riscv_fuse_same_dest_p (prev_set, curr_set, true)) return false; /* orc.b + not. */ if (GET_CODE (SET_SRC (prev_set)) == UNSPEC && GET_CODE (SET_SRC (curr_set)) == NOT - && XINT (SET_SRC (prev_set), 1) == UNSPEC_ORC_B - && 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))) + && XINT (SET_SRC (prev_set), 1) == UNSPEC_ORC_B) return true; /* ctz + andi. */ if (GET_CODE (SET_SRC (prev_set)) == CTZ && GET_CODE (SET_SRC (curr_set)) == AND && CONST_INT_P (XEXP (SET_SRC (curr_set), 1)) - && INTVAL (XEXP (SET_SRC (curr_set), 1)) == 63 - && 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))) + && INTVAL (XEXP (SET_SRC (curr_set), 1)) == 63) return true; /* sub + smax (abs pattern). */ @@ -718,27 +1053,15 @@ riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr) && (XEXP (SET_SRC (prev_set), 0) == CONST0_RTX (GET_MODE (SET_SRC (prev_set)))) && GET_CODE (SET_SRC (curr_set)) == SMAX - && 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)) - && REG_P (XEXP (SET_SRC (prev_set), 1)) - && REG_P (XEXP (SET_SRC (curr_set), 1)) - && (REGNO (XEXP (SET_SRC (prev_set), 1)) - == REGNO (XEXP (SET_SRC (curr_set), 1)))) + && riscv_fuse_same_reg_p (XEXP (SET_SRC (prev_set), 1), + XEXP (SET_SRC (curr_set), 1))) return true; /* neg + smax (abs pattern). */ if (GET_CODE (SET_SRC (prev_set)) == NEG && GET_CODE (SET_SRC (curr_set)) == SMAX - && 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)) - && REG_P (XEXP (SET_SRC (prev_set), 0)) - && REG_P (XEXP (SET_SRC (curr_set), 1)) - && (REGNO (XEXP (SET_SRC (prev_set), 0)) - == REGNO (XEXP (SET_SRC (curr_set), 1)))) + && riscv_fuse_same_reg_p (XEXP (SET_SRC (prev_set), 0), + XEXP (SET_SRC (curr_set), 1))) return true; return false; diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-prera-rtl.c b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-prera-rtl.c new file mode 100644 index 00000000000..187be1c24f7 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-prera-rtl.c @@ -0,0 +1,63 @@ +/* Check preallocation overmatching without dropping source dependencies. */ +/* { dg-do compile { target { rv64 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */ +/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -fno-dce -mcmodel=medany -mexplicit-relocs -mtune=sifive-p600-series -fdump-rtl-sched1-details -fdump-rtl-sched2-details" } */ +/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LUI_ADDI" 1 "sched1" } } */ +/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_LUI_ADDI" "sched2" } } */ + +long __RTL (startwith ("sched1")) +test_lui_addi_prera (void) +{ +(function "test_lui_addi_prera" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK) + (cnote 2 NOTE_INSN_FUNCTION_BEG) + (cinsn 3 (set (reg:DI a1) + (const_int 305418240))) + (cinsn 4 (set (reg:DI a0) + (plus:DI (reg:DI a1) + (const_int 1656)))) + (cinsn 5 (use (reg:DI a0))) + (cinsn 6 (use (reg:DI a1))) + (cjump_insn 7 (simple_return)) + (edge-to exit) + ) ;; block 2 + (cbarrier 8) + ) ;; insn-chain + (crtl + (return_rtx + (reg/i:DI a0) + ) ;; return_rtx + ) ;; crtl +) ;; function "test_lui_addi_prera" +} + +long __RTL (startwith ("sched1")) +test_lui_addi_source_mismatch (void) +{ +(function "test_lui_addi_source_mismatch" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK) + (cnote 2 NOTE_INSN_FUNCTION_BEG) + (cinsn 3 (set (reg:DI a0) + (const_int 305418240))) + (cinsn 4 (set (reg:DI a0) + (plus:DI (reg:DI a1) + (const_int 1656)))) + (cinsn 5 (use (reg:DI a0))) + (cjump_insn 6 (simple_return)) + (edge-to exit) + ) ;; block 2 + (cbarrier 7) + ) ;; insn-chain + (crtl + (return_rtx + (reg/i:DI a0) + ) ;; return_rtx + ) ;; crtl +) ;; function "test_lui_addi_source_mismatch" +} -- 2.52.0