tree-cfgcleanup.c (cfgcleanup_altered_bbs): New global variable.

* tree-cfgcleanup.c (cfgcleanup_altered_bbs): New global variable.
	(remove_fallthru_edge): Use remove_edge_and_dominated_blocks.
	(cleanup_control_expr_graph): Do not invalidate dominance info.
	Record altered blocks.
	(cleanup_control_flow, cleanup_forwarder_blocks): Removed.
	(cleanup_control_flow_bb, split_bbs_on_noreturn_calls,
	cleanup_tree_cfg_bb): New functions.
	(remove_forwarder_block): Do not maintain the worklist of blocks.
	Record altered blocks.
	(cleanup_tree_cfg_1): Iterate over cfgcleanup_altered_bbs,
	not over whole cfg.
	(cleanup_tree_cfg): Do not iterate cleanup_tree_cfg_1.  Only call
	delete_unreachable_blocks if dominators are not available.
	* tree-inline.c (optimize_inline_calls): Free dominance information
	earlier.
	* tree-flow.h (remove_edge_and_dominated_blocks,
	cfgcleanup_altered_bbs): Altered.
	* tree-cfg.c (replace_uses_by, tree_merge_blocks): Record altered
	blocks.
	(get_all_dominated_blocks, remove_edge_and_dominated_blocks): New
	functions.
	(tree_purge_dead_eh_edges): Use remove_edge_and_dominated_blocks,
	do not invalidate dominators.

From-SVN: r124203
This commit is contained in:
Zdenek Dvorak 2007-04-27 01:13:41 +02:00 committed by Zdenek Dvorak
parent 468a823ba9
commit 672987e82f
5 changed files with 348 additions and 158 deletions

View File

@ -1,3 +1,29 @@
2007-04-27 Zdenek Dvorak <dvorakz@suse.cz>
* tree-cfgcleanup.c (cfgcleanup_altered_bbs): New global variable.
(remove_fallthru_edge): Use remove_edge_and_dominated_blocks.
(cleanup_control_expr_graph): Do not invalidate dominance info.
Record altered blocks.
(cleanup_control_flow, cleanup_forwarder_blocks): Removed.
(cleanup_control_flow_bb, split_bbs_on_noreturn_calls,
cleanup_tree_cfg_bb): New functions.
(remove_forwarder_block): Do not maintain the worklist of blocks.
Record altered blocks.
(cleanup_tree_cfg_1): Iterate over cfgcleanup_altered_bbs,
not over whole cfg.
(cleanup_tree_cfg): Do not iterate cleanup_tree_cfg_1. Only call
delete_unreachable_blocks if dominators are not available.
* tree-inline.c (optimize_inline_calls): Free dominance information
earlier.
* tree-flow.h (remove_edge_and_dominated_blocks,
cfgcleanup_altered_bbs): Altered.
* tree-cfg.c (replace_uses_by, tree_merge_blocks): Record altered
blocks.
(get_all_dominated_blocks, remove_edge_and_dominated_blocks): New
functions.
(tree_purge_dead_eh_edges): Use remove_edge_and_dominated_blocks,
do not invalidate dominators.
2007-04-26 Anatoly Sokolov <aesok@post.ru>
* config/avr/avr.c (avr_mcu_types): Add support for ATmega8HVA and

View File

