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
This commit is contained in:
Georg-Johann Lay 2011-08-14 09:10:13 +00:00 committed by Georg-Johann Lay
parent fdf0337727
commit f371377833
3 changed files with 294 additions and 75 deletions

View file

@ -1,3 +1,16 @@
2011-08-14 Georg-Johann Lay <avr@gjlay.de>
* 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 <davidxl@google.com>
* cp/class.c (update_vtable_entry_for_fn): Set

View file

@ -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<n> is COMPARE now and ifelse<n> 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);
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));
/* Now we work under compare insn with difficult branch. */
rtx next = next_real_insn (insn);
rtx pat = PATTERN (next);
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;
}
}
}
}
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
&& 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;
}
}
}
}
}

View file

@ -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.