Message ID | da977e26-f462-c0ec-e054-2cb415ad2493@linux.ibm.com |
---|---|
State | New |
Headers | show |
Series | None | expand |
All: Ok for trunk. Please review. Thanks & Regards Ajit On 07/06/23 3:55 pm, Ajit Agarwal via Gcc-patches wrote: > Hello All: > > This patch provide functionality to improve ree pass for rs6000 target. > Eliminated sign_extend/zero_extend/AND with varying constants. > > Bootstrapped and regtested on powerpc64-linux-gnu. > > Thanks & Regards > Ajit > > ree: Improve ree pass for rs6000 target > > For rs6000 target we see redundant zero and sign extension and done to improve > ree pass to eliminate such redundant zero and sign extension. Support of > zero_extend/sign_extend/AND. Also support of AND with extension with different > constants other than 1. > > 2023-06-07 Ajit Kumar Agarwal <aagarwa1@linux.ibm.com> > > gcc/ChangeLog: > > * ree.cc (eliminate_across_bbs_p): Add checks to enable extension > elimination across and within basic blocks. > (def_arith_p): New function to check definition has arithmetic > operation. > (combine_set_extension): Modification to incorporate AND > and current zero_extend and sign_extend instruction. > (merge_def_and_ext): Add calls to eliminate_across_bbs_p and > zero_extend sign_extend and AND instruction. > (rtx_is_zext_p): New function. > (feasible_cfg): New function. > * rtl.h (reg_used_set_between_p): Add prototype. > * rtlanal.cc (reg_used_set_between_p): New function. > > gcc/testsuite/ChangeLog: > > * g++.target/powerpc/zext-elim.C: New testcase. > * g++.target/powerpc/zext-elim-1.C: New testcase. > * g++.target/powerpc/zext-elim-2.C: New testcase. > * g++.target/powerpc/sext-elim.C: New testcase. > --- > gcc/ree.cc | 476 ++++++++++++++++-- > gcc/rtl.h | 1 + > gcc/rtlanal.cc | 15 + > gcc/testsuite/g++.target/powerpc/sext-elim.C | 18 + > .../g++.target/powerpc/zext-elim-1.C | 19 + > .../g++.target/powerpc/zext-elim-2.C | 11 + > gcc/testsuite/g++.target/powerpc/zext-elim.C | 30 ++ > 7 files changed, 524 insertions(+), 46 deletions(-) > create mode 100644 gcc/testsuite/g++.target/powerpc/sext-elim.C > create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim-1.C > create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim-2.C > create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim.C > > diff --git a/gcc/ree.cc b/gcc/ree.cc > index fc04249fa84..dc6da21ec16 100644 > --- a/gcc/ree.cc > +++ b/gcc/ree.cc > @@ -253,6 +253,66 @@ struct ext_cand > > static int max_insn_uid; > > +/* Return TRUE if OP can be considered a zero extension from one or > + more sub-word modes to larger modes up to a full word. > + > + For example (and:DI (reg) (const_int X)) > + > + Depending on the value of X could be considered a zero extension > + from QI, HI and SI to larger modes up to DImode. */ > + > +static bool > +rtx_is_zext_p (rtx insn) > +{ > + if (GET_CODE (insn) == AND) > + { > + rtx set = XEXP (insn, 0); > + if (REG_P (set)) > + { > + rtx src = XEXP (insn, 1); > + > + if (CONST_INT_P (src) > + && IN_RANGE (exact_log2 (UINTVAL (src)), 0, 7)) > + return true; > + } > + else > + return false; > + } > + > + return false; > +} > +/* Return TRUE if OP can be considered a zero extension from one or > + more sub-word modes to larger modes up to a full word. > + > + For example (and:DI (reg) (const_int X)) > + > + Depending on the value of X could be considered a zero extension > + from QI, HI and SI to larger modes up to DImode. */ > + > +static bool > +rtx_is_zext_p (rtx_insn *insn) > +{ > + rtx body = single_set (insn); > + > + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == AND) > + { > + rtx set = XEXP (SET_SRC (body), 0); > + > + if (REG_P (set) && GET_MODE (SET_DEST (body)) == GET_MODE (set)) > + { > + rtx src = XEXP (SET_SRC (body), 1); > + > + if (CONST_INT_P (src) > + && IN_RANGE (exact_log2 (UINTVAL (src)), 0, 7)) > + return true; > + } > + else > + return false; > + } > + > + return false; > +} > + > /* Update or remove REG_EQUAL or REG_EQUIV notes for INSN. */ > > static bool > @@ -319,7 +379,7 @@ combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) > { > rtx orig_src = SET_SRC (*orig_set); > machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); > - rtx new_set; > + rtx new_set = NULL_RTX; > rtx cand_pat = single_set (cand->insn); > > /* If the extension's source/destination registers are not the same > @@ -359,27 +419,41 @@ combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) > else if (GET_CODE (orig_src) == cand->code) > { > /* Here is a sequence of two extensions. Try to merge them. */ > - rtx temp_extension > - = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); > + rtx temp_extension = NULL_RTX; > + if (GET_CODE (SET_SRC (cand_pat)) == AND) > + temp_extension > + = gen_rtx_AND (cand->mode, XEXP (orig_src, 0), XEXP (orig_src, 1)); > + else > + temp_extension > + = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); > rtx simplified_temp_extension = simplify_rtx (temp_extension); > if (simplified_temp_extension) > temp_extension = simplified_temp_extension; > + > new_set = gen_rtx_SET (new_reg, temp_extension); > } > else if (GET_CODE (orig_src) == IF_THEN_ELSE) > { > /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, > - in general, IF_THEN_ELSE should not be combined. */ > - return false; > + in general, IF_THEN_ELSE should not be combined. Relaxed > + cases with IF_THEN_ELSE across basic blocls */ > + return true; > } > else > { > /* This is the normal case. */ > - rtx temp_extension > + rtx temp_extension = NULL_RTX; > + > + if (GET_CODE (SET_SRC (cand_pat)) == AND) > + temp_extension > + = gen_rtx_AND (cand->mode, orig_src, XEXP (SET_SRC (cand_pat), 1)); > + else > + temp_extension > = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); > rtx simplified_temp_extension = simplify_rtx (temp_extension); > if (simplified_temp_extension) > temp_extension = simplified_temp_extension; > + > new_set = gen_rtx_SET (new_reg, temp_extension); > } > > @@ -468,12 +542,13 @@ get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) > FOR_EACH_INSN_USE (use, insn) > { > if (GET_CODE (DF_REF_REG (use)) == SUBREG) > - return NULL; > + return NULL; > if (REGNO (DF_REF_REG (use)) == REGNO (reg)) > break; > } > > - gcc_assert (use != NULL); > + if (use == NULL) > + return NULL; > > ref_chain = DF_REF_CHAIN (use); > > @@ -481,9 +556,9 @@ get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) > { > /* Problem getting some definition for this instruction. */ > if (ref_link->ref == NULL) > - return NULL; > + return NULL; > if (DF_REF_INSN_INFO (ref_link->ref) == NULL) > - return NULL; > + return NULL; > /* As global regs are assumed to be defined at each function call > dataflow can report a call_insn as being a definition of REG. > But we can't do anything with that in this pass so proceed only > @@ -698,6 +773,258 @@ get_sub_rtx (rtx_insn *def_insn) > return sub_rtx; > } > > +/* Return TRUE if reaching definition of def_insn source operand > + has has arithmetic peration like ASHIFT and LSHIFTRT. If TRUE > + don't eliminate sign extension */ > + > +static bool > +def_arith_p (rtx_insn *insn, rtx orig_src) > +{ > + if (!REG_P (orig_src)) > + return true; > + > + vec<rtx_insn *> *dest = XCNEWVEC (vec<rtx_insn *>, 4); > + if (!get_defs (insn, orig_src, dest)) > + return false; > + > + int i; > + rtx_insn *def_insn; > + bool has_arith = false; > + > + FOR_EACH_VEC_ELT (*dest, i, def_insn) > + { > + rtx def_set = single_set (def_insn); > + > + if (!def_set) > + { > + has_arith = true; > + break; > + } > + > + if (DEBUG_INSN_P (def_insn)) > + continue; > + > + /* Return True for following rtl insn. > + set (reg x), (ashift ( ...) > + set (reg x), (lshiftrt (....) */ > + > + if ((GET_CODE (PATTERN (def_insn)) == SET > + && (GET_CODE (SET_SRC (def_set)) == ASHIFT > + || GET_CODE (SET_SRC (def_set)) == LSHIFTRT))) > + { > + has_arith = true; > + break; > + } > + > + /* Return TRUE for following rtl insn. > + set (reg x) , (plus(ashift ( ....) > + set (reg x), (plus(lshiftrt (....) */ > + > + if (GET_CODE (PATTERN (def_insn)) == SET > + && (GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_BIN_ARITH > + || GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_COMM_ARITH)) > + { > + rtx src = XEXP (SET_SRC (def_set),0); > + > + if (GET_CODE (src) == LSHIFTRT > + || GET_CODE (src) == ASHIFT) > + { > + has_arith = true; > + break; > + } > + } > + } > + XDELETEVEC (dest); > + return has_arith; > +} > + > +/* Return TRUE if the cfg has following properties. > + bb1 > + |\ > + | \ > + | bb2 > + | / > + bb3 > + > + whereas bb1 has IF_THEN_ELSE and bb2 has the definition and bb3 has > + zero/sign/AND extensions. */ > + > +static bool > +feasible_cfg (ext_cand *cand, rtx_insn *def_insn) > +{ > + basic_block bb = BLOCK_FOR_INSN (cand->insn); > + edge fallthru_edge; > + edge e; > + edge_iterator ei; > + > + FOR_EACH_EDGE (e, ei, bb->preds) > + { > + rtx_insn *insn = BB_END (e->src) ? PREV_INSN (BB_END (e->src)) : NULL; > + > + if (insn == NULL) > + continue; > + > + if (DEBUG_INSN_P (insn)) > + continue; > + > + rtx set = single_set (insn); > + > + /* Block has IF_THEN_ELSE */ > + if (insn && set > + && GET_CODE (set) == SET && SET_SRC (set) > + && GET_CODE (SET_SRC (set)) == IF_THEN_ELSE) > + { > + if (e->dest == bb) > + { > + basic_block jump_block = e->dest; > + if (jump_block != bb) > + return false; > + } > + else > + { > + /* def_insn block has single successor and fall through > + edge target are the block for cand insn. */ > + if (single_succ_p (e->dest)) > + { > + fallthru_edge = single_succ_edge (e->dest); > + if (BB_END (fallthru_edge->dest) > + && bb != fallthru_edge->dest) > + return false; > + } > + } > + } > + } > + > + /* def_insn block has single successor and fall through > + edge target are the block for cand insn. */ > + if (single_succ_p (BLOCK_FOR_INSN (def_insn))) > + { > + fallthru_edge = single_succ_edge (BLOCK_FOR_INSN (def_insn)); > + if (BB_END (fallthru_edge->dest) > + && bb != fallthru_edge->dest) > + return false; > + } > + else > + return false; > + > + return true; > +} > + > +/* Return TRUE if the candidate extension INSN and def_insn are > + feasible for extension elimination. > + > + Things to consider: > + > + cfg properties are feasible for extension elimination. > + > + sign_extend with def insn as PLUS and the reaching definition > + of def_insn are not ASHIFT and LSHIFTRT. > + > + zero_extend with def insn as XOR/IOR and the reachin definition > + of def_insn are not ASHIFT and LSHIFTRT. > + > + The destination register of the extension insn must not be > + used or set between the def_insn and cand->insn exclusive. > + > + AND with zero extension properties has USE and the register > + of cand insn are same as register of USE operand. */ > + > +static bool > +eliminate_across_bbs_p (ext_cand *cand, rtx_insn *def_insn) > +{ > + basic_block bb = BLOCK_FOR_INSN (cand->insn); > + > + if (!feasible_cfg (cand, def_insn)) > + return false; > + > + rtx cand_set = single_set(cand->insn); > + /* The destination register of the extension insn must not be > + used or set between the def_insn and cand->insn exclusive. */ > + if (INSN_CHAIN_CODE_P (GET_CODE (def_insn)) > + && INSN_CHAIN_CODE_P (cand->code)) > + if ((cand->code == ZERO_EXTEND) > + && REG_P (SET_DEST (cand_set)) && NEXT_INSN (def_insn) > + && reg_used_set_between_p(SET_DEST (cand_set), def_insn, cand->insn)) > + return false; > + > + if (cand->code == ZERO_EXTEND > + && (bb != BLOCK_FOR_INSN (def_insn) > + || DF_INSN_LUID (def_insn) > DF_INSN_LUID (cand->insn))) > + return false; > + > + if (rtx_is_zext_p (cand->insn)) > + { > + if (GET_CODE (PATTERN (BB_END (bb))) != USE) > + return false; > + > + if (REGNO (XEXP (PATTERN (BB_END (bb)), 0)) != REGNO (SET_DEST (cand->expr))) > + return false; > + } > + > + rtx set = single_set (def_insn); > + > + if (!set) > + return false; > + > + if (cand->code == SIGN_EXTEND > + && GET_CODE (set) == SET) > + { > + rtx orig_src = SET_SRC (set); > + machine_mode ext_src_mode; > + > + ext_src_mode = GET_MODE (XEXP (SET_SRC (cand->expr), 0)); > + > + if (GET_MODE (SET_DEST (set)) != ext_src_mode) > + return false; > + > + if (GET_CODE (orig_src) != PLUS) > + return false; > + > + if (!REG_P (XEXP (orig_src, 0))) > + return false; > + > + if (!REG_P (XEXP (orig_src,1))) > + return false; > + > + if (GET_CODE (orig_src) == PLUS) > + { > + bool def_src1 > + = def_arith_p (def_insn, > + XEXP (SET_SRC (set), 0)); > + bool def_src2 > + = def_arith_p (def_insn, > + XEXP (SET_SRC (set), 1)); > + > + if (def_src1 || def_src2) > + return false; > + } > + } > + > + if (cand->code == ZERO_EXTEND > + && GET_CODE (set) == SET) > + { > + if (GET_CODE (SET_SRC (set)) != XOR > + && GET_CODE (SET_SRC (set)) != IOR) > + return false; > + > + if (GET_CODE (SET_SRC (set)) == XOR > + || GET_CODE (SET_SRC (set)) == IOR) > + { > + bool def_src1 > + = def_arith_p (def_insn, > + XEXP (SET_SRC (set), 0)); > + bool def_src2 > + = def_arith_p (def_insn, > + XEXP (SET_SRC (set), 1)); > + > + if (def_src1 || def_src2) > + return false; > + } > + } > + > + return true; > +} > + > /* Merge the DEF_INSN with an extension. Calls combine_set_extension > on the SET pattern. */ > > @@ -713,12 +1040,32 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) > if (sub_rtx == NULL) > return false; > > - if (GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode > - || ((state->modified[INSN_UID (def_insn)].kind > - == (cand->code == ZERO_EXTEND > + bool copy_needed > + = (REGNO (SET_DEST (cand->expr)) != REGNO (XEXP (SET_SRC (cand->expr), 0))); > + > + bool feasible = eliminate_across_bbs_p (cand, def_insn); > + > + if (!feasible) return false; > + > + /* Combine zero_extend/sign_extend/AND and if sign_extend and > + mode of DEST and SRC are different. */ > + > + bool is_zext = rtx_is_zext_p (cand->insn) > + || cand->code == ZERO_EXTEND > + || cand->code == SIGN_EXTEND; > + > + bool do_elimination = !copy_needed > + && is_zext > + && (cand->code == SIGN_EXTEND > + || GET_MODE (SET_DEST (*sub_rtx)) != ext_src_mode); > + > + if (((do_elimination > + && state->modified[INSN_UID (def_insn)].kind == EXT_MODIFIED_NONE)) > + || ((state->modified[INSN_UID (def_insn)].kind > + == (cand->code == ZERO_EXTEND > ? EXT_MODIFIED_ZEXT : EXT_MODIFIED_SEXT)) > - && state->modified[INSN_UID (def_insn)].mode > - == ext_src_mode)) > + && state->modified[INSN_UID (def_insn)].mode > + == ext_src_mode)) > { > if (GET_MODE_UNIT_SIZE (GET_MODE (SET_DEST (*sub_rtx))) > >= GET_MODE_UNIT_SIZE (cand->mode)) > @@ -734,7 +1081,6 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) > return true; > } > } > - > return false; > } > > @@ -744,7 +1090,9 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) > static inline rtx > get_extended_src_reg (rtx src) > { > - while (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND) > + while (GET_CODE (src) == SIGN_EXTEND > + || GET_CODE (src) == ZERO_EXTEND > + || rtx_is_zext_p (src)) > src = XEXP (src, 0); > gcc_assert (REG_P (src)); > return src; > @@ -882,8 +1230,7 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) > > /* The destination register of the extension insn must not be > used or set between the def_insn and cand->insn exclusive. */ > - if (reg_used_between_p (SET_DEST (set), def_insn, cand->insn) > - || reg_set_between_p (SET_DEST (set), def_insn, cand->insn)) > + if (reg_used_set_between_p (SET_DEST (set), def_insn, cand->insn)) > return false; > > /* We must be able to copy between the two registers. Generate, > @@ -975,10 +1322,8 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) > used or set between the def_insn2 and def_insn exclusive. > Likewise for the other reg, i.e. check both reg1 and reg2 > in the above comment. */ > - if (reg_used_between_p (SET_DEST (set), def_insn2, def_insn) > - || reg_set_between_p (SET_DEST (set), def_insn2, def_insn) > - || reg_used_between_p (src_reg, def_insn2, def_insn) > - || reg_set_between_p (src_reg, def_insn2, def_insn)) > + if (reg_used_set_between_p (SET_DEST (set), def_insn2, def_insn) > + || reg_used_set_between_p (src_reg, def_insn2, def_insn)) > break; > > state->defs_list[0] = def_insn2; > @@ -1004,15 +1349,17 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) > cand->mode = mode; > } > > - merge_successful = true; > - > + merge_successful = false; > /* Go through the defs vector and try to merge all the definitions > in this vector. */ > state->modified_list.truncate (0); > FOR_EACH_VEC_ELT (state->defs_list, defs_ix, def_insn) > { > if (merge_def_and_ext (cand, def_insn, state)) > - state->modified_list.safe_push (def_insn); > + { > + merge_successful = true; > + state->modified_list.safe_push (def_insn); > + } > else > { > merge_successful = false; > @@ -1045,34 +1392,71 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) > definitions could be merged. */ > if (apply_change_group ()) > { > - if (dump_file) > - fprintf (dump_file, "All merges were successful.\n"); > + if (state->modified_list.length() == 0) > + return false; > + > + rtx_insn *insn = state->modified_list[0]; > + > + if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) > + && GET_CODE (PATTERN (insn)) == SET > + && GET_CODE (SET_SRC (PATTERN (insn))) != XOR > + && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS > + && GET_CODE (SET_SRC (PATTERN (insn))) != IOR) > + return false; > + > + if (dump_file) > + fprintf (dump_file, "All merges were successful.\n"); > > FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) > { > ext_modified *modified = &state->modified[INSN_UID (def_insn)]; > if (modified->kind == EXT_MODIFIED_NONE) > modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT > - : EXT_MODIFIED_SEXT); > + : EXT_MODIFIED_SEXT); > > if (copy_needed) > modified->do_not_reextend = 1; > } > return true; > } > - else > - { > - /* Changes need not be cancelled explicitly as apply_change_group > - does it. Print list of definitions in the dump_file for debug > - purposes. This extension cannot be deleted. */ > - if (dump_file) > - { > - fprintf (dump_file, > - "Merge cancelled, non-mergeable definitions:\n"); > - FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) > - print_rtl_single (dump_file, def_insn); > - } > - } > + else > + { > + if (state->modified_list.length() == 0) > + return false; > + > + rtx_insn *insn = state->modified_list[0]; > + > + if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) > + && GET_CODE (PATTERN (insn)) == SET > + && GET_CODE (SET_SRC (PATTERN (insn))) != XOR > + && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS > + && GET_CODE (SET_SRC (PATTERN (insn))) != IOR) > + return false; > + > + if (cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) > + { > + FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) > + { > + ext_modified *modified = &state->modified[INSN_UID (def_insn)]; > + if (modified->kind == EXT_MODIFIED_NONE) > + modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT > + : EXT_MODIFIED_SEXT); > + > + modified->do_not_reextend = 1; > + } > + return true; > + } > + /* Changes need not be cancelled explicitly as apply_change_group > + does it. Print list of definitions in the dump_file for debug > + purposes. This extension cannot be deleted. */ > + if (dump_file) > + { > + fprintf (dump_file, > + "Merge cancelled, non-mergeable definitions:\n"); > + FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) > + print_rtl_single (dump_file, def_insn); > + } > + } > } > else > { > @@ -1106,7 +1490,7 @@ add_removable_extension (const_rtx expr, rtx_insn *insn, > mode = GET_MODE (dest); > > if (REG_P (dest) > - && (code == SIGN_EXTEND || code == ZERO_EXTEND) > + && (code == SIGN_EXTEND || code == ZERO_EXTEND || rtx_is_zext_p (src)) > && REG_P (XEXP (src, 0))) > { > rtx reg = XEXP (src, 0); > @@ -1125,7 +1509,7 @@ add_removable_extension (const_rtx expr, rtx_insn *insn, > fprintf (dump_file, "Cannot eliminate extension:\n"); > print_rtl_single (dump_file, insn); > fprintf (dump_file, " because it can operate on uninitialized" > - " data\n"); > + " data\n"); > } > return; > } > @@ -1320,8 +1704,8 @@ find_and_remove_re (void) > if (REG_P (XEXP (SET_SRC (set), 0)) > && (REGNO (SET_DEST (set)) != REGNO (XEXP (SET_SRC (set), 0)))) > { > - reinsn_copy_list.safe_push (curr_cand->insn); > - reinsn_copy_list.safe_push (state.defs_list[0]); > + reinsn_copy_list.safe_push (curr_cand->insn); > + reinsn_copy_list.safe_push (state.defs_list[0]); > } > reinsn_del_list.safe_push (curr_cand->insn); > state.modified[INSN_UID (curr_cand->insn)].deleted = 1; > diff --git a/gcc/rtl.h b/gcc/rtl.h > index 988691f5710..9278ec43d69 100644 > --- a/gcc/rtl.h > +++ b/gcc/rtl.h > @@ -3631,6 +3631,7 @@ extern int count_occurrences (const_rtx, const_rtx, int); > extern bool reg_referenced_p (const_rtx, const_rtx); > extern bool reg_used_between_p (const_rtx, const rtx_insn *, const rtx_insn *); > extern bool reg_set_between_p (const_rtx, const rtx_insn *, const rtx_insn *); > +extern bool reg_used_set_between_p (rtx, rtx_insn *, rtx_insn *); > extern int commutative_operand_precedence (rtx); > extern bool swap_commutative_operands_p (rtx, rtx); > extern bool modified_between_p (const_rtx, const rtx_insn *, const rtx_insn *); > diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc > index 31707f3b90a..fa0e8741416 100644 > --- a/gcc/rtlanal.cc > +++ b/gcc/rtlanal.cc > @@ -1134,6 +1134,21 @@ no_labels_between_p (const rtx_insn *beg, const rtx_insn *end) > return true; > } > > +/* The register reg of the extension to_insn must not be > + used or set between the from_insn and to_insn exclusive. */ > + > +bool > +reg_used_set_between_p (rtx reg, > + rtx_insn *from_insn, > + rtx_insn *to_insn) > +{ > + if (reg_used_between_p (reg, from_insn, to_insn) > + || reg_set_between_p (reg, from_insn, to_insn)) > + return true; > + > + return false; > +} > + > /* Return true if register REG is used in an insn between > FROM_INSN and TO_INSN (exclusive of those two). */ > > diff --git a/gcc/testsuite/g++.target/powerpc/sext-elim.C b/gcc/testsuite/g++.target/powerpc/sext-elim.C > new file mode 100644 > index 00000000000..431696cf11e > --- /dev/null > +++ b/gcc/testsuite/g++.target/powerpc/sext-elim.C > @@ -0,0 +1,18 @@ > +/* { dg-do compile { target { powerpc*-*-* } } } */ > +/* { dg-require-effective-target lp64 } */ > +/* { dg-require-effective-target powerpc_p9vector_ok } */ > +/* { dg-options "-mcpu=power9 -O2 -free" } */ > + > +unsigned long c2l(unsigned char* p) > +{ > + unsigned long res = *p + *(p+1); > + return res; > +} > + > +long c2sl(signed char* p) > +{ > + long res = *p + *(p+1); > + return res; > +} > + > +/* { dg-final { scan-assembler-not "extsw" } } */ > diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-1.C b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C > new file mode 100644 > index 00000000000..bc6cc0eb3ca > --- /dev/null > +++ b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target { powerpc*-*-* } } } */ > +/* { dg-require-effective-target lp64 } */ > +/* { dg-require-effective-target powerpc_p9vector_ok } */ > +/* { dg-options "-mcpu=power9 -O2 -free" } */ > + > +extern unsigned char magic1[256]; > + > +unsigned int hash(const unsigned char inp[4]) > +{ > + const unsigned long long INIT = 0x1ULL; > + unsigned long long h1 = INIT; > + h1 = magic1[((unsigned long long)inp[0]) ^ h1]; > + h1 = magic1[((unsigned long long)inp[1]) ^ h1]; > + h1 = magic1[((unsigned long long)inp[2]) ^ h1]; > + h1 = magic1[((unsigned long long)inp[3]) ^ h1]; > + return h1; > +} > + > +/* { dg-final { scan-assembler-not "rlwinm" } } */ > diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-2.C b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C > new file mode 100644 > index 00000000000..4e72925104f > --- /dev/null > +++ b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C > @@ -0,0 +1,11 @@ > +/* { dg-do compile { target { powerpc*-*-* } } } */ > +/* { dg-require-effective-target lp64 } */ > +/* { dg-require-effective-target powerpc_p9vector_ok } */ > +/* { dg-options "-mcpu=power9 -O2 -free" } */ > + > +unsigned char g(unsigned char t[], unsigned char v) > +{ > + return (t[v & 0x7f] & 0x7f) | (v & 0x80); > +} > + > +/* { dg-final { scan-assembler-times "rlwinm" 2 } } */ > diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim.C b/gcc/testsuite/g++.target/powerpc/zext-elim.C > new file mode 100644 > index 00000000000..56eabbe0c19 > --- /dev/null > +++ b/gcc/testsuite/g++.target/powerpc/zext-elim.C > @@ -0,0 +1,30 @@ > +/* { dg-do compile { target { powerpc*-*-* } } } */ > +/* { dg-require-effective-target lp64 } */ > +/* { dg-require-effective-target powerpc_p9vector_ok } */ > +/* { dg-options "-mcpu=power9 -O2 -free" } */ > + > +#include <stddef.h> > + > +bool foo (int a, int b) > +{ > + if (a > 2) > + return false; > + > + if (b < 10) > + return true; > + > + return true; > +} > + > +int bar (int a, int b) > +{ > + if (a > 2) > + return 0; > + > + if (b < 10) > + return 1; > + > + return 0; > +} > + > +/* { dg-final { scan-assembler-not "rldicl" } } */
diff --git a/gcc/ree.cc b/gcc/ree.cc index fc04249fa84..dc6da21ec16 100644 --- a/gcc/ree.cc +++ b/gcc/ree.cc @@ -253,6 +253,66 @@ struct ext_cand static int max_insn_uid; +/* Return TRUE if OP can be considered a zero extension from one or + more sub-word modes to larger modes up to a full word. + + For example (and:DI (reg) (const_int X)) + + Depending on the value of X could be considered a zero extension + from QI, HI and SI to larger modes up to DImode. */ + +static bool +rtx_is_zext_p (rtx insn) +{ + if (GET_CODE (insn) == AND) + { + rtx set = XEXP (insn, 0); + if (REG_P (set)) + { + rtx src = XEXP (insn, 1); + + if (CONST_INT_P (src) + && IN_RANGE (exact_log2 (UINTVAL (src)), 0, 7)) + return true; + } + else + return false; + } + + return false; +} +/* Return TRUE if OP can be considered a zero extension from one or + more sub-word modes to larger modes up to a full word. + + For example (and:DI (reg) (const_int X)) + + Depending on the value of X could be considered a zero extension + from QI, HI and SI to larger modes up to DImode. */ + +static bool +rtx_is_zext_p (rtx_insn *insn) +{ + rtx body = single_set (insn); + + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == AND) + { + rtx set = XEXP (SET_SRC (body), 0); + + if (REG_P (set) && GET_MODE (SET_DEST (body)) == GET_MODE (set)) + { + rtx src = XEXP (SET_SRC (body), 1); + + if (CONST_INT_P (src) + && IN_RANGE (exact_log2 (UINTVAL (src)), 0, 7)) + return true; + } + else + return false; + } + + return false; +} + /* Update or remove REG_EQUAL or REG_EQUIV notes for INSN. */ static bool @@ -319,7 +379,7 @@ combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*orig_set); machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); - rtx new_set; + rtx new_set = NULL_RTX; rtx cand_pat = single_set (cand->insn); /* If the extension's source/destination registers are not the same @@ -359,27 +419,41 @@ combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) else if (GET_CODE (orig_src) == cand->code) { /* Here is a sequence of two extensions. Try to merge them. */ - rtx temp_extension - = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); + rtx temp_extension = NULL_RTX; + if (GET_CODE (SET_SRC (cand_pat)) == AND) + temp_extension + = gen_rtx_AND (cand->mode, XEXP (orig_src, 0), XEXP (orig_src, 1)); + else + temp_extension + = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; + new_set = gen_rtx_SET (new_reg, temp_extension); } else if (GET_CODE (orig_src) == IF_THEN_ELSE) { /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, - in general, IF_THEN_ELSE should not be combined. */ - return false; + in general, IF_THEN_ELSE should not be combined. Relaxed + cases with IF_THEN_ELSE across basic blocls */ + return true; } else { /* This is the normal case. */ - rtx temp_extension + rtx temp_extension = NULL_RTX; + + if (GET_CODE (SET_SRC (cand_pat)) == AND) + temp_extension + = gen_rtx_AND (cand->mode, orig_src, XEXP (SET_SRC (cand_pat), 1)); + else + temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; + new_set = gen_rtx_SET (new_reg, temp_extension); } @@ -468,12 +542,13 @@ get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) FOR_EACH_INSN_USE (use, insn) { if (GET_CODE (DF_REF_REG (use)) == SUBREG) - return NULL; + return NULL; if (REGNO (DF_REF_REG (use)) == REGNO (reg)) break; } - gcc_assert (use != NULL); + if (use == NULL) + return NULL; ref_chain = DF_REF_CHAIN (use); @@ -481,9 +556,9 @@ get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) { /* Problem getting some definition for this instruction. */ if (ref_link->ref == NULL) - return NULL; + return NULL; if (DF_REF_INSN_INFO (ref_link->ref) == NULL) - return NULL; + return NULL; /* As global regs are assumed to be defined at each function call dataflow can report a call_insn as being a definition of REG. But we can't do anything with that in this pass so proceed only @@ -698,6 +773,258 @@ get_sub_rtx (rtx_insn *def_insn) return sub_rtx; } +/* Return TRUE if reaching definition of def_insn source operand + has has arithmetic peration like ASHIFT and LSHIFTRT. If TRUE + don't eliminate sign extension */ + +static bool +def_arith_p (rtx_insn *insn, rtx orig_src) +{ + if (!REG_P (orig_src)) + return true; + + vec<rtx_insn *> *dest = XCNEWVEC (vec<rtx_insn *>, 4); + if (!get_defs (insn, orig_src, dest)) + return false; + + int i; + rtx_insn *def_insn; + bool has_arith = false; + + FOR_EACH_VEC_ELT (*dest, i, def_insn) + { + rtx def_set = single_set (def_insn); + + if (!def_set) + { + has_arith = true; + break; + } + + if (DEBUG_INSN_P (def_insn)) + continue; + + /* Return True for following rtl insn. + set (reg x), (ashift ( ...) + set (reg x), (lshiftrt (....) */ + + if ((GET_CODE (PATTERN (def_insn)) == SET + && (GET_CODE (SET_SRC (def_set)) == ASHIFT + || GET_CODE (SET_SRC (def_set)) == LSHIFTRT))) + { + has_arith = true; + break; + } + + /* Return TRUE for following rtl insn. + set (reg x) , (plus(ashift ( ....) + set (reg x), (plus(lshiftrt (....) */ + + if (GET_CODE (PATTERN (def_insn)) == SET + && (GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_BIN_ARITH + || GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_COMM_ARITH)) + { + rtx src = XEXP (SET_SRC (def_set),0); + + if (GET_CODE (src) == LSHIFTRT + || GET_CODE (src) == ASHIFT) + { + has_arith = true; + break; + } + } + } + XDELETEVEC (dest); + return has_arith; +} + +/* Return TRUE if the cfg has following properties. + bb1 + |\ + | \ + | bb2 + | / + bb3 + + whereas bb1 has IF_THEN_ELSE and bb2 has the definition and bb3 has + zero/sign/AND extensions. */ + +static bool +feasible_cfg (ext_cand *cand, rtx_insn *def_insn) +{ + basic_block bb = BLOCK_FOR_INSN (cand->insn); + edge fallthru_edge; + edge e; + edge_iterator ei; + + FOR_EACH_EDGE (e, ei, bb->preds) + { + rtx_insn *insn = BB_END (e->src) ? PREV_INSN (BB_END (e->src)) : NULL; + + if (insn == NULL) + continue; + + if (DEBUG_INSN_P (insn)) + continue; + + rtx set = single_set (insn); + + /* Block has IF_THEN_ELSE */ + if (insn && set + && GET_CODE (set) == SET && SET_SRC (set) + && GET_CODE (SET_SRC (set)) == IF_THEN_ELSE) + { + if (e->dest == bb) + { + basic_block jump_block = e->dest; + if (jump_block != bb) + return false; + } + else + { + /* def_insn block has single successor and fall through + edge target are the block for cand insn. */ + if (single_succ_p (e->dest)) + { + fallthru_edge = single_succ_edge (e->dest); + if (BB_END (fallthru_edge->dest) + && bb != fallthru_edge->dest) + return false; + } + } + } + } + + /* def_insn block has single successor and fall through + edge target are the block for cand insn. */ + if (single_succ_p (BLOCK_FOR_INSN (def_insn))) + { + fallthru_edge = single_succ_edge (BLOCK_FOR_INSN (def_insn)); + if (BB_END (fallthru_edge->dest) + && bb != fallthru_edge->dest) + return false; + } + else + return false; + + return true; +} + +/* Return TRUE if the candidate extension INSN and def_insn are + feasible for extension elimination. + + Things to consider: + + cfg properties are feasible for extension elimination. + + sign_extend with def insn as PLUS and the reaching definition + of def_insn are not ASHIFT and LSHIFTRT. + + zero_extend with def insn as XOR/IOR and the reachin definition + of def_insn are not ASHIFT and LSHIFTRT. + + The destination register of the extension insn must not be + used or set between the def_insn and cand->insn exclusive. + + AND with zero extension properties has USE and the register + of cand insn are same as register of USE operand. */ + +static bool +eliminate_across_bbs_p (ext_cand *cand, rtx_insn *def_insn) +{ + basic_block bb = BLOCK_FOR_INSN (cand->insn); + + if (!feasible_cfg (cand, def_insn)) + return false; + + rtx cand_set = single_set(cand->insn); + /* The destination register of the extension insn must not be + used or set between the def_insn and cand->insn exclusive. */ + if (INSN_CHAIN_CODE_P (GET_CODE (def_insn)) + && INSN_CHAIN_CODE_P (cand->code)) + if ((cand->code == ZERO_EXTEND) + && REG_P (SET_DEST (cand_set)) && NEXT_INSN (def_insn) + && reg_used_set_between_p(SET_DEST (cand_set), def_insn, cand->insn)) + return false; + + if (cand->code == ZERO_EXTEND + && (bb != BLOCK_FOR_INSN (def_insn) + || DF_INSN_LUID (def_insn) > DF_INSN_LUID (cand->insn))) + return false; + + if (rtx_is_zext_p (cand->insn)) + { + if (GET_CODE (PATTERN (BB_END (bb))) != USE) + return false; + + if (REGNO (XEXP (PATTERN (BB_END (bb)), 0)) != REGNO (SET_DEST (cand->expr))) + return false; + } + + rtx set = single_set (def_insn); + + if (!set) + return false; + + if (cand->code == SIGN_EXTEND + && GET_CODE (set) == SET) + { + rtx orig_src = SET_SRC (set); + machine_mode ext_src_mode; + + ext_src_mode = GET_MODE (XEXP (SET_SRC (cand->expr), 0)); + + if (GET_MODE (SET_DEST (set)) != ext_src_mode) + return false; + + if (GET_CODE (orig_src) != PLUS) + return false; + + if (!REG_P (XEXP (orig_src, 0))) + return false; + + if (!REG_P (XEXP (orig_src,1))) + return false; + + if (GET_CODE (orig_src) == PLUS) + { + bool def_src1 + = def_arith_p (def_insn, + XEXP (SET_SRC (set), 0)); + bool def_src2 + = def_arith_p (def_insn, + XEXP (SET_SRC (set), 1)); + + if (def_src1 || def_src2) + return false; + } + } + + if (cand->code == ZERO_EXTEND + && GET_CODE (set) == SET) + { + if (GET_CODE (SET_SRC (set)) != XOR + && GET_CODE (SET_SRC (set)) != IOR) + return false; + + if (GET_CODE (SET_SRC (set)) == XOR + || GET_CODE (SET_SRC (set)) == IOR) + { + bool def_src1 + = def_arith_p (def_insn, + XEXP (SET_SRC (set), 0)); + bool def_src2 + = def_arith_p (def_insn, + XEXP (SET_SRC (set), 1)); + + if (def_src1 || def_src2) + return false; + } + } + + return true; +} + /* Merge the DEF_INSN with an extension. Calls combine_set_extension on the SET pattern. */ @@ -713,12 +1040,32 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) if (sub_rtx == NULL) return false; - if (GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode - || ((state->modified[INSN_UID (def_insn)].kind - == (cand->code == ZERO_EXTEND + bool copy_needed + = (REGNO (SET_DEST (cand->expr)) != REGNO (XEXP (SET_SRC (cand->expr), 0))); + + bool feasible = eliminate_across_bbs_p (cand, def_insn); + + if (!feasible) return false; + + /* Combine zero_extend/sign_extend/AND and if sign_extend and + mode of DEST and SRC are different. */ + + bool is_zext = rtx_is_zext_p (cand->insn) + || cand->code == ZERO_EXTEND + || cand->code == SIGN_EXTEND; + + bool do_elimination = !copy_needed + && is_zext + && (cand->code == SIGN_EXTEND + || GET_MODE (SET_DEST (*sub_rtx)) != ext_src_mode); + + if (((do_elimination + && state->modified[INSN_UID (def_insn)].kind == EXT_MODIFIED_NONE)) + || ((state->modified[INSN_UID (def_insn)].kind + == (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT : EXT_MODIFIED_SEXT)) - && state->modified[INSN_UID (def_insn)].mode - == ext_src_mode)) + && state->modified[INSN_UID (def_insn)].mode + == ext_src_mode)) { if (GET_MODE_UNIT_SIZE (GET_MODE (SET_DEST (*sub_rtx))) >= GET_MODE_UNIT_SIZE (cand->mode)) @@ -734,7 +1081,6 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) return true; } } - return false; } @@ -744,7 +1090,9 @@ merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state) static inline rtx get_extended_src_reg (rtx src) { - while (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND) + while (GET_CODE (src) == SIGN_EXTEND + || GET_CODE (src) == ZERO_EXTEND + || rtx_is_zext_p (src)) src = XEXP (src, 0); gcc_assert (REG_P (src)); return src; @@ -882,8 +1230,7 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) /* The destination register of the extension insn must not be used or set between the def_insn and cand->insn exclusive. */ - if (reg_used_between_p (SET_DEST (set), def_insn, cand->insn) - || reg_set_between_p (SET_DEST (set), def_insn, cand->insn)) + if (reg_used_set_between_p (SET_DEST (set), def_insn, cand->insn)) return false; /* We must be able to copy between the two registers. Generate, @@ -975,10 +1322,8 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) used or set between the def_insn2 and def_insn exclusive. Likewise for the other reg, i.e. check both reg1 and reg2 in the above comment. */ - if (reg_used_between_p (SET_DEST (set), def_insn2, def_insn) - || reg_set_between_p (SET_DEST (set), def_insn2, def_insn) - || reg_used_between_p (src_reg, def_insn2, def_insn) - || reg_set_between_p (src_reg, def_insn2, def_insn)) + if (reg_used_set_between_p (SET_DEST (set), def_insn2, def_insn) + || reg_used_set_between_p (src_reg, def_insn2, def_insn)) break; state->defs_list[0] = def_insn2; @@ -1004,15 +1349,17 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) cand->mode = mode; } - merge_successful = true; - + merge_successful = false; /* Go through the defs vector and try to merge all the definitions in this vector. */ state->modified_list.truncate (0); FOR_EACH_VEC_ELT (state->defs_list, defs_ix, def_insn) { if (merge_def_and_ext (cand, def_insn, state)) - state->modified_list.safe_push (def_insn); + { + merge_successful = true; + state->modified_list.safe_push (def_insn); + } else { merge_successful = false; @@ -1045,34 +1392,71 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state) definitions could be merged. */ if (apply_change_group ()) { - if (dump_file) - fprintf (dump_file, "All merges were successful.\n"); + if (state->modified_list.length() == 0) + return false; + + rtx_insn *insn = state->modified_list[0]; + + if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) != XOR + && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS + && GET_CODE (SET_SRC (PATTERN (insn))) != IOR) + return false; + + if (dump_file) + fprintf (dump_file, "All merges were successful.\n"); FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) { ext_modified *modified = &state->modified[INSN_UID (def_insn)]; if (modified->kind == EXT_MODIFIED_NONE) modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT - : EXT_MODIFIED_SEXT); + : EXT_MODIFIED_SEXT); if (copy_needed) modified->do_not_reextend = 1; } return true; } - else - { - /* Changes need not be cancelled explicitly as apply_change_group - does it. Print list of definitions in the dump_file for debug - purposes. This extension cannot be deleted. */ - if (dump_file) - { - fprintf (dump_file, - "Merge cancelled, non-mergeable definitions:\n"); - FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) - print_rtl_single (dump_file, def_insn); - } - } + else + { + if (state->modified_list.length() == 0) + return false; + + rtx_insn *insn = state->modified_list[0]; + + if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) != XOR + && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS + && GET_CODE (SET_SRC (PATTERN (insn))) != IOR) + return false; + + if (cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND) + { + FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) + { + ext_modified *modified = &state->modified[INSN_UID (def_insn)]; + if (modified->kind == EXT_MODIFIED_NONE) + modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT + : EXT_MODIFIED_SEXT); + + modified->do_not_reextend = 1; + } + return true; + } + /* Changes need not be cancelled explicitly as apply_change_group + does it. Print list of definitions in the dump_file for debug + purposes. This extension cannot be deleted. */ + if (dump_file) + { + fprintf (dump_file, + "Merge cancelled, non-mergeable definitions:\n"); + FOR_EACH_VEC_ELT (state->modified_list, i, def_insn) + print_rtl_single (dump_file, def_insn); + } + } } else { @@ -1106,7 +1490,7 @@ add_removable_extension (const_rtx expr, rtx_insn *insn, mode = GET_MODE (dest); if (REG_P (dest) - && (code == SIGN_EXTEND || code == ZERO_EXTEND) + && (code == SIGN_EXTEND || code == ZERO_EXTEND || rtx_is_zext_p (src)) && REG_P (XEXP (src, 0))) { rtx reg = XEXP (src, 0); @@ -1125,7 +1509,7 @@ add_removable_extension (const_rtx expr, rtx_insn *insn, fprintf (dump_file, "Cannot eliminate extension:\n"); print_rtl_single (dump_file, insn); fprintf (dump_file, " because it can operate on uninitialized" - " data\n"); + " data\n"); } return; } @@ -1320,8 +1704,8 @@ find_and_remove_re (void) if (REG_P (XEXP (SET_SRC (set), 0)) && (REGNO (SET_DEST (set)) != REGNO (XEXP (SET_SRC (set), 0)))) { - reinsn_copy_list.safe_push (curr_cand->insn); - reinsn_copy_list.safe_push (state.defs_list[0]); + reinsn_copy_list.safe_push (curr_cand->insn); + reinsn_copy_list.safe_push (state.defs_list[0]); } reinsn_del_list.safe_push (curr_cand->insn); state.modified[INSN_UID (curr_cand->insn)].deleted = 1; diff --git a/gcc/rtl.h b/gcc/rtl.h index 988691f5710..9278ec43d69 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -3631,6 +3631,7 @@ extern int count_occurrences (const_rtx, const_rtx, int); extern bool reg_referenced_p (const_rtx, const_rtx); extern bool reg_used_between_p (const_rtx, const rtx_insn *, const rtx_insn *); extern bool reg_set_between_p (const_rtx, const rtx_insn *, const rtx_insn *); +extern bool reg_used_set_between_p (rtx, rtx_insn *, rtx_insn *); extern int commutative_operand_precedence (rtx); extern bool swap_commutative_operands_p (rtx, rtx); extern bool modified_between_p (const_rtx, const rtx_insn *, const rtx_insn *); diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc index 31707f3b90a..fa0e8741416 100644 --- a/gcc/rtlanal.cc +++ b/gcc/rtlanal.cc @@ -1134,6 +1134,21 @@ no_labels_between_p (const rtx_insn *beg, const rtx_insn *end) return true; } +/* The register reg of the extension to_insn must not be + used or set between the from_insn and to_insn exclusive. */ + +bool +reg_used_set_between_p (rtx reg, + rtx_insn *from_insn, + rtx_insn *to_insn) +{ + if (reg_used_between_p (reg, from_insn, to_insn) + || reg_set_between_p (reg, from_insn, to_insn)) + return true; + + return false; +} + /* Return true if register REG is used in an insn between FROM_INSN and TO_INSN (exclusive of those two). */ diff --git a/gcc/testsuite/g++.target/powerpc/sext-elim.C b/gcc/testsuite/g++.target/powerpc/sext-elim.C new file mode 100644 index 00000000000..431696cf11e --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/sext-elim.C @@ -0,0 +1,18 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-require-effective-target lp64 } */ +/* { dg-require-effective-target powerpc_p9vector_ok } */ +/* { dg-options "-mcpu=power9 -O2 -free" } */ + +unsigned long c2l(unsigned char* p) +{ + unsigned long res = *p + *(p+1); + return res; +} + +long c2sl(signed char* p) +{ + long res = *p + *(p+1); + return res; +} + +/* { dg-final { scan-assembler-not "extsw" } } */ diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-1.C b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C new file mode 100644 index 00000000000..bc6cc0eb3ca --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C @@ -0,0 +1,19 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-require-effective-target lp64 } */ +/* { dg-require-effective-target powerpc_p9vector_ok } */ +/* { dg-options "-mcpu=power9 -O2 -free" } */ + +extern unsigned char magic1[256]; + +unsigned int hash(const unsigned char inp[4]) +{ + const unsigned long long INIT = 0x1ULL; + unsigned long long h1 = INIT; + h1 = magic1[((unsigned long long)inp[0]) ^ h1]; + h1 = magic1[((unsigned long long)inp[1]) ^ h1]; + h1 = magic1[((unsigned long long)inp[2]) ^ h1]; + h1 = magic1[((unsigned long long)inp[3]) ^ h1]; + return h1; +} + +/* { dg-final { scan-assembler-not "rlwinm" } } */ diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-2.C b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C new file mode 100644 index 00000000000..4e72925104f --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C @@ -0,0 +1,11 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-require-effective-target lp64 } */ +/* { dg-require-effective-target powerpc_p9vector_ok } */ +/* { dg-options "-mcpu=power9 -O2 -free" } */ + +unsigned char g(unsigned char t[], unsigned char v) +{ + return (t[v & 0x7f] & 0x7f) | (v & 0x80); +} + +/* { dg-final { scan-assembler-times "rlwinm" 2 } } */ diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim.C b/gcc/testsuite/g++.target/powerpc/zext-elim.C new file mode 100644 index 00000000000..56eabbe0c19 --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/zext-elim.C @@ -0,0 +1,30 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-require-effective-target lp64 } */ +/* { dg-require-effective-target powerpc_p9vector_ok } */ +/* { dg-options "-mcpu=power9 -O2 -free" } */ + +#include <stddef.h> + +bool foo (int a, int b) +{ + if (a > 2) + return false; + + if (b < 10) + return true; + + return true; +} + +int bar (int a, int b) +{ + if (a > 2) + return 0; + + if (b < 10) + return 1; + + return 0; +} + +/* { dg-final { scan-assembler-not "rldicl" } } */