From: Georg-Johann Lay Date: Sun, 14 Aug 2011 09:10:13 +0000 (+0000) Subject: re PR target/49903 ([avr] Redundant comparisons in binary-search switch/case expansion) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f3713778332ef6b2d10c3981ebcc6ab8540b3f85;p=gcc.git re PR target/49903 ([avr] Redundant comparisons in binary-search switch/case expansion) * PR target/49903 * config/avr/avr.md (UNSPEC_IDENTITY): New c_enum. (branch_unspec): New insn. (branch): Beauty farm. * config/avr/avr.c (compare_condition): Use JUMP_P. Test SET_SRC to be IF_THEN_ELSE. (avr_compare_pattern, avr_reorg_remove_redundant_compare): New static functions. (avr_reorg): Use them. Use next_real_insn instead of NEXT_INSN. Use CONST_INT_P. Beauty. From-SVN: r177744 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b600d256a6e..6e8c5adb2e9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2011-08-14 Georg-Johann Lay + + * PR target/49903 + * config/avr/avr.md (UNSPEC_IDENTITY): New c_enum. + (branch_unspec): New insn. + (branch): Beauty farm. + * config/avr/avr.c (compare_condition): Use JUMP_P. Test SET_SRC + to be IF_THEN_ELSE. + (avr_compare_pattern, avr_reorg_remove_redundant_compare): + New static functions. + (avr_reorg): Use them. Use next_real_insn instead of NEXT_INSN. + Use CONST_INT_P. Beauty. + 2011-08-12 David Li * cp/class.c (update_vtable_entry_for_fn): Set diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 76542a87dc0..6bb236cb584 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -2947,15 +2947,17 @@ static RTX_CODE compare_condition (rtx insn) { rtx next = next_real_insn (insn); - RTX_CODE cond = UNKNOWN; - if (next && GET_CODE (next) == JUMP_INSN) + + if (next && JUMP_P (next)) { rtx pat = PATTERN (next); rtx src = SET_SRC (pat); - rtx t = XEXP (src, 0); - cond = GET_CODE (t); + + if (IF_THEN_ELSE == GET_CODE (src)) + return GET_CODE (XEXP (src, 0)); } - return cond; + + return UNKNOWN; } /* Returns nonzero if INSN is a tst insn that only tests the sign. */ @@ -6046,82 +6048,265 @@ avr_normalize_condition (RTX_CODE condition) } } -/* This function optimizes conditional jumps. */ +/* Helper function for `avr_reorg'. */ + +static rtx +avr_compare_pattern (rtx insn) +{ + rtx pattern = single_set (insn); + + if (pattern + && NONJUMP_INSN_P (insn) + && SET_DEST (pattern) == cc0_rtx + && GET_CODE (SET_SRC (pattern)) == COMPARE) + { + return pattern; + } + + return NULL_RTX; +} + +/* Helper function for `avr_reorg'. */ + +/* Expansion of switch/case decision trees leads to code like + + cc0 = compare (Reg, Num) + if (cc0 == 0) + goto L1 + + cc0 = compare (Reg, Num) + if (cc0 > 0) + goto L2 + + The second comparison is superfluous and can be deleted. + The second jump condition can be transformed from a + "difficult" one to a "simple" one because "cc0 > 0" and + "cc0 >= 0" will have the same effect here. + + This function relies on the way switch/case is being expaned + as binary decision tree. For example code see PR 49903. + + Return TRUE if optimization performed. + Return FALSE if nothing changed. + + INSN1 is a comparison, i.e. avr_compare_pattern != 0. + + We don't want to do this in text peephole because it is + tedious to work out jump offsets there and the second comparison + might have been transormed by `avr_reorg'. + + RTL peephole won't do because peephole2 does not scan across + basic blocks. */ + +static bool +avr_reorg_remove_redundant_compare (rtx insn1) +{ + rtx comp1, ifelse1, xcond1, branch1; + rtx comp2, ifelse2, xcond2, branch2, insn2; + enum rtx_code code; + rtx jump, target, cond; + + /* Look out for: compare1 - branch1 - compare2 - branch2 */ + + branch1 = next_nonnote_nondebug_insn (insn1); + if (!branch1 || !JUMP_P (branch1)) + return false; + + insn2 = next_nonnote_nondebug_insn (branch1); + if (!insn2 || !avr_compare_pattern (insn2)) + return false; + + branch2 = next_nonnote_nondebug_insn (insn2); + if (!branch2 || !JUMP_P (branch2)) + return false; + + comp1 = avr_compare_pattern (insn1); + comp2 = avr_compare_pattern (insn2); + xcond1 = single_set (branch1); + xcond2 = single_set (branch2); + + if (!comp1 || !comp2 + || !rtx_equal_p (comp1, comp2) + || !xcond1 || SET_DEST (xcond1) != pc_rtx + || !xcond2 || SET_DEST (xcond2) != pc_rtx + || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1)) + || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2))) + { + return false; + } + + comp1 = SET_SRC (comp1); + ifelse1 = SET_SRC (xcond1); + ifelse2 = SET_SRC (xcond2); + + /* comp is COMPARE now and ifelse is IF_THEN_ELSE. */ + + if (EQ != GET_CODE (XEXP (ifelse1, 0)) + || !REG_P (XEXP (comp1, 0)) + || !CONST_INT_P (XEXP (comp1, 1)) + || XEXP (ifelse1, 2) != pc_rtx + || XEXP (ifelse2, 2) != pc_rtx + || LABEL_REF != GET_CODE (XEXP (ifelse1, 1)) + || LABEL_REF != GET_CODE (XEXP (ifelse2, 1)) + || !COMPARISON_P (XEXP (ifelse2, 0)) + || cc0_rtx != XEXP (XEXP (ifelse1, 0), 0) + || cc0_rtx != XEXP (XEXP (ifelse2, 0), 0) + || const0_rtx != XEXP (XEXP (ifelse1, 0), 1) + || const0_rtx != XEXP (XEXP (ifelse2, 0), 1)) + { + return false; + } + + /* We filtered the insn sequence to look like + + (set (cc0) + (compare (reg:M N) + (const_int VAL))) + (set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref L1) + (pc))) + + (set (cc0) + (compare (reg:M N) + (const_int VAL))) + (set (pc) + (if_then_else (CODE (cc0) + (const_int 0)) + (label_ref L2) + (pc))) + */ + + code = GET_CODE (XEXP (ifelse2, 0)); + + /* Map GT/GTU to GE/GEU which is easier for AVR. + The first two instructions compare/branch on EQ + so we may replace the difficult + + if (x == VAL) goto L1; + if (x > VAL) goto L2; + + with easy + + if (x == VAL) goto L1; + if (x >= VAL) goto L2; + + Similarly, replace LE/LEU by LT/LTU. */ + + switch (code) + { + case EQ: + case LT: case LTU: + case GE: case GEU: + break; + + case LE: case LEU: + case GT: case GTU: + code = avr_normalize_condition (code); + break; + + default: + return false; + } + + /* Wrap the branches into UNSPECs so they won't be changed or + optimized in the remainder. */ + + target = XEXP (XEXP (ifelse1, 1), 0); + cond = XEXP (ifelse1, 0); + jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1); + + JUMP_LABEL (jump) = JUMP_LABEL (branch1); + + target = XEXP (XEXP (ifelse2, 1), 0); + cond = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx); + jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2); + + JUMP_LABEL (jump) = JUMP_LABEL (branch2); + + /* The comparisons in insn1 and insn2 are exactly the same; + insn2 is superfluous so delete it. */ + + delete_insn (insn2); + delete_insn (branch1); + delete_insn (branch2); + + return true; +} + + +/* Implement `TARGET_MACHINE_DEPENDENT_REORG'. */ +/* Optimize conditional jumps. */ static void avr_reorg (void) { - rtx insn, pattern; + rtx insn = get_insns(); - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn)) { - if (! (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - || !single_set (insn)) - continue; + rtx pattern = avr_compare_pattern (insn); + + if (!pattern) + continue; - pattern = PATTERN (insn); + if (optimize + && avr_reorg_remove_redundant_compare (insn)) + { + continue; + } - if (GET_CODE (pattern) == PARALLEL) - pattern = XVECEXP (pattern, 0, 0); - if (GET_CODE (pattern) == SET - && SET_DEST (pattern) == cc0_rtx - && compare_diff_p (insn)) + if (compare_diff_p (insn)) { - if (GET_CODE (SET_SRC (pattern)) == COMPARE) - { - /* Now we work under compare insn. */ - - pattern = SET_SRC (pattern); - if (true_regnum (XEXP (pattern,0)) >= 0 - && true_regnum (XEXP (pattern,1)) >= 0 ) - { - rtx x = XEXP (pattern,0); - rtx next = next_real_insn (insn); - rtx pat = PATTERN (next); - rtx src = SET_SRC (pat); - rtx t = XEXP (src,0); - PUT_CODE (t, swap_condition (GET_CODE (t))); - XEXP (pattern,0) = XEXP (pattern,1); - XEXP (pattern,1) = x; - INSN_CODE (next) = -1; - } - else if (true_regnum (XEXP (pattern, 0)) >= 0 - && XEXP (pattern, 1) == const0_rtx) - { - /* This is a tst insn, we can reverse it. */ - rtx next = next_real_insn (insn); - rtx pat = PATTERN (next); - rtx src = SET_SRC (pat); - rtx t = XEXP (src,0); + /* Now we work under compare insn with difficult branch. */ + + rtx next = next_real_insn (insn); + rtx pat = PATTERN (next); + + pattern = SET_SRC (pattern); + + if (true_regnum (XEXP (pattern, 0)) >= 0 + && true_regnum (XEXP (pattern, 1)) >= 0) + { + rtx x = XEXP (pattern, 0); + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); + PUT_CODE (t, swap_condition (GET_CODE (t))); + XEXP (pattern, 0) = XEXP (pattern, 1); + XEXP (pattern, 1) = x; + INSN_CODE (next) = -1; + } + else if (true_regnum (XEXP (pattern, 0)) >= 0 + && XEXP (pattern, 1) == const0_rtx) + { + /* This is a tst insn, we can reverse it. */ + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); - PUT_CODE (t, swap_condition (GET_CODE (t))); - XEXP (pattern, 1) = XEXP (pattern, 0); - XEXP (pattern, 0) = const0_rtx; - INSN_CODE (next) = -1; - INSN_CODE (insn) = -1; - } - else if (true_regnum (XEXP (pattern,0)) >= 0 - && GET_CODE (XEXP (pattern,1)) == CONST_INT) - { - rtx x = XEXP (pattern,1); - rtx next = next_real_insn (insn); - rtx pat = PATTERN (next); - rtx src = SET_SRC (pat); - rtx t = XEXP (src,0); - enum machine_mode mode = GET_MODE (XEXP (pattern, 0)); - - if (avr_simplify_comparison_p (mode, GET_CODE (t), x)) - { - XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode); - PUT_CODE (t, avr_normalize_condition (GET_CODE (t))); - INSN_CODE (next) = -1; - INSN_CODE (insn) = -1; - } - } - } - } + PUT_CODE (t, swap_condition (GET_CODE (t))); + XEXP (pattern, 1) = XEXP (pattern, 0); + XEXP (pattern, 0) = const0_rtx; + INSN_CODE (next) = -1; + INSN_CODE (insn) = -1; + } + else if (true_regnum (XEXP (pattern, 0)) >= 0 + && CONST_INT_P (XEXP (pattern, 1))) + { + rtx x = XEXP (pattern, 1); + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); + enum machine_mode mode = GET_MODE (XEXP (pattern, 0)); + + if (avr_simplify_comparison_p (mode, GET_CODE (t), x)) + { + XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode); + PUT_CODE (t, avr_normalize_condition (GET_CODE (t))); + INSN_CODE (next) = -1; + INSN_CODE (insn) = -1; + } + } + } } } diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 356b5095438..ad0febc333e 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -56,6 +56,7 @@ UNSPEC_FMULS UNSPEC_FMULSU UNSPEC_COPYSIGN + UNSPEC_IDENTITY ]) (define_c_enum "unspecv" @@ -3339,16 +3340,36 @@ (define_insn "branch" [(set (pc) (if_then_else (match_operator 1 "simple_comparison_operator" - [(cc0) - (const_int 0)]) + [(cc0) + (const_int 0)]) (label_ref (match_operand 0 "" "")) (pc)))] "" - "* - return ret_cond_branch (operands[1], avr_jump_mode (operands[0],insn), 0);" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); + } [(set_attr "type" "branch") (set_attr "cc" "clobber")]) + +;; Same as above but wrap SET_SRC so that this branch won't be transformed +;; or optimized in the remainder. + +(define_insn "branch_unspec" + [(set (pc) + (unspec [(if_then_else (match_operator 1 "simple_comparison_operator" + [(cc0) + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)) + ] UNSPEC_IDENTITY))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); + } + [(set_attr "type" "branch") + (set_attr "cc" "none")]) + ;; **************************************************************** ;; AVR does not have following conditional jumps: LE,LEU,GT,GTU. ;; Convert them all to proper jumps.