@ -1199,6 +1199,8 @@ replace_uses_by (tree name, tree val)
tree rhs;
fold_stmt_inplace (stmt);
if (cfgcleanup_altered_bbs)
bitmap_set_bit (cfgcleanup_altered_bbs, bb_for_stmt (stmt)->index);
/* FIXME. This should go in pop_stmt_changes. */
rhs = get_rhs (stmt);
@ -1312,6 +1314,9 @@ tree_merge_blocks (basic_block a, basic_block b)
last = tsi_last (bb_stmt_list (a));
tsi_link_after (&last, bb_stmt_list (b), TSI_NEW_STMT);
set_bb_stmt_list (b, NULL_TREE);
if (cfgcleanup_altered_bbs)
bitmap_set_bit (cfgcleanup_altered_bbs, a->index);
}
@ -5429,6 +5434,144 @@ tree_purge_dead_abnormal_call_edges (basic_block bb)
return changed;
}
/* Stores all basic blocks dominated by BB to DOM_BBS. */
static void
get_all_dominated_blocks (basic_block bb, VEC (basic_block, heap) **dom_bbs)
{
basic_block son;
VEC_safe_push (basic_block, heap, *dom_bbs, bb);
for (son = first_dom_son (CDI_DOMINATORS, bb);
son;
son = next_dom_son (CDI_DOMINATORS, son))
get_all_dominated_blocks (son, dom_bbs);
}
/* Removes edge E and all the blocks dominated by it, and updates dominance
information. The IL in E->src needs to be updated separately.
If dominance info is not available, only the edge E is removed.*/
void
remove_edge_and_dominated_blocks (edge e)
{
VEC (basic_block, heap) *bbs_to_remove = NULL;
VEC (basic_block, heap) *bbs_to_fix_dom = NULL;
bitmap df, df_idom;
edge f;
edge_iterator ei;
bool none_removed = false;
unsigned i;
basic_block bb, dbb;
bitmap_iterator bi;
if (!dom_computed[CDI_DOMINATORS])
{
remove_edge (e);
return;
}
/* No updating is needed for edges to exit. */
if (e->dest == EXIT_BLOCK_PTR)
{
if (cfgcleanup_altered_bbs)
bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
remove_edge (e);
return;
}
/* First, we find the basic blocks to remove. If E->dest has a predecessor
that is not dominated by E->dest, then this set is empty. Otherwise,
all the basic blocks dominated by E->dest are removed.
Also, to DF_IDOM we store the immediate dominators of the blocks in
the dominance frontier of E (i.e., of the successors of the
removed blocks, if there are any, and of E->dest otherwise). */
FOR_EACH_EDGE (f, ei, e->dest->preds)
{
if (f == e)
continue;
if (!dominated_by_p (CDI_DOMINATORS, f->src, e->dest))
{
none_removed = true;
break;
}
}
df = BITMAP_ALLOC (NULL);
df_idom = BITMAP_ALLOC (NULL);
if (none_removed)
bitmap_set_bit (df_idom,
get_immediate_dominator (CDI_DOMINATORS, e->dest)->index);
else
{
get_all_dominated_blocks (e->dest, &bbs_to_remove);
for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
{
FOR_EACH_EDGE (f, ei, bb->succs)
{
if (f->dest != EXIT_BLOCK_PTR)
bitmap_set_bit (df, f->dest->index);
}
}
for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
bitmap_clear_bit (df, bb->index);
EXECUTE_IF_SET_IN_BITMAP (df, 0, i, bi)
{
bb = BASIC_BLOCK (i);
bitmap_set_bit (df_idom,
get_immediate_dominator (CDI_DOMINATORS, bb)->index);
}
}
if (cfgcleanup_altered_bbs)
{
/* Record the set of the altered basic blocks. */
bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
bitmap_ior_into (cfgcleanup_altered_bbs, df);
}
/* Remove E and the cancelled blocks. */
if (none_removed)
remove_edge (e);
else
{
for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
delete_basic_block (bb);
}
/* Update the dominance information. The immediate dominator may change only
for blocks whose immediate dominator belongs to DF_IDOM:
Suppose that idom(X) = Y before removal of E and idom(X) != Y after the
removal. Let Z the arbitrary block such that idom(Z) = Y and
Z dominates X after the removal. Before removal, there exists a path P
from Y to X that avoids Z. Let F be the last edge on P that is
removed, and let W = F->dest. Before removal, idom(W) = Y (since Y
dominates W, and because of P, Z does not dominate W), and W belongs to
the dominance frontier of E. Therefore, Y belongs to DF_IDOM. */
EXECUTE_IF_SET_IN_BITMAP (df_idom, 0, i, bi)
{
bb = BASIC_BLOCK (i);
for (dbb = first_dom_son (CDI_DOMINATORS, bb);
dbb;
dbb = next_dom_son (CDI_DOMINATORS, dbb))
VEC_safe_push (basic_block, heap, bbs_to_fix_dom, dbb);
}
iterate_fix_dominators (CDI_DOMINATORS,
VEC_address (basic_block, bbs_to_fix_dom),
VEC_length (basic_block, bbs_to_fix_dom));
BITMAP_FREE (df);
BITMAP_FREE (df_idom);
VEC_free (basic_block, heap, bbs_to_remove);
VEC_free (basic_block, heap, bbs_to_fix_dom);
}
/* Purge dead EH edges from basic block BB. */
bool
@ -5446,33 +5589,13 @@ tree_purge_dead_eh_edges (basic_block bb)
{
if (e->flags & EDGE_EH)
{
remove_edge (e);
remove_edge_and_dominated_blocks (e);
changed = true;
}
else
ei_next (&ei);
}
/* Removal of dead EH edges might change dominators of not
just immediate successors. E.g. when bb1 is changed so that
it no longer can throw and bb1->bb3 and bb1->bb4 are dead
eh edges purged by this function in:
0
/ \
v v
1-->2
/ \ |
v v |
3-->4 |
\ v
--->5
|
-
idom(bb5) must be recomputed. For now just free the dominance
info. */
if (changed)
free_dominance_info (CDI_DOMINATORS);
return changed;
}

