This patch is the main bulk of this submission. It modifies the compare combining part of try_combine(), adding a call of CANONICALIZE_COMPARISON into the entire logic.
Also, instead of testing for XEXP(SET_SRC(PATTERN(i3)),1) == const0_rtx at the top, it now allows CONST_INT_P(XEXP(SET_SRC(PATTERN(i3)),1)), tries to adjust it by simplify_compare_const() from the last patch, and then tests if op1 == const0_rtx. This is a small improvement in some cases. (if you remove the call to simplify_compare_const(), and if CANONICALIZE_COMPARISON is not defined for the target, then this entire patch should be an 'idempotent patch', nothing should be changed to the effective combine results) One issue that I would like to RFC, is the use of CANONICALIZE_COMPARISON here; I'm afraid I might be abusing it. Since added_sets_2 is set here, the value of i2src/op0 is needed afterwards. This might not be conformant to the description of CANONICALIZE_COMPARISON in the internals manual, which doesn't say it can't trash op0 under arbitrary conditions while only preserving the comparison result. OTOH, I don't see another suitable macro/hook (with a close enough meaning), and the current definitions of CANONICALIZE_COMPARISON across the targets do not seem to clash with my use here. Does this macro use look okay, or does this justify creating a new one? Thanks, Chung-Lin
Index: combine.c =================================================================== --- combine.c (revision 172860) +++ combine.c (working copy) @@ -3046,58 +3047,89 @@ if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE - && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx + && CONST_INT_P (XEXP (SET_SRC (PATTERN (i3)), 1)) && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest)) { -#ifdef SELECT_CC_MODE - rtx *cc_use; - enum machine_mode compare_mode; -#endif + rtx newpat_dest; + rtx *cc_use_loc = NULL, cc_use_insn = NULL_RTX; + rtx op0 = i2src, op1 = XEXP (SET_SRC (PATTERN (i3)), 1); + enum machine_mode compare_mode, orig_compare_mode; + enum rtx_code compare_code = UNKNOWN, orig_compare_code = UNKNOWN; newpat = PATTERN (i3); - SUBST (XEXP (SET_SRC (newpat), 0), i2src); + newpat_dest = SET_DEST (newpat); + compare_mode = orig_compare_mode = GET_MODE (newpat_dest); - i2_is_used = 1; - -#ifdef SELECT_CC_MODE - /* See if a COMPARE with the operand we substituted in should be done - with the mode that is currently being used. If not, do the same - processing we do in `subst' for a SET; namely, if the destination - is used only once, try to replace it with a register of the proper - mode and also replace the COMPARE. */ if (undobuf.other_insn == 0 - && (cc_use = find_single_use (SET_DEST (newpat), i3, - &undobuf.other_insn)) - && ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use), - i2src, const0_rtx)) - != GET_MODE (SET_DEST (newpat)))) + && (cc_use_loc = find_single_use (SET_DEST (newpat), i3, + &cc_use_insn))) { - if (can_change_dest_mode (SET_DEST (newpat), added_sets_2, - compare_mode)) + compare_code = orig_compare_code = GET_CODE (*cc_use_loc); + compare_code = simplify_compare_const (compare_code, + op0, &op1); +#ifdef CANONICALIZE_COMPARISON + CANONICALIZE_COMPARISON (compare_code, op0, op1); +#endif + } + + /* Do the rest only if op1 is const0_rtx, which may be the + result of simplification. */ + if (op1 == const0_rtx) + { + if (cc_use_loc) { - unsigned int regno = REGNO (SET_DEST (newpat)); - rtx new_dest; - - if (regno < FIRST_PSEUDO_REGISTER) - new_dest = gen_rtx_REG (compare_mode, regno); - else +#ifdef SELECT_CC_MODE + enum machine_mode new_mode + = SELECT_CC_MODE (compare_code, op0, op1); + if (new_mode != orig_compare_mode + && can_change_dest_mode (SET_DEST (newpat), + added_sets_2, new_mode)) { - SUBST_MODE (regno_reg_rtx[regno], compare_mode); - new_dest = regno_reg_rtx[regno]; + unsigned int regno = REGNO (newpat_dest); + compare_mode = new_mode; + if (regno < FIRST_PSEUDO_REGISTER) + newpat_dest = gen_rtx_REG (compare_mode, regno); + else + { + SUBST_MODE (regno_reg_rtx[regno], compare_mode); + newpat_dest = regno_reg_rtx[regno]; + } } +#endif + /* Cases for modifying the CC-using comparison. */ + if (compare_code != orig_compare_code + /* ??? Do we need to verify the zero rtx? */ + && XEXP (*cc_use_loc, 1) == const0_rtx) + { + /* Replace cc_use_loc with entire new RTX. */ + SUBST (*cc_use_loc, + gen_rtx_fmt_ee (compare_code, compare_mode, + newpat_dest, const0_rtx)); + undobuf.other_insn = cc_use_insn; + } + else if (compare_mode != orig_compare_mode) + { + /* Just replace the CC reg with a new mode. */ + SUBST (XEXP (*cc_use_loc, 0), newpat_dest); + undobuf.other_insn = cc_use_insn; + } + } - SUBST (SET_DEST (newpat), new_dest); - SUBST (XEXP (*cc_use, 0), new_dest); - SUBST (SET_SRC (newpat), - gen_rtx_COMPARE (compare_mode, i2src, const0_rtx)); - } - else - undobuf.other_insn = 0; + /* Create new reg:CC if the CC mode has been altered. */ + if (compare_mode != orig_compare_mode) + SUBST (SET_DEST (newpat), newpat_dest); + /* This is always done to propagate i2src into newpat. */ + SUBST (SET_SRC (newpat), + gen_rtx_COMPARE (compare_mode, op0, op1)); + /* Create new version of i2pat if needed. */ + if (! rtx_equal_p (i2src, op0)) + i2pat = gen_rtx_SET (VOIDmode, i2dest, op0); + i2_is_used = 1; } -#endif } - else #endif + + if (i2_is_used == 0) { /* It is possible that the source of I2 or I1 may be performing an unneeded operation, such as a ZERO_EXTEND of something