Commit b02eea61 by Richard Henderson Committed by Richard Henderson

flow.c: Grammar check and clarify a lot of comments.

        * flow.c: Grammar check and clarify a lot of comments.
        (try_simplify_condjump): Rename variables to be clearer.
        (try_forward_edges): Skip complex and fallthru edges.
        Rearrange tests to avoid duplicate checks.
        (flow_find_cross_jump): Likewise.
        (outgoing_edges_match): Allow match if neither branch has
        probability data.  Loosen probability match to 5%.
        (try_crossjump_to_edge): Hoist repeated indirection into
        local variables.
        (try_crossjump_bb): Don't check complex edges.  Eliminate
        redundant crossjump tests.
        (try_optimize_cfg): Fix use of bool.  Reorganize cheaper
        checks before more expensive checks.

From-SVN: r44257
parent 18fee3ee
2001-07-22 Richard Henderson <rth@redhat.com> 2001-07-22 Richard Henderson <rth@redhat.com>
* flow.c: Grammar check and clarify a lot of comments.
(try_simplify_condjump): Rename variables to be clearer.
(try_forward_edges): Skip complex and fallthru edges.
Rearrange tests to avoid duplicate checks.
(flow_find_cross_jump): Likewise.
(outgoing_edges_match): Allow match if neither branch has
probability data. Loosen probability match to 5%.
(try_crossjump_to_edge): Hoist repeated indirection into
local variables.
(try_crossjump_bb): Don't check complex edges. Eliminate
redundant crossjump tests.
(try_optimize_cfg): Fix use of bool. Reorganize cheaper
checks before more expensive checks.
2001-07-22 Richard Henderson <rth@redhat.com>
* fold-const.c (fold): Test vs FLOAT_TYPE_P instead of * fold-const.c (fold): Test vs FLOAT_TYPE_P instead of
INTEGRAL_TYPE_P when folding comparisons with operand_equal_p INTEGRAL_TYPE_P when folding comparisons with operand_equal_p
arguments. arguments.
......
...@@ -2963,27 +2963,16 @@ merge_blocks (e, b, c, mode) ...@@ -2963,27 +2963,16 @@ merge_blocks (e, b, c, mode)
int c_has_outgoing_fallthru; int c_has_outgoing_fallthru;
int b_has_incoming_fallthru; int b_has_incoming_fallthru;
/* Avoid overactive code motion, as the forwarder blocks should eb /* Avoid overactive code motion, as the forwarder blocks should be
eliminated by the edge redirection instead. Only exception is the eliminated by edge redirection instead. One exception might have
case b is an forwarder block and c has no fallthru edge, but no been if B is a forwarder block and C has no fallthru edge, but
optimizers should be confused by this extra jump and we are about that should be cleaned up by bb-reorder instead. */
to kill the jump in bb_reorder pass instead. */
if (forwarder_block_p (b) || forwarder_block_p (c)) if (forwarder_block_p (b) || forwarder_block_p (c))
return 0; return 0;
/* We must make sure to not munge nesting of exception regions, /* We must make sure to not munge nesting of lexical blocks,
lexical blocks, and loop notes. and loop notes. This is done by squeezing out all the notes
and leaving them there to lie. Not ideal, but functional. */
The first is taken care of by requiring that the active eh
region at the end of one block always matches the active eh
region at the beginning of the next block.
The later two are taken care of by squeezing out all the notes. */
/* ??? A throw/catch edge (or any abnormal edge) should be rarely
executed and we may want to treat blocks which have two out
edges, one normal, one abnormal as only having one edge for
block merging purposes. */
for (tmp_edge = c->succ; tmp_edge; tmp_edge = tmp_edge->succ_next) for (tmp_edge = c->succ; tmp_edge; tmp_edge = tmp_edge->succ_next)
if (tmp_edge->flags & EDGE_FALLTHRU) if (tmp_edge->flags & EDGE_FALLTHRU)
...@@ -3029,8 +3018,8 @@ merge_blocks (e, b, c, mode) ...@@ -3029,8 +3018,8 @@ merge_blocks (e, b, c, mode)
redirect_edge_succ (c_fallthru_edge, ENTRY_BLOCK_PTR); redirect_edge_succ (c_fallthru_edge, ENTRY_BLOCK_PTR);
new = redirect_edge_and_branch_force (c_fallthru_edge, target); new = redirect_edge_and_branch_force (c_fallthru_edge, target);
/* We've just created barrier, but other barrier is already present /* We've just created barrier, but another barrier is
in the stream. Avoid duplicate. */ already present in the stream. Avoid the duplicate. */
barrier = next_nonnote_insn (new ? new->end : b->end); barrier = next_nonnote_insn (new ? new->end : b->end);
if (GET_CODE (barrier) != BARRIER) if (GET_CODE (barrier) != BARRIER)
abort (); abort ();
...@@ -3042,76 +3031,97 @@ merge_blocks (e, b, c, mode) ...@@ -3042,76 +3031,97 @@ merge_blocks (e, b, c, mode)
return 0; return 0;
} }
/* Simplify conditional jump around an jump. /* Simplify a conditional jump around an unconditional jump.
Return nonzero in case optimization matched. */ Return true if something changed. */
static bool static bool
try_simplify_condjump (src) try_simplify_condjump (cbranch_block)
basic_block src; basic_block cbranch_block;
{ {
basic_block final_block, next_block; basic_block jump_block, jump_dest_block, cbranch_dest_block;
rtx insn = src->end; edge cbranch_jump_edge, cbranch_fallthru_edge;
edge branch, fallthru; rtx cbranch_insn;
/* Verify that there are exactly two successors. */ /* Verify that there are exactly two successors. */
if (!src->succ || !src->succ->succ_next || src->succ->succ_next->succ_next if (!cbranch_block->succ
|| !any_condjump_p (insn)) || !cbranch_block->succ->succ_next
|| cbranch_block->succ->succ_next->succ_next)
return false; return false;
fallthru = FALLTHRU_EDGE (src); /* Verify that we've got a normal conditional branch at the end
of the block. */
/* Following block must be simple forwarder block with single cbranch_insn = cbranch_block->end;
entry and must not be last in the stream. */ if (!any_condjump_p (cbranch_insn))
next_block = fallthru->dest;
if (!forwarder_block_p (next_block)
|| next_block->pred->pred_next
|| next_block->index == n_basic_blocks - 1)
return false; return false;
/* The branch must target to block afterwards. */ cbranch_fallthru_edge = FALLTHRU_EDGE (cbranch_block);
final_block = BASIC_BLOCK (next_block->index + 1); cbranch_jump_edge = BRANCH_EDGE (cbranch_block);
branch = BRANCH_EDGE (src); /* The next block must not have multiple predecessors, must not
be the last block in the function, and must contain just the
unconditional jump. */
jump_block = cbranch_fallthru_edge->dest;
if (jump_block->pred->pred_next
|| jump_block->index == n_basic_blocks - 1
|| !forwarder_block_p (jump_block))
return false;
jump_dest_block = jump_block->succ->dest;
if (branch->dest != final_block) /* The conditional branch must target the block after the
unconditional branch. */
cbranch_dest_block = cbranch_jump_edge->dest;
if (cbranch_dest_block->index != jump_block->index + 1)
return false; return false;
/* Avoid jump.c from being overactive on removin ureachable insns. */ /* Invert the conditional branch. Prevent jump.c from deleting
LABEL_NUSES (JUMP_LABEL (insn))++; "unreachable" instructions. */
if (!invert_jump (insn, block_label (next_block->succ->dest), 1)) LABEL_NUSES (JUMP_LABEL (cbranch_insn))++;
if (!invert_jump (cbranch_insn, block_label (jump_dest_block), 1))
{ {
LABEL_NUSES (JUMP_LABEL (insn))--; LABEL_NUSES (JUMP_LABEL (cbranch_insn))--;
return false; return false;
} }
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "Simplifying condjump %i around jump %i\n", fprintf (rtl_dump_file, "Simplifying condjump %i around jump %i\n",
INSN_UID (insn), INSN_UID (next_block->end)); INSN_UID (cbranch_insn), INSN_UID (jump_block->end));
redirect_edge_succ (branch, final_block);
redirect_edge_succ (fallthru, next_block->succ->dest);
branch->flags |= EDGE_FALLTHRU; /* Success. Update the CFG to match. */
fallthru->flags &= ~EDGE_FALLTHRU; redirect_edge_succ (cbranch_jump_edge, cbranch_dest_block);
redirect_edge_succ (cbranch_fallthru_edge, jump_dest_block);
cbranch_jump_edge->flags |= EDGE_FALLTHRU;
cbranch_fallthru_edge->flags &= ~EDGE_FALLTHRU;
flow_delete_block (next_block); flow_delete_block (jump_block);
return true; return true;
} }
/* Attempt to forward edges leaving basic block B. /* Attempt to forward edges leaving basic block B.
Return nonzero if sucessfull. */ Return true if sucessful. */
static bool static bool
try_forward_edges (b) try_forward_edges (b)
basic_block b; basic_block b;
{ {
bool changed = 0; bool changed = false;
edge e; edge e, next;
for (e = b->succ; e; e = e->succ_next)
for (e = b->succ; e ; e = next)
{ {
basic_block target = e->dest, first = e->dest; basic_block target, first;
int counter = 0; int counter;
next = e->succ_next;
/* Skip complex edges because we don't know how to update them.
Skip fallthru edges because there's no jump to update. */
if (e->flags & (EDGE_COMPLEX | EDGE_FALLTHRU))
continue;
/* Look for the real destination of jump. target = first = e->dest;
counter = 0;
/* Look for the real destination of the jump.
Avoid inifinite loop in the infinite empty loop by counting Avoid inifinite loop in the infinite empty loop by counting
up to n_basic_blocks. */ up to n_basic_blocks. */
while (forwarder_block_p (target) while (forwarder_block_p (target)
...@@ -3124,10 +3134,20 @@ try_forward_edges (b) ...@@ -3124,10 +3134,20 @@ try_forward_edges (b)
target = target->succ->dest, counter++; target = target->succ->dest, counter++;
} }
if (target != first && counter < n_basic_blocks if (counter >= n_basic_blocks)
&& redirect_edge_and_branch (e, target))
{ {
while (first != target) if (rtl_dump_file)
fprintf (rtl_dump_file, "Infinite loop in BB %i.\n",
target->index);
}
else if (target == first)
; /* We didn't do anything. */
else if (redirect_edge_and_branch (e, target))
{
/* We successfully forwarded the edge. Now update profile
data: for each edge we traversed in the chain, remove
the original edge's execution count. */
do
{ {
first->count -= e->count; first->count -= e->count;
first->succ->count -= e->count; first->succ->count -= e->count;
...@@ -3136,59 +3156,61 @@ try_forward_edges (b) ...@@ -3136,59 +3156,61 @@ try_forward_edges (b)
/ REG_BR_PROB_BASE); / REG_BR_PROB_BASE);
first = first->succ->dest; first = first->succ->dest;
} }
/* We've possibly removed the edge. */ while (first != target);
changed = 1;
e = b->succ; changed = true;
} }
else if (rtl_dump_file && counter == n_basic_blocks) else
fprintf (rtl_dump_file, "Infinite loop in BB %i.\n", target->index); {
else if (rtl_dump_file && first != target) if (rtl_dump_file)
fprintf (rtl_dump_file, fprintf (rtl_dump_file, "Forwarding edge %i->%i to %i failed.\n",
"Forwarding edge %i->%i to %i failed.\n", b->index, b->index, e->dest->index, target->index);
e->dest->index, target->index);
} }
}
return changed; return changed;
} }
/* Compare the instructions before end of B1 and B2 /* Look through the insns at the end of BB1 and BB2 and find the longest
to find an opportunity for cross jumping. sequence that are equivalent. Store the first insns for that sequence
(This means detecting identical sequences of insns) in *F1 and *F2 and return the sequence length.
Find the longest possible equivalent sequences
and store the first insns of those sequences into *F1 and *F2
and return length of that sequence.
To simplify callers of this function, in the To simplify callers of this function, if the blocks match exactly,
all instructions were matched, allways store bb->head. */ store the head of the blocks in *F1 and *F2. */
static int static int
flow_find_cross_jump (mode, bb1, bb2, f1, f2) flow_find_cross_jump (mode, bb1, bb2, f1, f2)
int mode; int mode ATTRIBUTE_UNUSED;
basic_block bb1, bb2; basic_block bb1, bb2;
rtx *f1, *f2; rtx *f1, *f2;
{ {
rtx i1 = onlyjump_p (bb1->end) ? PREV_INSN (bb1->end): bb1->end; rtx i1, i2, p1, p2, last1, last2, afterlast1, afterlast2;
rtx i2 = onlyjump_p (bb2->end) ? PREV_INSN (bb2->end): bb2->end;
rtx p1, p2;
int lose = 0;
int ninsns = 0; int ninsns = 0;
rtx last1 = bb1->end, last2 = bb2->end;
rtx afterlast1 = bb1->end, afterlast2 = bb2->end;
/* In case basic block ends by nontrivial jump instruction, count it as
an instruction. Do not count an unconditional jump, as it will be
removed by basic_block reordering pass in case it is on the common
path. */
if (bb1->succ->succ_next && bb1->end != i1)
ninsns++;
for (;i1 != bb1->head; i1 = PREV_INSN (i1)) /* Skip simple jumps at the end of the blocks. Complex jumps still
need to be compared for equivalence, which we'll do below. */
i1 = bb1->end;
if (onlyjump_p (i1))
i1 = PREV_INSN (i1);
i2 = bb2->end;
if (onlyjump_p (i2))
i2 = PREV_INSN (i2);
last1 = afterlast1 = last2 = afterlast2 = NULL_RTX;
while (true)
{ {
/* Ignore notes. */ /* Ignore notes. */
if (GET_CODE (i1) == NOTE) while ((GET_CODE (i1) == NOTE && i1 != bb1->head))
continue; i1 = PREV_INSN (i1);
while ((GET_CODE (i2) == NOTE && i2 != bb2->head)) while ((GET_CODE (i2) == NOTE && i2 != bb2->head))
i2 = PREV_INSN (i2); i2 = PREV_INSN (i2);
if (i1 == bb1->head || i2 == bb2->head)
break;
/* Verify that I1 and I2 are equivalent. */
if (GET_CODE (i1) != GET_CODE (i2)) if (GET_CODE (i1) != GET_CODE (i2))
break; break;
...@@ -3208,14 +3230,14 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2) ...@@ -3208,14 +3230,14 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2)
if (GET_CODE (i1) == CALL_INSN if (GET_CODE (i1) == CALL_INSN
&& ! rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1), && ! rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1),
CALL_INSN_FUNCTION_USAGE (i2))) CALL_INSN_FUNCTION_USAGE (i2)))
lose = 1; break;
#ifdef STACK_REGS #ifdef STACK_REGS
/* If cross_jump_death_matters is not 0, the insn's mode /* If cross_jump_death_matters is not 0, the insn's mode
indicates whether or not the insn contains any stack-like indicates whether or not the insn contains any stack-like
regs. */ regs. */
if (!lose && (mode & CLEANUP_POST_REGSTACK ) && stack_regs_mentioned (i1)) if ((mode & CLEANUP_POST_REGSTACK) && stack_regs_mentioned (i1))
{ {
/* If register stack conversion has already been done, then /* If register stack conversion has already been done, then
death notes must also be compared before it is certain that death notes must also be compared before it is certain that
...@@ -3239,25 +3261,23 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2) ...@@ -3239,25 +3261,23 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2)
GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done); GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done);
lose = 1; break;
done: done:
; ;
} }
#endif #endif
if (lose || GET_CODE (p1) != GET_CODE (p2) if (GET_CODE (p1) != GET_CODE (p2))
|| ! rtx_renumbered_equal_p (p1, p2)) break;
if (! rtx_renumbered_equal_p (p1, p2))
{ {
/* The following code helps take care of G++ cleanups. */ /* The following code helps take care of G++ cleanups. */
rtx equiv1; rtx equiv1 = find_reg_equal_equiv_note (i1);
rtx equiv2; rtx equiv2 = find_reg_equal_equiv_note (i2);
if (!lose && GET_CODE (p1) == GET_CODE (p2) if (equiv1 && equiv2
&& ((equiv1 = find_reg_note (i1, REG_EQUAL, NULL_RTX)) != 0
|| (equiv1 = find_reg_note (i1, REG_EQUIV, NULL_RTX)) != 0)
&& ((equiv2 = find_reg_note (i2, REG_EQUAL, NULL_RTX)) != 0
|| (equiv2 = find_reg_note (i2, REG_EQUIV, NULL_RTX)) != 0)
/* If the equivalences are not to a constant, they may /* If the equivalences are not to a constant, they may
reference pseudos that no longer exist, so we can't reference pseudos that no longer exist, so we can't
use them. */ use them. */
...@@ -3277,36 +3297,36 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2) ...@@ -3277,36 +3297,36 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2)
goto win; goto win;
} }
} }
/* Insns fail to match; cross jumping is limited to the following
insns. */
#ifdef HAVE_cc0
/* Don't allow the insn after a compare to be shared by
cross-jumping unless the compare is also shared.
Here, if either of these non-matching insns is a compare,
exclude the following insn from possible cross-jumping. */
if (sets_cc0_p (p1) || sets_cc0_p (p2))
last1 = afterlast1, last2 = afterlast2, ninsns--;
#endif
break; break;
} }
win: win:
/* Don't begin a cross-jump with a USE or CLOBBER insn. */
if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER) if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER)
{ {
/* Ok, this insn is potentially includable in a cross-jump here. */
afterlast1 = last1, afterlast2 = last2; afterlast1 = last1, afterlast2 = last2;
last1 = i1, last2 = i2; last1 = i1, last2 = i2;
ninsns++; ninsns++;
} }
i1 = PREV_INSN (i1);
if (i2 == bb2->end)
break;
i2 = PREV_INSN (i2); i2 = PREV_INSN (i2);
} }
/* Skip the notes to reach potential head of basic block. */ #ifdef HAVE_cc0
if (ninsns)
{
/* Don't allow the insn after a compare to be shared by
cross-jumping unless the compare is also shared. */
if (reg_mentioned_p (cc0_rtx, last1) && ! sets_cc0_p (last1))
last1 = afterlast1, last2 = afterlast2, ninsns--;
}
#endif
/* Include preceeding notes and labels in the cross-jump. One,
this may bring us to the head of the blocks as requested above.
Two, it keeps line number notes as matched as may be. */
if (ninsns)
{
while (last1 != bb1->head && GET_CODE (PREV_INSN (last1)) == NOTE) while (last1 != bb1->head && GET_CODE (PREV_INSN (last1)) == NOTE)
last1 = PREV_INSN (last1); last1 = PREV_INSN (last1);
if (last1 != bb1->head && GET_CODE (PREV_INSN (last1)) == CODE_LABEL) if (last1 != bb1->head && GET_CODE (PREV_INSN (last1)) == CODE_LABEL)
...@@ -3318,6 +3338,8 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2) ...@@ -3318,6 +3338,8 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2)
*f1 = last1; *f1 = last1;
*f2 = last2; *f2 = last2;
}
return ninsns; return ninsns;
} }
...@@ -3325,20 +3347,24 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2) ...@@ -3325,20 +3347,24 @@ flow_find_cross_jump (mode, bb1, bb2, f1, f2)
the branch instruction. This means that if we commonize the control the branch instruction. This means that if we commonize the control
flow before end of the basic block, the semantic remains unchanged. flow before end of the basic block, the semantic remains unchanged.
Assume that at least one outgoing edge is forwarded to the same We may assume that there exists one edge with a common destination. */
location. */
static bool static bool
outgoing_edges_match (bb1, bb2) outgoing_edges_match (bb1, bb2)
basic_block bb1; basic_block bb1;
basic_block bb2; basic_block bb2;
{ {
/* bb1 has one succesor, so we are seeing unconditional jump. */ /* If BB1 has only one successor, we must be looking at an unconditional
jump. Which, by the assumption above, means that we only need to check
that BB2 has one successor. */
if (bb1->succ && !bb1->succ->succ_next) if (bb1->succ && !bb1->succ->succ_next)
return (bb2->succ && !bb2->succ->succ_next); return (bb2->succ && !bb2->succ->succ_next);
/* Match conditional jumps - this may get tricky when fallthru and branch /* Match conditional jumps - this may get tricky when fallthru and branch
edges are crossed. */ edges are crossed. */
if (bb1->succ && bb1->succ->succ_next && !bb1->succ->succ_next->succ_next if (bb1->succ
&& bb1->succ->succ_next
&& !bb1->succ->succ_next->succ_next
&& any_condjump_p (bb1->end)) && any_condjump_p (bb1->end))
{ {
edge b1, f1, b2, f2; edge b1, f1, b2, f2;
...@@ -3346,9 +3372,12 @@ outgoing_edges_match (bb1, bb2) ...@@ -3346,9 +3372,12 @@ outgoing_edges_match (bb1, bb2)
rtx set1, set2, cond1, cond2; rtx set1, set2, cond1, cond2;
enum rtx_code code1, code2; enum rtx_code code1, code2;
if (!bb2->succ || !bb2->succ->succ_next if (!bb2->succ
|| bb1->succ->succ_next->succ_next || !any_condjump_p (bb2->end)) || !bb2->succ->succ_next
|| bb1->succ->succ_next->succ_next
|| !any_condjump_p (bb2->end))
return false; return false;
b1 = BRANCH_EDGE (bb1); b1 = BRANCH_EDGE (bb1);
b2 = BRANCH_EDGE (bb2); b2 = BRANCH_EDGE (bb2);
f1 = FALLTHRU_EDGE (bb1); f1 = FALLTHRU_EDGE (bb1);
...@@ -3390,11 +3419,10 @@ outgoing_edges_match (bb1, bb2) ...@@ -3390,11 +3419,10 @@ outgoing_edges_match (bb1, bb2)
code2 = reversed_comparison_code (cond2, bb2->end); code2 = reversed_comparison_code (cond2, bb2->end);
else else
code2 = GET_CODE (cond2); code2 = GET_CODE (cond2);
if (code2 == UNKNOWN) if (code2 == UNKNOWN)
return false; return false;
/* See if we don have (cross) match in the codes and operands. */ /* Verify codes and operands match. */
match = ((code1 == code2 match = ((code1 == code2
&& rtx_renumbered_equal_p (XEXP (cond1, 0), XEXP (cond2, 0)) && rtx_renumbered_equal_p (XEXP (cond1, 0), XEXP (cond2, 0))
&& rtx_renumbered_equal_p (XEXP (cond1, 1), XEXP (cond2, 1))) && rtx_renumbered_equal_p (XEXP (cond1, 1), XEXP (cond2, 1)))
...@@ -3403,71 +3431,90 @@ outgoing_edges_match (bb1, bb2) ...@@ -3403,71 +3431,90 @@ outgoing_edges_match (bb1, bb2)
XEXP (cond2, 0)) XEXP (cond2, 0))
&& rtx_renumbered_equal_p (XEXP (cond1, 0), && rtx_renumbered_equal_p (XEXP (cond1, 0),
XEXP (cond2, 1)))); XEXP (cond2, 1))));
/* In case of returning true, we will commonize the flow.
This also means, that both branches will contain only single /* If we return true, we will join the blocks. Which means that
branch prediction algorithm. To match require resulting branch we will only have one branch prediction bit to work with. Thus
to be still well predictable. */ we require the existing branches to have probabilities that are
roughly similar. */
/* ??? We should use bb->frequency to allow merging in infrequently
executed blocks, but at the moment it is not available when
cleanup_cfg is run. */
if (match && !optimize_size) if (match && !optimize_size)
{ {
rtx note1, note2; rtx note1, note2;
int prob1, prob2; int prob1, prob2;
note1 = find_reg_note (bb1->end, REG_BR_PROB, 0); note1 = find_reg_note (bb1->end, REG_BR_PROB, 0);
note2 = find_reg_note (bb2->end, REG_BR_PROB, 0); note2 = find_reg_note (bb2->end, REG_BR_PROB, 0);
if (!note1 || !note2)
return false; if (note1 && note2)
{
prob1 = INTVAL (XEXP (note1, 0)); prob1 = INTVAL (XEXP (note1, 0));
prob2 = INTVAL (XEXP (note2, 0)); prob2 = INTVAL (XEXP (note2, 0));
if (reverse) if (reverse)
prob2 = REG_BR_PROB_BASE - prob2; prob2 = REG_BR_PROB_BASE - prob2;
/* ??? Later we should use basic block frequency to allow merging /* Fail if the difference in probabilities is
in the infrequent blocks, but at the moment it is not greater than 5%. */
available when cleanup_cfg is run. */ if (abs (prob1 - prob2) > REG_BR_PROB_BASE / 20)
if (abs (prob1 - prob2) > REG_BR_PROB_BASE / 90)
return false; return false;
} }
else if (note1 || note2)
return false;
}
if (rtl_dump_file && match) if (rtl_dump_file && match)
fprintf (rtl_dump_file, "Conditionals in bb %i and %i match.\n", fprintf (rtl_dump_file, "Conditionals in bb %i and %i match.\n",
bb1->index, bb2->index); bb1->index, bb2->index);
return match; return match;
} }
/* ??? We can handle computed jumps too. This may be important for /* ??? We can handle computed jumps too. This may be important for
inlined functions containing switch statements. Also jumps w/o inlined functions containing switch statements. Also jumps w/o
fallthru edges can be handled by simply matching whole insn. */ fallthru edges can be handled by simply matching whole insn. */
return false; return false;
} }
/* Assume that e1 and e2 are the edges from the same basic block. /* E1 and E2 are edges with the same destination block. Search their
Attempt to find common code on both paths and forward control flow predecessors for common code. If found, redirect control flow from
from the first path to second if such exist. */ (maybe the middle of) E1->SRC to (maybe the middle of) E2->SRC. */
static bool static bool
try_crossjump_to_edge (mode, e1, e2) try_crossjump_to_edge (mode, e1, e2)
int mode; int mode;
edge e1, e2; edge e1, e2;
{ {
int nmatch; int nmatch;
basic_block src1 = e1->src, src2 = e2->src;
basic_block redirect_to; basic_block redirect_to;
rtx newpos1, newpos2; rtx newpos1, newpos2;
rtx first, last;
edge s; edge s;
rtx last;
rtx label; rtx label;
rtx barrier;
/* Skip forwarder blocks. This is needed to avoid forced forwarders
after conditional jumps from making us to miss optimization.
We don't need to worry about multiple entry or chained forwarders, as they /* Search backward through forwarder blocks. We don't need to worry
will be optimized out. */ about multiple entry or chained forwarders, as they will be optimized
if (e1->src->pred && !e1->src->pred->pred_next away. We do this to look past the unconditional jump following a
&& forwarder_block_p (e1->src)) conditional jump that is required due to the current CFG shape. */
e1 = e1->src->pred; if (src1->pred
if (e2->src->pred && !e2->src->pred->pred_next && !src1->pred->pred_next
&& forwarder_block_p (e2->src)) && forwarder_block_p (src1))
e2 = e2->src->pred; {
e1 = src1->pred;
src1 = e1->src;
}
if (src2->pred
&& !src2->pred->pred_next
&& forwarder_block_p (src2))
{
e2 = src2->pred;
src2 = e2->src;
}
if (e1->src == ENTRY_BLOCK_PTR || e2->src == ENTRY_BLOCK_PTR) /* Nothing to do if we reach ENTRY, or a common source block. */
if (src1 == ENTRY_BLOCK_PTR || src2 == ENTRY_BLOCK_PTR)
return false; return false;
if (e1->src == e2->src) if (src1 == src2)
return false; return false;
/* Seeing more than 1 forwarder blocks would confuse us later... */ /* Seeing more than 1 forwarder blocks would confuse us later... */
...@@ -3477,55 +3524,66 @@ try_crossjump_to_edge (mode, e1, e2) ...@@ -3477,55 +3524,66 @@ try_crossjump_to_edge (mode, e1, e2)
if (forwarder_block_p (e2->dest) if (forwarder_block_p (e2->dest)
&& forwarder_block_p (e2->dest->succ->dest)) && forwarder_block_p (e2->dest->succ->dest))
return false; return false;
/* ... similary as seeing dead code... */
if (!e1->src->pred || !e2->src->pred) /* Likewise with dead code. */
/* ??? Won't we have eliminated these by now? */
if (!src1->pred || !src2->pred)
return false; return false;
/* ...similary non-jump edges. */
/* Likewise with non-jump edges. */
/* ??? Non-jump? You mean GET_CODE (e1->src-end) != JUMP_INSN?
This fails for computed-goto as well, which may in fact be joinable. */
if (e1->flags & EDGE_COMPLEX) if (e1->flags & EDGE_COMPLEX)
return false; return false;
if (!outgoing_edges_match (e1->src, e2->src)) /* Look for the common insn sequence, part the first ... */
if (!outgoing_edges_match (src1, src2))
return false; return false;
nmatch = flow_find_cross_jump (mode, e1->src, e2->src, &newpos1, &newpos2);
/* ... and part the second. */
nmatch = flow_find_cross_jump (mode, src1, src2, &newpos1, &newpos2);
if (!nmatch) if (!nmatch)
return false; return false;
/* Avoid splitting if possible. */ /* Avoid splitting if possible. */
if (newpos2 == e2->src->head) if (newpos2 == src2->head)
redirect_to = e2->src; redirect_to = src2;
else else
{ {
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "Splitting bb %i before %i insns\n", fprintf (rtl_dump_file, "Splitting bb %i before %i insns\n",
e2->src->index, nmatch); src2->index, nmatch);
redirect_to = split_block (e2->src, PREV_INSN (newpos2))->dest; redirect_to = split_block (src2, PREV_INSN (newpos2))->dest;
} }
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, fprintf (rtl_dump_file,
"Cross jumping from bb %i to bb %i. %i insn commoized\n", "Cross jumping from bb %i to bb %i; %i common insns\n",
e1->src->index, e2->src->index, nmatch); src1->index, src2->index, nmatch);
redirect_to->count += e1->src->count; redirect_to->count += src1->count;
redirect_to->frequency += e1->src->frequency; redirect_to->frequency += src1->frequency;
/* Recompute the frequencies and counts of outgoing edges. */ /* Recompute the frequencies and counts of outgoing edges. */
for (s = redirect_to->succ; s; s = s->succ_next) for (s = redirect_to->succ; s; s = s->succ_next)
{ {
edge s2; edge s2;
basic_block d = (forwarder_block_p (s->dest) ? s->dest->succ->dest basic_block d = s->dest;
: s->dest);
for (s2 = e1->src->succ;; s2 = s2->succ_next) if (forwarder_block_p (d))
d = d->succ->dest;
for (s2 = src1->succ; ; s2 = s2->succ_next)
{ {
basic_block d2 = basic_block d2 = s2->dest;
(forwarder_block_p (s2->dest) ? s2->dest->succ->dest : s2->dest); if (forwarder_block_p (d2))
d2 = d2->succ->dest;
if (d == d2) if (d == d2)
break; break;
} }
s->count += s2->count; s->count += s2->count;
/* Take care to update possible forwarder blocks. We took care /* Take care to update possible forwarder blocks. We verified
that there is no more than one in chain, so we can't run that there is no more than one in the chain, so we can't run
into infinite loop. */ into infinite loop. */
if (forwarder_block_p (s->dest)) if (forwarder_block_p (s->dest))
{ {
...@@ -3541,116 +3599,164 @@ try_crossjump_to_edge (mode, e1, e2) ...@@ -3541,116 +3599,164 @@ try_crossjump_to_edge (mode, e1, e2)
s2->dest->frequency -= ((s->probability * s->src->frequency) s2->dest->frequency -= ((s->probability * s->src->frequency)
/ REG_BR_PROB_BASE); / REG_BR_PROB_BASE);
} }
if (!redirect_to->frequency && !e1->src->frequency) if (!redirect_to->frequency && !src1->frequency)
s->probability = (s->probability + s2->probability) / 2; s->probability = (s->probability + s2->probability) / 2;
else else
s->probability = s->probability =
((s->probability * redirect_to->frequency + ((s->probability * redirect_to->frequency +
s2->probability * e1->src->frequency) s2->probability * src1->frequency)
/ (redirect_to->frequency + e1->src->frequency)); / (redirect_to->frequency + src1->frequency));
} }
/* FIXME: enable once probabilities are fetched properly at /* FIXME: enable once probabilities are fetched properly at CFG build. */
CFG build. */
#if 0 #if 0
note = find_reg_note (redirect_to->end, REG_BR_PROB, 0); note = find_reg_note (redirect_to->end, REG_BR_PROB, 0);
if (note) if (note)
XEXP (note, 0) = GEN_INT (BRANCH_EDGE (redirect_to)->probability); XEXP (note, 0) = GEN_INT (BRANCH_EDGE (redirect_to)->probability);
#endif #endif
/* Skip possible basic block header. */ /* Edit SRC1 to go to REDIRECT_TO at NEWPOS1. */
first = newpos1;
if (GET_CODE (first) == CODE_LABEL)
first = NEXT_INSN (first);
if (GET_CODE (first) == NOTE)
first = NEXT_INSN (first);
last = e1->src->end; /* Skip possible basic block header. */
if (GET_CODE (newpos1) == CODE_LABEL)
newpos1 = NEXT_INSN (newpos1);
if (GET_CODE (newpos1) == NOTE)
newpos1 = NEXT_INSN (newpos1);
last = src1->end;
/* Now emit the jump insn. */ /* Emit the jump insn. */
label = block_label (redirect_to); label = block_label (redirect_to);
e1->src->end = emit_jump_insn_after (gen_jump (label), e1->src->end); src1->end = emit_jump_insn_before (gen_jump (label), newpos1);
JUMP_LABEL (e1->src->end) = label; JUMP_LABEL (src1->end) = label;
LABEL_NUSES (label)++; LABEL_NUSES (label)++;
if (basic_block_for_insn) if (basic_block_for_insn)
set_block_for_new_insns (e1->src->end, e1->src); set_block_for_new_insns (src1->end, src1);
flow_delete_insn_chain (first, last); /* Delete the now unreachable instructions. */
flow_delete_insn_chain (newpos1, last);
barrier = next_nonnote_insn (e1->src->end); /* Make sure there is a barrier after the new jump. */
if (!barrier || GET_CODE (barrier) != BARRIER) last = next_nonnote_insn (src1->end);
emit_barrier_after (e1->src->end); if (!last || GET_CODE (last) != BARRIER)
emit_barrier_after (src1->end);
/* Update CFG. */ /* Update CFG. */
while (e1->src->succ->succ_next) while (src1->succ)
remove_edge (e1->src->succ); remove_edge (src1->succ);
e1->src->succ->flags = 0; make_edge (NULL, src1, redirect_to, 0);
redirect_edge_succ (e1->src->succ, redirect_to);
return true; return true;
} }
/* Attempt to implement cross jumping. This means moving one or more branches /* Search the predecessors of BB for common insn sequences. When found,
to BB earlier to BB predecesors commonizing some code. */ share code between them by redirecting control flow. Return true if
any changes made. */
static bool static bool
try_crossjump_bb (mode, bb) try_crossjump_bb (mode, bb)
int mode; int mode;
basic_block bb; basic_block bb;
{ {
edge e, e2, nexte2, nexte, fallthru; edge e, e2, nexte2, nexte, fallthru;
bool changed = false; bool changed;
/* In case basic block has single predecesor, do nothing. */ /* Nothing to do if there is not at least two incomming edges. */
if (!bb->pred || !bb->pred->pred_next) if (!bb->pred || !bb->pred->pred_next)
return false; return false;
/* It is always cheapest to jump into fallthru edge. */ /* It is always cheapest to redirect a block that ends in a branch to
a block that falls through into BB, as that adds no branches to the
program. We'll try that combination first. */
for (fallthru = bb->pred; fallthru; fallthru = fallthru->pred_next) for (fallthru = bb->pred; fallthru; fallthru = fallthru->pred_next)
if (fallthru->flags & EDGE_FALLTHRU) if (fallthru->flags & EDGE_FALLTHRU)
break; break;
changed = false;
for (e = bb->pred; e; e = nexte) for (e = bb->pred; e; e = nexte)
{ {
nexte = e->pred_next; nexte = e->pred_next;
/* First of all prioritize the fallthru edge, as the cheapest. */
if (e != fallthru && fallthru
&& try_crossjump_to_edge (mode, e, fallthru))
changed = true, nexte = bb->pred;
else
/* Try match in other incomming edges.
Loop only over the earlier edges to avoid,as the later /* Elide complex edges now, as neither try_crossjump_to_edge
will be examined in the oposite direction. */ nor outgoing_edges_match can handle them. */
for (e2 = bb->pred; e2 != e; e2 = nexte2) if (e->flags & EDGE_COMPLEX)
continue;
/* As noted above, first try with the fallthru predecessor. */
if (fallthru)
{ {
nexte2 = e2->pred_next; /* Don't combine the fallthru edge into anything else.
if (e2 != fallthru && try_crossjump_to_edge (mode, e, e2)) If there is a match, we'll do it the other way around. */
if (e == fallthru)
continue;
if (try_crossjump_to_edge (mode, e, fallthru))
{ {
changed = true; changed = true;
nexte = bb->pred; nexte = bb->pred;
continue;
}
}
/* We may've removed the fallthru edge. */ /* Non-obvious work limiting check: Recognize that we're going
for (fallthru = bb->pred; fallthru; to call try_crossjump_bb on every basic block. So if we have
fallthru = fallthru->pred_next) two blocks with lots of outgoing edges (a switch) and they
if (fallthru->flags & EDGE_FALLTHRU) share lots of common destinations, then we would do the
break; cross-jump check once for each common destination.
Now, if the blocks actually are cross-jump candidates, then
all of their destinations will be shared. Which means that
we only need check them for cross-jump candidacy once. We
can eliminate redundant checks of crossjump(A,B) by arbitrarily
choosing to do the check from the block for which the edge
in question is the first successor of A. */
if (e->src->succ != e)
continue;
for (e2 = bb->pred; e2; e2 = nexte2)
{
nexte2 = e2->pred_next;
if (e2 == e)
continue;
/* We've already checked the fallthru edge above. */
if (e2 == fallthru)
continue;
/* Again, neither try_crossjump_to_edge nor outgoing_edges_match
can handle complex edges. */
if (e2->flags & EDGE_COMPLEX)
continue;
/* The "first successor" check above only prevents multiple
checks of crossjump(A,B). In order to prevent redundant
checks of crossjump(B,A), require that A be the block
with the lowest index. */
/* ??? Perhaps better is lowest execution frequency. */
if (e->src->index > e2->src->index)
continue;
if (try_crossjump_to_edge (mode, e, e2))
{
changed = true;
nexte = bb->pred;
break; break;
} }
} }
} }
return changed; return changed;
} }
/* Do simple CFG optimizations - basic block merging, simplifying of jump /* Do simple CFG optimizations - basic block merging, simplifying of jump
instructions etc. instructions etc. Return nonzero if changes were made. */
Return nonzero in case some optimizations matched. */
static bool static bool
try_optimize_cfg (mode) try_optimize_cfg (mode)
int mode; int mode;
{ {
int i; int i;
bool changed_overall = 0; bool changed_overall = false;
bool changed; bool changed;
int iterations = 0; int iterations = 0;
...@@ -3660,16 +3766,18 @@ try_optimize_cfg (mode) ...@@ -3660,16 +3766,18 @@ try_optimize_cfg (mode)
do do
{ {
changed = 0; changed = false;
iterations++; iterations++;
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "\n\ntry_optimize_cfg iteration %i\n\n", fprintf (rtl_dump_file, "\n\ntry_optimize_cfg iteration %i\n\n",
iterations); iterations);
for (i = 0; i < n_basic_blocks;) for (i = 0; i < n_basic_blocks;)
{ {
basic_block c, b = BASIC_BLOCK (i); basic_block c, b = BASIC_BLOCK (i);
edge s; edge s;
int changed_here = 0; bool changed_here = false;
/* Delete trivially dead basic blocks. */ /* Delete trivially dead basic blocks. */
while (b->pred == NULL) while (b->pred == NULL)
...@@ -3678,19 +3786,20 @@ try_optimize_cfg (mode) ...@@ -3678,19 +3786,20 @@ try_optimize_cfg (mode)
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "Deleting block %i.\n", b->index); fprintf (rtl_dump_file, "Deleting block %i.\n", b->index);
flow_delete_block (b); flow_delete_block (b);
changed = 1; changed = true;
b = c; b = c;
} }
/* Remove code labels no longer used.
Don't do the optimization before sibling calls are discovered, /* Remove code labels no longer used. Don't do this before
as some branches may be hidden inside CALL_PLACEHOLDERs. */ CALL_PLACEHOLDER is removed, as some branches may be hidden
within. */
if (b->pred->pred_next == NULL if (b->pred->pred_next == NULL
&& (b->pred->flags & EDGE_FALLTHRU) && (b->pred->flags & EDGE_FALLTHRU)
&& !(b->pred->flags & EDGE_COMPLEX) && !(b->pred->flags & EDGE_COMPLEX)
&& GET_CODE (b->head) == CODE_LABEL && GET_CODE (b->head) == CODE_LABEL
&& (!(mode & CLEANUP_PRE_SIBCALL) && (!(mode & CLEANUP_PRE_SIBCALL)
|| !tail_recursion_label_p (b->head)) || !tail_recursion_label_p (b->head))
/* If previous block does end with condjump jumping to next BB, /* If previous block ends with condjump jumping to next BB,
we can't delete the label. */ we can't delete the label. */
&& (b->pred->src == ENTRY_BLOCK_PTR && (b->pred->src == ENTRY_BLOCK_PTR
|| !reg_mentioned_p (b->head, b->pred->src->end))) || !reg_mentioned_p (b->head, b->pred->src->end)))
...@@ -3702,75 +3811,82 @@ try_optimize_cfg (mode) ...@@ -3702,75 +3811,82 @@ try_optimize_cfg (mode)
fprintf (rtl_dump_file, "Deleted label in block %i.\n", fprintf (rtl_dump_file, "Deleted label in block %i.\n",
b->index); b->index);
} }
/* The fallthru forwarder block can be deleted. */
/* If we fall through an empty block, we can remove it. */
if (b->pred->pred_next == NULL if (b->pred->pred_next == NULL
&& forwarder_block_p (b)
&& n_basic_blocks > 1
&& (b->pred->flags & EDGE_FALLTHRU) && (b->pred->flags & EDGE_FALLTHRU)
&& GET_CODE (b->head) != CODE_LABEL
&& forwarder_block_p (b)
/* Note that forwarder_block_p true ensures that there
is a successor for this block. */
&& (b->succ->flags & EDGE_FALLTHRU) && (b->succ->flags & EDGE_FALLTHRU)
&& GET_CODE (b->head) != CODE_LABEL) && n_basic_blocks > 1)
{ {
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "Deleting fallthru block %i.\n", fprintf (rtl_dump_file, "Deleting fallthru block %i.\n",
b->index); b->index);
c = BASIC_BLOCK (i ? i - 1 : i + 1); c = BASIC_BLOCK (b->index ? b->index - 1 : 1);
redirect_edge_succ (b->pred, b->succ->dest); redirect_edge_succ (b->pred, b->succ->dest);
flow_delete_block (b); flow_delete_block (b);
changed = 1; changed = true;
b = c; b = c;
} }
/* Merge blocks. Loop because chains of blocks might be
/* A loop because chains of blocks might be combineable. */ combineable. */
while ((s = b->succ) != NULL while ((s = b->succ) != NULL
&& s->succ_next == NULL && s->succ_next == NULL
&& (s->flags & EDGE_EH) == 0
&& (c = s->dest) != EXIT_BLOCK_PTR
&& !(s->flags & EDGE_COMPLEX) && !(s->flags & EDGE_COMPLEX)
&& (c = s->dest) != EXIT_BLOCK_PTR
&& c->pred->pred_next == NULL && c->pred->pred_next == NULL
/* If the jump insn has side effects, /* If the jump insn has side effects,
we can't kill the edge. */ we can't kill the edge. */
&& (GET_CODE (b->end) != JUMP_INSN && (GET_CODE (b->end) != JUMP_INSN
|| onlyjump_p (b->end)) && merge_blocks (s, b, c, mode)) || onlyjump_p (b->end))
changed_here = 1; && merge_blocks (s, b, c, mode))
changed_here = true;
/* Simplify branch over branch. */
if ((mode & CLEANUP_EXPENSIVE) && try_simplify_condjump (b)) if ((mode & CLEANUP_EXPENSIVE) && try_simplify_condjump (b))
changed_here = 1; changed_here = true;
/* In the case basic blocks has single outgoing edge, but over by the
non-trivial jump instruction, we can replace it by unconditional
jump, or delete the jump completely. Use logic of
redirect_edge_and_branch to do the dirty job for us.
We match cases as conditional jumps jumping to the next block or
dispatch tables. */
/* If B has a single outgoing edge, but uses a non-trivial jump
instruction without side-effects, we can either delete the
jump entirely, or replace it with a simple unconditional jump.
Use redirect_edge_and_branch to do the dirty work. */
if (b->succ if (b->succ
&& b->succ->succ_next == NULL && ! b->succ->succ_next
&& GET_CODE (b->end) == JUMP_INSN
&& b->succ->dest != EXIT_BLOCK_PTR && b->succ->dest != EXIT_BLOCK_PTR
&& onlyjump_p (b->end)
&& redirect_edge_and_branch (b->succ, b->succ->dest)) && redirect_edge_and_branch (b->succ, b->succ->dest))
changed_here = 1; changed_here = true;
/* Simplify branch to branch. */
if (try_forward_edges (b)) if (try_forward_edges (b))
changed_here = 1; changed_here = true;
if ((mode & CLEANUP_CROSSJUMP) && try_crossjump_bb (mode, b)) /* Look for shared code between blocks. */
changed_here = 1; if ((mode & CLEANUP_CROSSJUMP)
&& try_crossjump_bb (mode, b))
changed_here = true;
/* Don't get confused by the index shift caused by deleting /* Don't get confused by the index shift caused by deleting
blocks. */ blocks. */
if (!changed_here) if (!changed_here)
i = b->index + 1; i = b->index + 1;
else else
changed = 1; changed = true;
} }
if ((mode & CLEANUP_CROSSJUMP) && try_crossjump_bb (mode, EXIT_BLOCK_PTR))
changed = 1; if ((mode & CLEANUP_CROSSJUMP)
&& try_crossjump_bb (mode, EXIT_BLOCK_PTR))
changed = true;
#ifdef ENABLE_CHECKING #ifdef ENABLE_CHECKING
if (changed) if (changed)
verify_flow_info (); verify_flow_info ();
#endif #endif
changed_overall |= changed; changed_overall |= changed;
} }
while (changed); while (changed);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment