/* Control flow optimization code for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GCC.
#include "expr.h"
#include "df.h"
#include "dce.h"
+#include "dbgcnt.h"
#define FORWARDER_BLOCK_P(BB) ((BB)->flags & BB_FORWARDER_BLOCK)
/* Set to true when we are running first pass of try_optimize_cfg loop. */
static bool first_pass;
+
+/* Set to true if crossjumps occured in the latest run of try_optimize_cfg. */
+static bool crossjumps_occured;
+
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);
and cold sections.
Basic block partitioning may result in some jumps that appear to
- be optimizable (or blocks that appear to be mergeable), but which really m
- ust be left untouched (they are required to make it safely across
+ be optimizable (or blocks that appear to be mergeable), but which really
+ must be left untouched (they are required to make it safely across
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
for (ei = ei_start (b->succs); (e = ei_safe_edge (ei)); )
{
basic_block target, first;
- int counter;
+ int counter, goto_locus;
bool threaded = false;
int nthreaded_edges = 0;
bool may_thread = first_pass | df_get_bb_dirty (b);
target = first = e->dest;
counter = NUM_FIXED_BLOCKS;
+ goto_locus = e->goto_locus;
/* If we are partitioning hot/cold basic_blocks, we don't want to mess
up jumps that cross between hot/cold sections.
new_target = single_succ (target);
if (target == new_target)
counter = n_basic_blocks;
+ else if (!optimize)
+ {
+ /* When not optimizing, ensure that edges or forwarder
+ blocks with different locus are not optimized out. */
+ int locus = single_succ_edge (target)->goto_locus;
+
+ if (locus && goto_locus && !locator_eq (locus, goto_locus))
+ counter = n_basic_blocks;
+ else if (locus)
+ goto_locus = locus;
+
+ if (INSN_P (BB_END (target)))
+ {
+ locus = INSN_LOCATOR (BB_END (target));
+
+ if (locus && goto_locus
+ && !locator_eq (locus, goto_locus))
+ counter = n_basic_blocks;
+ else if (locus)
+ goto_locus = locus;
+ }
+ }
}
/* Allow to thread only over one edge at time to simplify updating
int edge_frequency;
int n = 0;
+ e->goto_locus = goto_locus;
+
/* Don't force if target is exit block. */
if (threaded && target != EXIT_BLOCK_PTR)
{
if (GET_CODE (i1) != GET_CODE (i2))
return false;
- p1 = PATTERN (i1);
+ /* __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;
+
+ p1 = PATTERN (i1);
p2 = PATTERN (i2);
if (GET_CODE (p1) != GET_CODE (p2))
? rtx_renumbered_equal_p (p1, p2) : rtx_equal_p (p1, p2))
return true;
- /* Do not do EQUIV substitution after reload. First, we're undoing the
- work of reload_cse. Second, we may be undoing the work of the post-
- reload splitting pass. */
- /* ??? Possibly add a new phase switch variable that can be used by
- targets to disallow the troublesome insns after splitting. */
- if (!reload_completed)
- {
- /* The following code helps take care of G++ cleanups. */
- rtx equiv1 = find_reg_equal_equiv_note (i1);
- rtx equiv2 = find_reg_equal_equiv_note (i2);
-
- if (equiv1 && equiv2
- /* If the equivalences are not to a constant, they may
- reference pseudos that no longer exist, so we can't
- use them. */
- && (! reload_completed
- || (CONSTANT_P (XEXP (equiv1, 0))
- && rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))))
- {
- rtx s1 = single_set (i1);
- rtx s2 = single_set (i2);
- if (s1 != 0 && s2 != 0
- && rtx_renumbered_equal_p (SET_DEST (s1), SET_DEST (s2)))
- {
- validate_change (i1, &SET_SRC (s1), XEXP (equiv1, 0), 1);
- validate_change (i2, &SET_SRC (s2), XEXP (equiv2, 0), 1);
- if (! rtx_renumbered_equal_p (p1, p2))
- cancel_changes (0);
- else if (apply_change_group ())
- return true;
- }
- }
- }
-
return false;
}
\f
while (true)
{
/* Ignore notes. */
- while (!INSN_P (i1) && i1 != BB_HEAD (bb1))
+ while (!NONDEBUG_INSN_P (i1) && i1 != BB_HEAD (bb1))
i1 = PREV_INSN (i1);
- while (!INSN_P (i2) && i2 != BB_HEAD (bb2))
+ while (!NONDEBUG_INSN_P (i2) && i2 != BB_HEAD (bb2))
i2 = PREV_INSN (i2);
if (i1 == BB_HEAD (bb1) || i2 == BB_HEAD (bb2))
Two, it keeps line number notes as matched as may be. */
if (ninsns)
{
- while (last1 != BB_HEAD (bb1) && !INSN_P (PREV_INSN (last1)))
+ while (last1 != BB_HEAD (bb1) && !NONDEBUG_INSN_P (PREV_INSN (last1)))
last1 = PREV_INSN (last1);
if (last1 != BB_HEAD (bb1) && LABEL_P (PREV_INSN (last1)))
last1 = PREV_INSN (last1);
- while (last2 != BB_HEAD (bb2) && !INSN_P (PREV_INSN (last2)))
+ while (last2 != BB_HEAD (bb2) && !NONDEBUG_INSN_P (PREV_INSN (last2)))
last2 = PREV_INSN (last2);
if (last2 != BB_HEAD (bb2) && LABEL_P (PREV_INSN (last2)))
return ninsns;
}
-/* Return true iff the condbranches at the end of BB1 and BB2 match. */
-bool
-condjump_equiv_p (struct equiv_info *info, bool call_init)
-{
- basic_block bb1 = info->x_block;
- basic_block bb2 = info->y_block;
- edge b1 = BRANCH_EDGE (bb1);
- edge b2 = BRANCH_EDGE (bb2);
- edge f1 = FALLTHRU_EDGE (bb1);
- edge f2 = FALLTHRU_EDGE (bb2);
- bool reverse, match;
- rtx set1, set2, cond1, cond2;
- rtx src1, src2;
- enum rtx_code code1, code2;
-
- /* Get around possible forwarders on fallthru edges. Other cases
- should be optimized out already. */
- if (FORWARDER_BLOCK_P (f1->dest))
- f1 = single_succ_edge (f1->dest);
-
- if (FORWARDER_BLOCK_P (f2->dest))
- f2 = single_succ_edge (f2->dest);
-
- /* To simplify use of this function, return false if there are
- unneeded forwarder blocks. These will get eliminated later
- during cleanup_cfg. */
- if (FORWARDER_BLOCK_P (f1->dest)
- || FORWARDER_BLOCK_P (f2->dest)
- || FORWARDER_BLOCK_P (b1->dest)
- || FORWARDER_BLOCK_P (b2->dest))
- return false;
-
- if (f1->dest == f2->dest && b1->dest == b2->dest)
- reverse = false;
- else if (f1->dest == b2->dest && b1->dest == f2->dest)
- reverse = true;
- else
- return false;
-
- set1 = pc_set (BB_END (bb1));
- set2 = pc_set (BB_END (bb2));
- if ((XEXP (SET_SRC (set1), 1) == pc_rtx)
- != (XEXP (SET_SRC (set2), 1) == pc_rtx))
- reverse = !reverse;
-
- src1 = SET_SRC (set1);
- src2 = SET_SRC (set2);
- cond1 = XEXP (src1, 0);
- cond2 = XEXP (src2, 0);
- code1 = GET_CODE (cond1);
- if (reverse)
- code2 = reversed_comparison_code (cond2, BB_END (bb2));
- else
- code2 = GET_CODE (cond2);
-
- if (code2 == UNKNOWN)
- return false;
-
- if (call_init && !struct_equiv_init (STRUCT_EQUIV_START | info->mode, info))
- gcc_unreachable ();
- /* Make the sources of the pc sets unreadable so that when we call
- insns_match_p it won't process them.
- The death_notes_match_p from insns_match_p won't see the local registers
- used for the pc set, but that could only cause missed optimizations when
- there are actually condjumps that use stack registers. */
- SET_SRC (set1) = pc_rtx;
- SET_SRC (set2) = pc_rtx;
- /* Verify codes and operands match. */
- if (code1 == code2)
- {
- match = (insns_match_p (BB_END (bb1), BB_END (bb2), info)
- && rtx_equiv_p (&XEXP (cond1, 0), XEXP (cond2, 0), 1, info)
- && rtx_equiv_p (&XEXP (cond1, 1), XEXP (cond2, 1), 1, info));
-
- }
- else if (code1 == swap_condition (code2))
- {
- match = (insns_match_p (BB_END (bb1), BB_END (bb2), info)
- && rtx_equiv_p (&XEXP (cond1, 1), XEXP (cond2, 0), 1, info)
- && rtx_equiv_p (&XEXP (cond1, 0), XEXP (cond2, 1), 1, info));
-
- }
- else
- match = false;
- SET_SRC (set1) = src1;
- SET_SRC (set2) = src2;
- match &= verify_changes (0);
-
- /* If we return true, we will join the blocks. Which means that
- we will only have one branch prediction bit to work with. Thus
- we require the existing branches to have probabilities that are
- roughly similar. */
- if (match
- && !optimize_size
- && maybe_hot_bb_p (bb1)
- && maybe_hot_bb_p (bb2))
- {
- int prob2;
-
- if (b1->dest == b2->dest)
- prob2 = b2->probability;
- else
- /* Do not use f2 probability as f2 may be forwarded. */
- prob2 = REG_BR_PROB_BASE - b2->probability;
-
- /* Fail if the difference in probabilities is greater than 50%.
- This rules out two well-predicted branches with opposite
- outcomes. */
- if (abs (b1->probability - prob2) > REG_BR_PROB_BASE / 2)
- {
- if (dump_file)
- fprintf (dump_file,
- "Outcomes of branch in bb %i and %i differ too much (%i %i)\n",
- bb1->index, bb2->index, b1->probability, prob2);
-
- match = false;
- }
- }
-
- if (dump_file && match)
- fprintf (dump_file, "Conditionals in bb %i and %i match.\n",
- bb1->index, bb2->index);
-
- if (!match)
- cancel_changes (0);
- return match;
-}
-
/* Return true iff outgoing edges of BB1 and BB2 match, together with
the branch instruction. This means that if we commonize the control
flow before end of the basic block, the semantic remains unchanged.
we require the existing branches to have probabilities that are
roughly similar. */
if (match
- && !optimize_size
- && maybe_hot_bb_p (bb1)
- && maybe_hot_bb_p (bb2))
+ && optimize_bb_for_speed_p (bb1)
+ && optimize_bb_for_speed_p (bb2))
{
int prob2;
/* Skip possible basic block header. */
if (LABEL_P (newpos2))
newpos2 = NEXT_INSN (newpos2);
+ while (DEBUG_INSN_P (newpos2))
+ newpos2 = NEXT_INSN (newpos2);
if (NOTE_P (newpos2))
newpos2 = NEXT_INSN (newpos2);
+ while (DEBUG_INSN_P (newpos2))
+ newpos2 = NEXT_INSN (newpos2);
}
if (dump_file)
"Cross jumping from bb %i to bb %i; %i common insns\n",
src1->index, src2->index, nmatch);
- redirect_to->count += src1->count;
- redirect_to->frequency += src1->frequency;
/* We may have some registers visible through the block. */
df_set_bb_dirty (redirect_to);
/ (redirect_to->frequency + src1->frequency));
}
+ /* Adjust count and frequency for the block. An earlier jump
+ threading pass may have left the profile in an inconsistent
+ state (see update_bb_profile_for_threading) so we must be
+ prepared for overflows. */
+ redirect_to->count += src1->count;
+ redirect_to->frequency += src1->frequency;
+ if (redirect_to->frequency > BB_FREQ_MAX)
+ redirect_to->frequency = BB_FREQ_MAX;
update_br_prob_note (redirect_to);
/* Edit SRC1 to go to REDIRECT_TO at NEWPOS1. */
if (LABEL_P (newpos1))
newpos1 = NEXT_INSN (newpos1);
- if (NOTE_P (newpos1))
+ while (DEBUG_INSN_P (newpos1))
+ newpos1 = NEXT_INSN (newpos1);
+
+ if (NOTE_INSN_BASIC_BLOCK_P (newpos1))
+ newpos1 = NEXT_INSN (newpos1);
+
+ while (DEBUG_INSN_P (newpos1))
newpos1 = NEXT_INSN (newpos1);
redirect_from = split_block (src1, PREV_INSN (newpos1))->src;
/* Don't crossjump if this block ends in a computed jump,
unless we are optimizing for size. */
- if (!optimize_size
+ if (optimize_bb_for_size_p (bb)
&& bb != EXIT_BLOCK_PTR
&& computed_jump_p (BB_END (bb)))
return false;
FOR_EACH_EDGE (e, ei, bb->preds)
{
if (e->flags & EDGE_FALLTHRU)
- fallthru = e;
+ {
+ fallthru = e;
+ break;
+ }
}
changed = false;
e = EDGE_PRED (ev, ix);
ix++;
- /* As noted above, first try with the fallthru predecessor. */
+ /* As noted above, first try with the fallthru predecessor (or, a
+ fallthru predecessor if we are in cfglayout mode). */
if (fallthru)
{
/* Don't combine the fallthru edge into anything else.
}
}
+ if (changed)
+ crossjumps_occured = true;
+
return changed;
}
int iterations = 0;
basic_block bb, b, next;
- if (mode & CLEANUP_CROSSJUMP)
- add_noreturn_fake_exit_edges ();
-
if (mode & (CLEANUP_CROSSJUMP | CLEANUP_THREADING))
clear_bb_flags ();
+ crossjumps_occured = false;
+
FOR_EACH_BB (bb)
update_forwarder_flag (bb);
edge s;
bool changed_here = false;
- /* Delete trivially dead basic blocks. */
- if (EDGE_COUNT (b->preds) == 0)
+ /* Delete trivially dead basic blocks. This is either
+ blocks with no predecessors, or empty blocks with no
+ successors. However if the empty block with no
+ successors is the successor of the ENTRY_BLOCK, it is
+ kept. This ensures that the ENTRY_BLOCK will have a
+ successor which is a precondition for many RTL
+ passes. Empty blocks may result from expanding
+ __builtin_unreachable (). */
+ if (EDGE_COUNT (b->preds) == 0
+ || (EDGE_COUNT (b->succs) == 0
+ && BB_HEAD (b) == BB_END (b)
+ && single_succ_edge (ENTRY_BLOCK_PTR)->dest != b))
{
c = b->prev_bb;
if (dump_file)
delete_basic_block (b);
changed = true;
b = c;
+ continue;
}
if (single_succ_p (b)
while (changed);
}
- if (mode & CLEANUP_CROSSJUMP)
- remove_fake_exit_edges ();
-
FOR_ALL_BB (b)
b->flags &= ~(BB_FORWARDER_BLOCK | BB_NONTHREADABLE_BLOCK);
delete_unreachable_blocks (void)
{
bool changed = false;
- basic_block b, next_bb;
+ basic_block b, prev_bb;
find_unreachable_blocks ();
- /* Delete all unreachable basic blocks. */
-
- for (b = ENTRY_BLOCK_PTR->next_bb; b != EXIT_BLOCK_PTR; b = next_bb)
+ /* When we're in GIMPLE mode and there may be debug insns, we should
+ delete blocks in reverse dominator order, so as to get a chance
+ to substitute all released DEFs into debug stmts. If we don't
+ have dominators information, walking blocks backward gets us a
+ better chance of retaining most debug information than
+ otherwise. */
+ if (MAY_HAVE_DEBUG_STMTS && current_ir_type () == IR_GIMPLE
+ && dom_info_available_p (CDI_DOMINATORS))
{
- next_bb = b->next_bb;
+ for (b = EXIT_BLOCK_PTR->prev_bb; b != ENTRY_BLOCK_PTR; b = prev_bb)
+ {
+ prev_bb = b->prev_bb;
- if (!(b->flags & BB_REACHABLE))
+ if (!(b->flags & BB_REACHABLE))
+ {
+ /* Speed up the removal of blocks that don't dominate
+ others. Walking backwards, this should be the common
+ case. */
+ if (!first_dom_son (CDI_DOMINATORS, b))
+ delete_basic_block (b);
+ else
+ {
+ VEC (basic_block, heap) *h
+ = get_all_dominated_blocks (CDI_DOMINATORS, b);
+
+ while (VEC_length (basic_block, h))
+ {
+ b = VEC_pop (basic_block, h);
+
+ prev_bb = b->prev_bb;
+
+ gcc_assert (!(b->flags & BB_REACHABLE));
+
+ delete_basic_block (b);
+ }
+
+ VEC_free (basic_block, heap, h);
+ }
+
+ changed = true;
+ }
+ }
+ }
+ else
+ {
+ for (b = EXIT_BLOCK_PTR->prev_bb; b != ENTRY_BLOCK_PTR; b = prev_bb)
{
- delete_basic_block (b);
- changed = true;
+ prev_bb = b->prev_bb;
+
+ if (!(b->flags & BB_REACHABLE))
+ {
+ delete_basic_block (b);
+ changed = true;
+ }
}
}
next = NEXT_INSN (insn);
if (LABEL_P (insn)
&& LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn)
- && JUMP_P (next)
- && (GET_CODE (PATTERN (next)) == ADDR_VEC
- || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
+ && JUMP_TABLE_DATA_P (next))
{
rtx label = insn, jump = next;
compact_blocks ();
+ /* To tail-merge blocks ending in the same noreturn function (e.g.
+ a call to abort) we have to insert fake edges to exit. Do this
+ here once. The fake edges do not interfere with any other CFG
+ cleanups. */
+ if (mode & CLEANUP_CROSSJUMP)
+ add_noreturn_fake_exit_edges ();
+
+ if (!dbg_cnt (cfg_cleanup))
+ return changed;
+
while (try_optimize_cfg (mode))
{
delete_unreachable_blocks (), changed = true;
- if (!(mode & CLEANUP_NO_INSN_DEL)
- && (mode & CLEANUP_EXPENSIVE)
- && !reload_completed)
+ if (!(mode & CLEANUP_NO_INSN_DEL))
{
- if (!delete_trivially_dead_insns (get_insns (), max_reg_num ()))
+ /* Try to remove some trivially dead insns when doing an expensive
+ cleanup. But delete_trivially_dead_insns doesn't work after
+ reload (it only handles pseudos) and run_fast_dce is too costly
+ to run in every iteration.
+
+ For effective cross jumping, we really want to run a fast DCE to
+ clean up any dead conditions, or they get in the way of performing
+ useful tail merges.
+
+ Other transformations in cleanup_cfg are not so sensitive to dead
+ code, so delete_trivially_dead_insns or even doing nothing at all
+ is good enough. */
+ if ((mode & CLEANUP_EXPENSIVE) && !reload_completed
+ && !delete_trivially_dead_insns (get_insns (), max_reg_num ()))
break;
+ else if ((mode & CLEANUP_CROSSJUMP)
+ && crossjumps_occured)
+ run_fast_dce ();
}
else
break;
}
+ if (mode & CLEANUP_CROSSJUMP)
+ remove_fake_exit_edges ();
+
/* Don't call delete_dead_jumptables in cfglayout mode, because
that function assumes that jump tables are in the insns stream.
But we also don't _have_ to delete dead jumptables in cfglayout
static unsigned int
rest_of_handle_jump (void)
{
- delete_unreachable_blocks ();
-
- if (cfun->tail_call_emit)
+ if (crtl->tail_call_emit)
fixup_tail_calls ();
return 0;
}
-struct tree_opt_pass pass_jump =
+struct rtl_opt_pass pass_jump =
{
+ {
+ RTL_PASS,
"sibling", /* name */
NULL, /* gate */
rest_of_handle_jump, /* execute */
0, /* properties_destroyed */
TODO_ggc_collect, /* todo_flags_start */
TODO_verify_flow, /* todo_flags_finish */
- 'i' /* letter */
+ }
};
}
-struct tree_opt_pass pass_jump2 =
+struct rtl_opt_pass pass_jump2 =
{
+ {
+ RTL_PASS,
"jump", /* name */
NULL, /* gate */
rest_of_handle_jump2, /* execute */
0, /* properties_destroyed */
TODO_ggc_collect, /* todo_flags_start */
TODO_dump_func | TODO_verify_rtl_sharing,/* todo_flags_finish */
- 'j' /* letter */
+ }
};