View File

@ -48,6 +48,15 @@ Boston, MA 02110-1301, USA. */
#include "tree-ssa-propagate.h"
#include "tree-scalar-evolution.h"
/* The set of blocks in that at least one of the following changes happened:
-- the statement at the end of the block was changed
-- the block was newly created
-- the set of the predecessors of the block changed
-- the set of the successors of the block changed
??? Maybe we could track these changes separately, since they determine
what cleanups it makes sense to try on the block. */
bitmap cfgcleanup_altered_bbs;
/* Remove any fallthru edge from EV. Return true if an edge was removed. */
static bool
@ -59,7 +68,7 @@ remove_fallthru_edge (VEC(edge,gc) *ev)
FOR_EACH_EDGE (e, ei, ev)
if ((e->flags & EDGE_FALLTHRU) != 0)
{
remove_edge (e);
remove_edge_and_dominated_blocks (e);
return true;
}
return false;
@ -124,7 +133,7 @@ cleanup_control_expr_graph (basic_block bb, block_stmt_iterator bsi)
taken_edge->probability += e->probability;
taken_edge->count += e->count;
remove_edge (e);
remove_edge_and_dominated_blocks (e);
retval = true;
}
else
@ -138,106 +147,82 @@ cleanup_control_expr_graph (basic_block bb, block_stmt_iterator bsi)
else
taken_edge = single_succ_edge (bb);
bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
bsi_remove (&bsi, true);
taken_edge->flags = EDGE_FALLTHRU;
/* We removed some paths from the cfg. */
free_dominance_info (CDI_DOMINATORS);
return retval;
}
/* Try to remove superfluous control structures. */
/* Try to remove superfluous control structures in basic block BB. Returns
true if anything changes. */
static bool
cleanup_control_flow (void)
cleanup_control_flow_bb (basic_block bb)
{
basic_block bb;
block_stmt_iterator bsi;
bool retval = false;
tree stmt;
/* Detect cases where a mid-block call is now known not to return. */
if (cfun->gimple_df)
while (VEC_length (tree, MODIFIED_NORETURN_CALLS (cfun)))
{
stmt = VEC_pop (tree, MODIFIED_NORETURN_CALLS (cfun));
bb = bb_for_stmt (stmt);
if (bb != NULL && last_stmt (bb) != stmt && noreturn_call_p (stmt))
split_block (bb, stmt);
}
/* If the last statement of the block could throw and now cannot,
we need to prune cfg. */
retval |= tree_purge_dead_eh_edges (bb);
FOR_EACH_BB (bb)
bsi = bsi_last (bb);
if (bsi_end_p (bsi))
return retval;
stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) == COND_EXPR
|| TREE_CODE (stmt) == SWITCH_EXPR)
retval |= cleanup_control_expr_graph (bb, bsi);
/* If we had a computed goto which has a compile-time determinable
destination, then we can eliminate the goto. */
else if (TREE_CODE (stmt) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (stmt)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (GOTO_DESTINATION (stmt), 0))
== LABEL_DECL))
{
bsi = bsi_last (bb);
edge e;
tree label;
edge_iterator ei;
basic_block target_block;
/* If the last statement of the block could throw and now cannot,
we need to prune cfg. */
retval |= tree_purge_dead_eh_edges (bb);
if (bsi_end_p (bsi))
continue;
stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) == COND_EXPR
|| TREE_CODE (stmt) == SWITCH_EXPR)
retval |= cleanup_control_expr_graph (bb, bsi);
/* If we had a computed goto which has a compile-time determinable
destination, then we can eliminate the goto. */
else if (TREE_CODE (stmt) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (stmt)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (GOTO_DESTINATION (stmt), 0))
== LABEL_DECL))
/* First look at all the outgoing edges. Delete any outgoing
edges which do not go to the right block. For the one
edge which goes to the right block, fix up its flags. */
label = TREE_OPERAND (GOTO_DESTINATION (stmt), 0);
target_block = label_to_block (label);
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
edge e;
tree label;
edge_iterator ei;
basic_block target_block;
bool removed_edge = false;
/* First look at all the outgoing edges. Delete any outgoing
edges which do not go to the right block. For the one
edge which goes to the right block, fix up its flags. */
label = TREE_OPERAND (GOTO_DESTINATION (stmt), 0);
target_block = label_to_block (label);
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
if (e->dest != target_block)
remove_edge_and_dominated_blocks (e);
else
{
if (e->dest != target_block)
{
removed_edge = true;
remove_edge (e);
}
else
{
/* Turn off the EDGE_ABNORMAL flag. */
e->flags &= ~EDGE_ABNORMAL;
/* Turn off the EDGE_ABNORMAL flag. */
e->flags &= ~EDGE_ABNORMAL;
/* And set EDGE_FALLTHRU. */
e->flags |= EDGE_FALLTHRU;
ei_next (&ei);
}
/* And set EDGE_FALLTHRU. */
e->flags |= EDGE_FALLTHRU;
ei_next (&ei);
}
/* If we removed one or more edges, then we will need to fix the
dominators. It may be possible to incrementally update them. */
if (removed_edge)
free_dominance_info (CDI_DOMINATORS);
/* Remove the GOTO_EXPR as it is not needed. The CFG has all the
relevant information we need. */
bsi_remove (&bsi, true);
retval = true;
}
/* Check for indirect calls that have been turned into
noreturn calls. */
else if (noreturn_call_p (stmt) && remove_fallthru_edge (bb->succs))
{
free_dominance_info (CDI_DOMINATORS);
retval = true;
}
bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
bitmap_set_bit (cfgcleanup_altered_bbs, target_block->index);
/* Remove the GOTO_EXPR as it is not needed. The CFG has all the
relevant information we need. */
bsi_remove (&bsi, true);
retval = true;
}
/* Check for indirect calls that have been turned into
noreturn calls. */
else if (noreturn_call_p (stmt) && remove_fallthru_edge (bb->succs))
retval = true;
return retval;
}
@ -366,12 +351,10 @@ phi_alternatives_equal (basic_block dest, edge e1, edge e2)
return true;
}
/* Removes forwarder block BB. Returns false if this failed. If a new
forwarder block is created due to redirection of edges, it is
stored to worklist. */
/* Removes forwarder block BB. Returns false if this failed. */
static bool
remove_forwarder_block (basic_block bb, basic_block **worklist)
remove_forwarder_block (basic_block bb)
{
edge succ = single_succ_edge (bb), e, s;
basic_block dest = succ->dest;
@ -434,6 +417,8 @@ remove_forwarder_block (basic_block bb, basic_block **worklist)
/* Redirect the edges. */
for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
if (e->flags & EDGE_ABNORMAL)
{
/* If there is an abnormal edge, redirect it anyway, and
@ -450,15 +435,6 @@ remove_forwarder_block (basic_block bb, basic_block **worklist)
for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
add_phi_arg (phi, PHI_ARG_DEF (phi, succ->dest_idx), s);
}
else
{
/* The source basic block might become a forwarder. We know
that it was not a forwarder before, since it used to have
at least two outgoing edges, so we may just add it to
worklist. */
if (tree_forwarder_block_p (s->src, false))
*(*worklist)++ = s->src;
}
}
if (seen_abnormal_edge)
@ -476,6 +452,8 @@ remove_forwarder_block (basic_block bb, basic_block **worklist)
}
}
bitmap_set_bit (cfgcleanup_altered_bbs, dest->index);
/* Update the dominators. */
if (dom_info_available_p (CDI_DOMINATORS))
{
@ -501,65 +479,120 @@ remove_forwarder_block (basic_block bb, basic_block **worklist)
return true;
}
/* Removes forwarder blocks. */
/* Split basic blocks on calls in the middle of a basic block that are now
known not to return, and remove the unreachable code. */
static bool
cleanup_forwarder_blocks (void)
split_bbs_on_noreturn_calls (void)
{
basic_block bb;
bool changed = false;
basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks);
basic_block *current = worklist;
tree stmt;
basic_block bb;
FOR_EACH_BB (bb)
{
if (tree_forwarder_block_p (bb, false))
*current++ = bb;
}
/* Detect cases where a mid-block call is now known not to return. */
if (cfun->gimple_df)
while (VEC_length (tree, MODIFIED_NORETURN_CALLS (cfun)))
{
stmt = VEC_pop (tree, MODIFIED_NORETURN_CALLS (cfun));
bb = bb_for_stmt (stmt);
if (bb == NULL
|| last_stmt (bb) == stmt
|| !noreturn_call_p (stmt))
continue;
while (current != worklist)
{
bb = *--current;
changed |= remove_forwarder_block (bb, &current);
}
changed = true;
split_block (bb, stmt);
remove_fallthru_edge (bb->succs);
}
free (worklist);
return changed;
}
/* Do one round of CFG cleanup. */
/* Tries to cleanup cfg in basic block BB. Returns true if anything
changes. */
static bool
cleanup_tree_cfg_1 (void)
cleanup_tree_cfg_bb (basic_block bb)
{
bool retval;
bool retval = false;
retval = cleanup_control_flow ();
retval |= delete_unreachable_blocks ();
retval = cleanup_control_flow_bb (bb);
/* Forwarder blocks can carry line number information which is
useful when debugging, so we only clean them up when
optimizing. */
if (optimize > 0)
{
/* cleanup_forwarder_blocks can redirect edges out of
SWITCH_EXPRs, which can get expensive. So we want to enable
recording of edge to CASE_LABEL_EXPR mappings around the call
to cleanup_forwarder_blocks. */
start_recording_case_labels ();
retval |= cleanup_forwarder_blocks ();
end_recording_case_labels ();
}
if (optimize > 0
&& tree_forwarder_block_p (bb, false)
&& remove_forwarder_block (bb))
return true;
/* Merging the blocks may create new opportunities for folding
conditional branches (due to the elimination of single-valued PHI
nodes). */
retval |= merge_seq_blocks ();
if (single_succ_p (bb)
&& can_merge_blocks_p (bb, single_succ (bb)))
{
merge_blocks (bb, single_succ (bb));
return true;
}
return retval;
}
/* Iterate the cfg cleanups, while anything changes. */
static bool
cleanup_tree_cfg_1 (void)
{
bool retval = false;
basic_block bb;
unsigned i, n;
retval |= split_bbs_on_noreturn_calls ();
/* Prepare the worklists of altered blocks. */
cfgcleanup_altered_bbs = BITMAP_ALLOC (NULL);
/* During forwarder block cleanup, we may redirect edges out of
SWITCH_EXPRs, which can get expensive. So we want to enable
recording of edge to CASE_LABEL_EXPR. */
start_recording_case_labels ();
/* Start by iterating over all basic blocks. We cannot use FOR_EACH_BB,
since the basic blocks may get removed. */
n = last_basic_block;
for (i = NUM_FIXED_BLOCKS; i < n; i++)
{
bb = BASIC_BLOCK (i);
if (bb)
retval |= cleanup_tree_cfg_bb (bb);
}
/* Now process the altered blocks, as long as any are available. */
while (!bitmap_empty_p (cfgcleanup_altered_bbs))
{
i = bitmap_first_set_bit (cfgcleanup_altered_bbs);
bitmap_clear_bit (cfgcleanup_altered_bbs, i);
if (i < NUM_FIXED_BLOCKS)
continue;
bb = BASIC_BLOCK (i);
if (!bb)
continue;
retval |= cleanup_tree_cfg_bb (bb);
/* Rerun split_bbs_on_noreturn_calls, in case we have altered any noreturn
calls. */
retval |= split_bbs_on_noreturn_calls ();
}
end_recording_case_labels ();
BITMAP_FREE (cfgcleanup_altered_bbs);
return retval;
}
/* Remove unreachable blocks and other miscellaneous clean up work.
Return true if the flowgraph was modified, false otherwise. */
@ -567,20 +600,26 @@ cleanup_tree_cfg_1 (void)
bool
cleanup_tree_cfg (void)
{
bool retval, changed;
bool changed;
timevar_push (TV_TREE_CLEANUP_CFG);
/* Iterate until there are no more cleanups left to do. If any
iteration changed the flowgraph, set CHANGED to true. */
changed = false;
do
{
retval = cleanup_tree_cfg_1 ();
changed |= retval;
}
while (retval);
iteration changed the flowgraph, set CHANGED to true.
If dominance information is available, there cannot be any unreachable
blocks. */
if (!dom_computed[CDI_DOMINATORS])
{
changed = delete_unreachable_blocks ();
calculate_dominance_info (CDI_DOMINATORS);
}
else
changed = false;
changed |= cleanup_tree_cfg_1 ();
gcc_assert (dom_computed[CDI_DOMINATORS]);
compact_blocks ();
#ifdef ENABLE_CHECKING
@ -602,7 +641,6 @@ cleanup_tree_cfg_loop (void)
if (changed && current_loops != NULL)
{
bitmap changed_bbs = BITMAP_ALLOC (NULL);
calculate_dominance_info (CDI_DOMINATORS);
fix_loop_structure (changed_bbs);
/* This usually does nothing. But sometimes parts of cfg that originally

View File

@ -778,8 +778,10 @@ extern void start_recording_case_labels (void);
extern void end_recording_case_labels (void);
extern basic_block move_sese_region_to_fn (struct function *, basic_block,
basic_block);
void remove_edge_and_dominated_blocks (edge);
/* In tree-cfgcleanup.c */
extern bitmap cfgcleanup_altered_bbs;
extern bool cleanup_tree_cfg (void);
extern bool cleanup_tree_cfg_loop (void);

View File

@ -2804,6 +2804,10 @@ optimize_inline_calls (tree fn)
push_gimplify_context ();
/* We make no attempts to keep dominance info up-to-date. */
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
/* Reach the trees by walking over the CFG, and note the
enclosing basic-blocks in the call edges. */
/* We walk the blocks going forward, because inlined function bodies
@ -2840,9 +2844,6 @@ optimize_inline_calls (tree fn)
fold_cond_expr_cond ();
if (current_function_has_nonlocal_label)
make_nonlocal_label_edges ();
/* We make no attempts to keep dominance info up-to-date. */
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
/* It would be nice to check SSA/CFG/statement consistency here, but it is
not possible yet - the IPA passes might make various functions to not
throw and they don't care to proactively update local EH info. This is