diff options
author | Tom de Vries <tom@codesourcery.com> | 2011-04-07 08:10:34 +0000 |
---|---|---|
committer | Tom de Vries <vries@gcc.gnu.org> | 2011-04-07 08:10:34 +0000 |
commit | 472c95f5af0a86d073d305c2328acac19dd784bc (patch) | |
tree | 16eccafa75bb37f7b9b96e7f23c0feeb6c8d439b /gcc | |
parent | 7c16382a3ac63d118295dd2c039e4032738d2a60 (diff) | |
download | gcc-472c95f5af0a86d073d305c2328acac19dd784bc.zip gcc-472c95f5af0a86d073d305c2328acac19dd784bc.tar.gz gcc-472c95f5af0a86d073d305c2328acac19dd784bc.tar.bz2 |
re PR target/43920 (Choosing conditional execution over conditional branches for code size in some cases.)
2011-04-07 Tom de Vries <tom@codesourcery.com>
PR target/43920
* cfgcleanup.c (equal_different_set_p, can_replace_by, merge_dir): New
function.
(old_insns_match_p): Change return type. Replace return false/true with
return dir_none/dir_both. Use can_replace_by.
(flow_find_cross_jump): Add dir_p parameter. Init replacement direction
from dir_p. Register replacement direction in dir, last_dir and
afterlast_dir. Handle new return type of old_insns_match_p using
merge_dir. Return replacement direction in dir_p.
(flow_find_head_matching_sequence, outgoing_edges_match): Handle new
return type of old_insns_match_p.
(try_crossjump_to_edge): Add argument to call to flow_find_cross_jump.
* ifcvt.c ( cond_exec_process_if_block): Add argument to call to
flow_find_cross_jump.
* basic-block.h (enum replace_direction): New type.
(flow_find_cross_jump): Add parameter to declaration.
From-SVN: r172090
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/basic-block.h | 5 | ||||
-rw-r--r-- | gcc/cfgcleanup.c | 178 | ||||
-rw-r--r-- | gcc/ifcvt.c | 3 |
4 files changed, 183 insertions, 22 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e3b8aa8..00917fe 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2011-04-07 Tom de Vries <tom@codesourcery.com> + + PR target/43920 + * cfgcleanup.c (equal_different_set_p, can_replace_by, merge_dir): New + function. + (old_insns_match_p): Change return type. Replace return false/true with + return dir_none/dir_both. Use can_replace_by. + (flow_find_cross_jump): Add dir_p parameter. Init replacement direction + from dir_p. Register replacement direction in dir, last_dir and + afterlast_dir. Handle new return type of old_insns_match_p using + merge_dir. Return replacement direction in dir_p. + (flow_find_head_matching_sequence, outgoing_edges_match): Handle new + return type of old_insns_match_p. + (try_crossjump_to_edge): Add argument to call to flow_find_cross_jump. + * ifcvt.c ( cond_exec_process_if_block): Add argument to call to + flow_find_cross_jump. + * basic-block.h (enum replace_direction): New type. + (flow_find_cross_jump): Add parameter to declaration. + 2011-04-06 Uros Bizjak <ubizjak@gmail.com> * config/i386/sse.md (AVXMODEDCVTDQ2PS): Remove. diff --git a/gcc/basic-block.h b/gcc/basic-block.h index 239c925..2791fbc 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -804,9 +804,12 @@ extern bool fixup_abnormal_edges (void); extern void find_many_sub_basic_blocks (sbitmap); extern void rtl_make_eh_edge (sbitmap, basic_block, rtx); +enum replace_direction { dir_none, dir_forward, dir_backward, dir_both }; + /* In cfgcleanup.c. */ extern bool cleanup_cfg (int); -extern int flow_find_cross_jump (basic_block, basic_block, rtx *, rtx *); +extern int flow_find_cross_jump (basic_block, basic_block, rtx *, rtx *, + enum replace_direction*); extern int flow_find_head_matching_sequence (basic_block, basic_block, rtx *, rtx *, int); diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index 112cb49..8b9bb45 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -72,7 +72,7 @@ static bool block_was_dirty; static bool try_crossjump_to_edge (int, edge, edge); static bool try_crossjump_bb (int, basic_block); static bool outgoing_edges_match (int, basic_block, basic_block); -static bool old_insns_match_p (int, rtx, rtx); +static enum replace_direction old_insns_match_p (int, rtx, rtx); static void merge_blocks_move_predecessor_nojumps (basic_block, basic_block); static void merge_blocks_move_successor_nojumps (basic_block, basic_block); @@ -946,27 +946,143 @@ merge_memattrs (rtx x, rtx y) } -/* Return true if I1 and I2 are equivalent and thus can be crossjumped. */ + /* Checks if patterns P1 and P2 are equivalent, apart from the possibly + different single sets S1 and S2. */ static bool +equal_different_set_p (rtx p1, rtx s1, rtx p2, rtx s2) +{ + int i; + rtx e1, e2; + + if (p1 == s1 && p2 == s2) + return true; + + if (GET_CODE (p1) != PARALLEL || GET_CODE (p2) != PARALLEL) + return false; + + if (XVECLEN (p1, 0) != XVECLEN (p2, 0)) + return false; + + for (i = 0; i < XVECLEN (p1, 0); i++) + { + e1 = XVECEXP (p1, 0, i); + e2 = XVECEXP (p2, 0, i); + if (e1 == s1 && e2 == s2) + continue; + if (reload_completed + ? rtx_renumbered_equal_p (e1, e2) : rtx_equal_p (e1, e2)) + continue; + + return false; + } + + return true; +} + +/* Examine register notes on I1 and I2 and return: + - dir_forward if I1 can be replaced by I2, or + - dir_backward if I2 can be replaced by I1, or + - dir_both if both are the case. */ + +static enum replace_direction +can_replace_by (rtx i1, rtx i2) +{ + rtx s1, s2, d1, d2, src1, src2, note1, note2; + bool c1, c2; + + /* Check for 2 sets. */ + s1 = single_set (i1); + s2 = single_set (i2); + if (s1 == NULL_RTX || s2 == NULL_RTX) + return dir_none; + + /* Check that the 2 sets set the same dest. */ + d1 = SET_DEST (s1); + d2 = SET_DEST (s2); + if (!(reload_completed + ? rtx_renumbered_equal_p (d1, d2) : rtx_equal_p (d1, d2))) + return dir_none; + + /* Find identical req_equiv or reg_equal note, which implies that the 2 sets + set dest to the same value. */ + note1 = find_reg_equal_equiv_note (i1); + note2 = find_reg_equal_equiv_note (i2); + if (!note1 || !note2 || !rtx_equal_p (XEXP (note1, 0), XEXP (note2, 0)) + || !CONST_INT_P (XEXP (note1, 0))) + return dir_none; + + if (!equal_different_set_p (PATTERN (i1), s1, PATTERN (i2), s2)) + return dir_none; + + /* Although the 2 sets set dest to the same value, we cannot replace + (set (dest) (const_int)) + by + (set (dest) (reg)) + because we don't know if the reg is live and has the same value at the + location of replacement. */ + src1 = SET_SRC (s1); + src2 = SET_SRC (s2); + c1 = CONST_INT_P (src1); + c2 = CONST_INT_P (src2); + if (c1 && c2) + return dir_both; + else if (c2) + return dir_forward; + else if (c1) + return dir_backward; + + return dir_none; +} + +/* Merges directions A and B. */ + +static enum replace_direction +merge_dir (enum replace_direction a, enum replace_direction b) +{ + /* Implements the following table: + |bo fw bw no + ---+----------- + bo |bo fw bw no + fw |-- fw no no + bw |-- -- bw no + no |-- -- -- no. */ + + if (a == b) + return a; + + if (a == dir_both) + return b; + if (b == dir_both) + return a; + + return dir_none; +} + +/* Examine I1 and I2 and return: + - dir_forward if I1 can be replaced by I2, or + - dir_backward if I2 can be replaced by I1, or + - dir_both if both are the case. */ + +static enum replace_direction old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) { rtx p1, p2; /* Verify that I1 and I2 are equivalent. */ if (GET_CODE (i1) != GET_CODE (i2)) - return false; + return dir_none; /* __builtin_unreachable() may lead to empty blocks (ending with NOTE_INSN_BASIC_BLOCK). They may be crossjumped. */ if (NOTE_INSN_BASIC_BLOCK_P (i1) && NOTE_INSN_BASIC_BLOCK_P (i2)) - return true; + return dir_both; p1 = PATTERN (i1); p2 = PATTERN (i2); if (GET_CODE (p1) != GET_CODE (p2)) - return false; + return dir_none; /* If this is a CALL_INSN, compare register usage information. If we don't check this on stack register machines, the two @@ -987,15 +1103,15 @@ old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) rtx n2 = find_reg_note (i2, REG_EH_REGION, 0); if (!n1 && n2) - return false; + return dir_none; if (n1 && (!n2 || XEXP (n1, 0) != XEXP (n2, 0))) - return false; + return dir_none; if (!rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1), CALL_INSN_FUNCTION_USAGE (i2)) || SIBLING_CALL_P (i1) != SIBLING_CALL_P (i2)) - return false; + return dir_none; } #ifdef STACK_REGS @@ -1024,15 +1140,15 @@ old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) SET_HARD_REG_BIT (i2_regset, REGNO (XEXP (note, 0))); if (!hard_reg_set_equal_p (i1_regset, i2_regset)) - return false; + return dir_none; } #endif if (reload_completed ? rtx_renumbered_equal_p (p1, p2) : rtx_equal_p (p1, p2)) - return true; + return dir_both; - return false; + return can_replace_by (i1, i2); } /* When comparing insns I1 and I2 in flow_find_cross_jump or @@ -1059,18 +1175,32 @@ merge_notes (rtx i1, rtx i2) } /* Look through the insns at the end of BB1 and BB2 and find the longest - sequence that are equivalent. Store the first insns for that sequence - in *F1 and *F2 and return the sequence length. + sequence that are either equivalent, or allow forward or backward + replacement. Store the first insns for that sequence in *F1 and *F2 and + return the sequence length. + + DIR_P indicates the allowed replacement direction on function entry, and + the actual replacement direction on function exit. If NULL, only equivalent + sequences are allowed. To simplify callers of this function, if the blocks match exactly, store the head of the blocks in *F1 and *F2. */ int -flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) +flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2, + enum replace_direction *dir_p) { rtx i1, i2, last1, last2, afterlast1, afterlast2; int ninsns = 0; rtx p1; + enum replace_direction dir, last_dir, afterlast_dir; + + if (dir_p) + dir = *dir_p; + else + dir = dir_both; + afterlast_dir = dir; + last_dir = afterlast_dir; /* Skip simple jumps at the end of the blocks. Complex jumps still need to be compared for equivalence, which we'll do below. */ @@ -1107,7 +1237,8 @@ flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) if (i1 == BB_HEAD (bb1) || i2 == BB_HEAD (bb2)) break; - if (!old_insns_match_p (0, i1, i2)) + dir = merge_dir (dir, old_insns_match_p (0, i1, i2)); + if (dir == dir_none || (!dir_p && dir != dir_both)) break; merge_memattrs (i1, i2); @@ -1119,6 +1250,8 @@ flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) afterlast1 = last1, afterlast2 = last2; last1 = i1, last2 = i2; + afterlast_dir = last_dir; + last_dir = dir; p1 = PATTERN (i1); if (!(GET_CODE (p1) == USE || GET_CODE (p1) == CLOBBER)) ninsns++; @@ -1132,7 +1265,7 @@ flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) /* Don't allow the insn after a compare to be shared by cross-jumping unless the compare is also shared. */ if (ninsns && reg_mentioned_p (cc0_rtx, last1) && ! sets_cc0_p (last1)) - last1 = afterlast1, last2 = afterlast2, ninsns--; + last1 = afterlast1, last2 = afterlast2, last_dir = afterlast_dir, ninsns--; #endif /* Include preceding notes and labels in the cross-jump. One, @@ -1156,6 +1289,8 @@ flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) *f2 = last2; } + if (dir_p) + *dir_p = last_dir; return ninsns; } @@ -1222,7 +1357,7 @@ flow_find_head_matching_sequence (basic_block bb1, basic_block bb2, rtx *f1, && nehedges1 != nehedges2)) break; - if (!old_insns_match_p (0, i1, i2)) + if (old_insns_match_p (0, i1, i2) != dir_both) break; merge_memattrs (i1, i2); @@ -1451,7 +1586,8 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2) rr.update_label_nuses = false; for_each_rtx (&BB_END (bb1), replace_label, &rr); - match = old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)); + match = (old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)) + == dir_both); if (dump_file && match) fprintf (dump_file, "Tablejumps in bb %i and %i match.\n", @@ -1473,7 +1609,7 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2) /* First ensure that the instructions match. There may be many outgoing edges so this test is generally cheaper. */ - if (!old_insns_match_p (mode, BB_END (bb1), BB_END (bb2))) + if (old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)) != dir_both) return false; /* Search the outgoing edges, ensure that the counts do match, find possible @@ -1574,6 +1710,7 @@ try_crossjump_to_edge (int mode, edge e1, edge e2) int nmatch; basic_block src1 = e1->src, src2 = e2->src; basic_block redirect_to, redirect_from, to_remove; + enum replace_direction dir; rtx newpos1, newpos2; edge s; edge_iterator ei; @@ -1629,7 +1766,8 @@ try_crossjump_to_edge (int mode, edge e1, edge e2) return false; /* ... and part the second. */ - nmatch = flow_find_cross_jump (src1, src2, &newpos1, &newpos2); + dir = dir_forward; + nmatch = flow_find_cross_jump (src1, src2, &newpos1, &newpos2, &dir); /* Don't proceed with the crossjump unless we found a sufficient number of matching instructions or the 'from' block was totally matched diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 3e26799..b34aee2 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -481,7 +481,8 @@ cond_exec_process_if_block (ce_if_block_t * ce_info, /* Look for matching sequences at the head and tail of the two blocks, and limit the range of insns to be converted if possible. */ n_matching = flow_find_cross_jump (then_bb, else_bb, - &then_first_tail, &else_first_tail); + &then_first_tail, &else_first_tail, + NULL); if (then_first_tail == BB_HEAD (then_bb)) then_start = then_end = NULL_RTX; if (else_first_tail == BB_HEAD (else_bb)) |