aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorGeorg-Johann Lay <avr@gjlay.de>2011-08-14 09:10:13 +0000
committerGeorg-Johann Lay <avr@gjlay.de>2011-08-14 09:10:13 +0000
commit68c794f2de025f5ddc2ccfff88a005d44d5f4acc (patch)
tree445db3c84b839c8c0923ff0ffe355b8a99eb20bf /gcc
parent68708f2ce1b87fe8770dc3a4199d2a6eab047881 (diff)
* 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. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@177744 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog13
-rw-r--r--gcc/config/avr/avr.c327
-rw-r--r--gcc/config/avr/avr.md29
3 files changed, 294 insertions, 75 deletions
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 <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
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<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);
+ /* 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.