Makefile.in (OBJC-common): Add tree-ssa-threadupdate.c

* Makefile.in (OBJC-common): Add tree-ssa-threadupdate.c
        (tree-ssa-threadupdate.o): Add dependencies.
        * tree-ssa-threadupdate.c: New file.
        * tree-flow.h (incoming_edge_threaded): New flag in block annotation.
        (rewrite_vars_out_of_ssa): Remove prototype.
        (cleanup_tree_cfg): Returns a bool.
        * tree.h (thread_through_all_blocks): Prototype.
        * tree-outof-ssa.c  (SSANORM_*): Move into here.
        (remove_ssa_form): Now static.
        (rewrite_vars_out_of_ssa): Kill.
        * tree-ssa-live.c (register_ssa_partitions_for_vars): Kill.
        * tree-ssa-live.h (SSANORM_*): Moved into tree-outof-ssa.c.
        (remove_ssa_form, register_partitions_for_vars): Kill declarations.
        * tree-cfg.c (cleanup_tree_cfg): Return a value indicating if
        anything was changed.
        * tree-phinodes.c (add_phi_arg): Get the block for the PHI
        from the PHI's annotation rather than the edge associated with
        the new argument.
        * tree-ssa-dom.c (redirection_edges): Kill.
        (redirect_edges_and_update_ssa_graph): Kill.
        (tree_ssa_dominator_optimize): Do not reset forwardable flag
        for blocks anymore.  Do not initialize redirection_edges.
        Call thread_through_all_blocks.  Simplify code for cleanup
        of the CFG and iterating.  No longer call cleanup_tree_cfg
        outside the iteration loop.
        (thread_across_edge): No longer mess with forwardable blocks.

From-SVN: r85721
This commit is contained in:
Jeff Law 2004-08-09 13:13:07 -06:00 committed by Jeff Law
parent 9b305d55bf
commit 56b043c808
11 changed files with 516 additions and 531 deletions

View File

@ -1,3 +1,32 @@
2004-08-09 Jeff Law <law@redhat.com>
* Makefile.in (OBJC-common): Add tree-ssa-threadupdate.c
(tree-ssa-threadupdate.o): Add dependencies.
* tree-ssa-threadupdate.c: New file.
* tree-flow.h (incoming_edge_threaded): New flag in block annotation.
(rewrite_vars_out_of_ssa): Remove prototype.
(cleanup_tree_cfg): Returns a bool.
* tree.h (thread_through_all_blocks): Prototype.
* tree-outof-ssa.c (SSANORM_*): Move into here.
(remove_ssa_form): Now static.
(rewrite_vars_out_of_ssa): Kill.
* tree-ssa-live.c (register_ssa_partitions_for_vars): Kill.
* tree-ssa-live.h (SSANORM_*): Moved into tree-outof-ssa.c.
(remove_ssa_form, register_partitions_for_vars): Kill declarations.
* tree-cfg.c (cleanup_tree_cfg): Return a value indicating if
anything was changed.
* tree-phinodes.c (add_phi_arg): Get the block for the PHI
from the PHI's annotation rather than the edge associated with
the new argument.
* tree-ssa-dom.c (redirection_edges): Kill.
(redirect_edges_and_update_ssa_graph): Kill.
(tree_ssa_dominator_optimize): Do not reset forwardable flag
for blocks anymore. Do not initialize redirection_edges.
Call thread_through_all_blocks. Simplify code for cleanup
of the CFG and iterating. No longer call cleanup_tree_cfg
outside the iteration loop.
(thread_across_edge): No longer mess with forwardable blocks.
2004-08-09 Zack Weinberg <zack@codesourcery.com>
* explow.c (memory_address): Use memory_address_p.

View File

@ -898,7 +898,7 @@ OBJS-common = \
tree-ssa-phiopt.o tree-ssa-forwprop.o tree-nested.o tree-ssa-dse.o \
tree-ssa-dom.o domwalk.o tree-tailcall.o gimple-low.o tree-iterator.o \
tree-phinodes.o tree-ssanames.o tree-sra.o tree-complex.o tree-ssa-loop.o \
tree-ssa-loop-niter.o tree-ssa-loop-manip.o \
tree-ssa-loop-niter.o tree-ssa-loop-manip.o tree-ssa-threadupdate.o \
alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \
cfg.o cfganal.o cfgbuild.o cfgcleanup.o cfglayout.o cfgloop.o \
cfgloopanal.o cfgloopmanip.o loop-init.o loop-unswitch.o loop-unroll.o \
@ -1632,6 +1632,10 @@ tree-ssa-dom.o : tree-ssa-dom.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
$(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h diagnostic.h \
errors.h function.h $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \
$(BASIC_BLOCK_H) domwalk.h real.h tree-pass.h $(FLAGS_H) langhooks.h
tree-ssa-threadupdate.o : tree-ssa-threadupdate.c $(TREE_FLOW_H) $(CONFIG_H) \
$(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h \
diagnostic.h errors.h function.h $(TM_H) coretypes.h $(TREE_DUMP_H) \
$(BASIC_BLOCK_H) $(FLAGS_H) tree-pass.h
tree-ssanames.o : tree-ssanames.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(TREE_H) varray.h $(GGC_H) gt-tree-ssanames.h $(TREE_FLOW_H)
tree-phinodes.o : tree-phinodes.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \

View File

@ -717,10 +717,11 @@ make_goto_expr_edges (basic_block bb)
/* Remove unreachable blocks and other miscellaneous clean up work. */
void
bool
cleanup_tree_cfg (void)
{
bool something_changed = true;
bool retval = false;
timevar_push (TV_TREE_CLEANUP_CFG);
@ -731,6 +732,7 @@ cleanup_tree_cfg (void)
something_changed = cleanup_control_flow ();
something_changed |= delete_unreachable_blocks ();
something_changed |= thread_jumps ();
retval |= something_changed;
}
/* Merging the blocks creates no new opportunities for the other
@ -743,6 +745,7 @@ cleanup_tree_cfg (void)
verify_flow_info ();
#endif
timevar_pop (TV_TREE_CLEANUP_CFG);
return retval;
}

View File

@ -356,6 +356,10 @@ struct bb_ann_d GTY(())
/* Nonzero if this block contains an escape point (see is_escape_site). */
unsigned has_escape_site : 1;
/* Nonzero if one or more incoming edges to this block should be threaded
to an outgoing edge of this block. */
unsigned incoming_edge_threaded : 1;
struct edge_prediction *predictions;
};
@ -474,7 +478,7 @@ extern void debug_loop_ir (void);
extern void print_loop_ir (FILE *);
extern void cleanup_dead_labels (void);
extern void group_case_labels (void);
extern void cleanup_tree_cfg (void);
extern bool cleanup_tree_cfg (void);
extern tree first_stmt (basic_block);
extern tree last_stmt (basic_block);
extern tree *last_stmt_ptr (basic_block);
@ -561,7 +565,6 @@ typedef bool (*walk_use_def_chains_fn) (tree, tree, void *);
/* In tree-ssa.c */
extern void init_tree_ssa (void);
extern void rewrite_vars_out_of_ssa (bitmap);
extern void dump_reaching_defs (FILE *);
extern void debug_reaching_defs (void);
extern void dump_tree_ssa (FILE *);

View File

@ -48,6 +48,14 @@ Boston, MA 02111-1307, USA. */
#include "tree-ssa-live.h"
#include "tree-pass.h"
/* Flags to pass to remove_ssa_form. */
#define SSANORM_PERFORM_TER 0x1
#define SSANORM_COMBINE_TEMPS 0x2
#define SSANORM_REMOVE_ALL_PHIS 0x4
#define SSANORM_COALESCE_PARTITIONS 0x8
#define SSANORM_USE_COALESCE_LIST 0x10
/* Used to hold all the components required to do SSA PHI elimination.
The node and pred/succ list is a simple linear list of nodes and
edges represented as pairs of nodes.
@ -1956,7 +1964,7 @@ rewrite_trees (var_map map, tree *values)
/* Remove the variables specified in MAP from SSA form. Any debug information
is sent to DUMP. FLAGS indicate what options should be used. */
void
static void
remove_ssa_form (FILE *dump, var_map map, int flags)
{
tree_live_info_p liveinfo;
@ -2039,122 +2047,6 @@ remove_ssa_form (FILE *dump, var_map map, int flags)
dump_file = save;
}
/* Take a subset of the variables VARS in the current function out of SSA
form. */
void
rewrite_vars_out_of_ssa (bitmap vars)
{
if (bitmap_first_set_bit (vars) >= 0)
{
var_map map;
basic_block bb;
tree phi;
int i;
int ssa_flags;
/* Search for PHIs in which one of the PHI arguments is marked for
translation out of SSA form, but for which the PHI result is not
marked for translation out of SSA form.
Our per-variable out of SSA translation can not handle that case;
however we can easily handle it here by creating a new instance
of the PHI result's underlying variable and initializing it to
the offending PHI argument on the edge associated with the
PHI argument. We then change the PHI argument to use our new
instead of the PHI's underlying variable.
You might think we could register partitions for the out-of-ssa
translation here and avoid a second walk of the PHI nodes. No
such luck since the size of the var map will change if we have
to manually take variables out of SSA form here. */
FOR_EACH_BB (bb)
{
for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
{
tree result = SSA_NAME_VAR (PHI_RESULT (phi));
/* If the definition is marked for renaming, then we need
to do nothing more for this PHI node. */
if (bitmap_bit_p (vars, var_ann (result)->uid))
continue;
/* Look at all the arguments and see if any of them are
marked for renaming. If so, we need to handle them
specially. */
for (i = 0; i < PHI_NUM_ARGS (phi); i++)
{
tree arg = PHI_ARG_DEF (phi, i);
/* If the argument is not an SSA_NAME, then we can ignore
this argument. */
if (TREE_CODE (arg) != SSA_NAME)
continue;
/* If this argument is marked for renaming, then we need
to undo the copy propagation so that we can take
the argument out of SSA form without taking the
result out of SSA form. */
arg = SSA_NAME_VAR (arg);
if (bitmap_bit_p (vars, var_ann (arg)->uid))
{
tree new_name, copy;
/* Get a new SSA_NAME for the copy, it is based on
the result, not the argument! We use the PHI
as the definition since we haven't created the
definition statement yet. */
new_name = make_ssa_name (result, phi);
/* Now create the copy statement. */
copy = build (MODIFY_EXPR, TREE_TYPE (arg),
new_name, PHI_ARG_DEF (phi, i));
/* Now update SSA_NAME_DEF_STMT to point to the
newly created statement. */
SSA_NAME_DEF_STMT (new_name) = copy;
/* Now make the argument reference our new SSA_NAME. */
SET_PHI_ARG_DEF (phi, i, new_name);
/* Queue the statement for insertion. */
bsi_insert_on_edge (PHI_ARG_EDGE (phi, i), copy);
modify_stmt (copy);
}
}
}
}
/* If any copies were inserted on edges, actually insert them now. */
bsi_commit_edge_inserts (NULL);
/* Now register partitions for all instances of the variables we
are taking out of SSA form. */
map = init_var_map (num_ssa_names + 1);
register_ssa_partitions_for_vars (vars, map);
/* Now that we have all the partitions registered, translate the
appropriate variables out of SSA form. */
ssa_flags = SSANORM_COALESCE_PARTITIONS;
if (flag_tree_combine_temps)
ssa_flags |= SSANORM_COMBINE_TEMPS;
remove_ssa_form (dump_file, map, ssa_flags);
/* And finally, reset the out_of_ssa flag for each of the vars
we just took out of SSA form. */
EXECUTE_IF_SET_IN_BITMAP (vars, 0, i,
{
var_ann (referenced_var (i))->out_of_ssa_tag = 0;
});
/* Free the map as we are done with it. */
delete_var_map (map);
}
}
/* Take the current function out of SSA form, as described in
R. Morgan, ``Building an Optimizing Compiler'',
Butterworth-Heinemann, Boston, MA, 1998. pp 176-186. */

View File

@ -335,17 +335,24 @@ add_phi_arg (tree *phi, tree def, edge e)
release the old PHI node. */
if (*phi != old_phi)
{
/* Extract the basic block for the PHI from the PHI's annotation
rather than the edge. This works better as the edge's
destination may not currently be the block with the PHI
node if we are in the process of threading the edge to
a new destination. */
basic_block bb = bb_for_stmt (*phi);
release_phi_node (old_phi);
/* Update the list head if replacing the first listed phi. */
if (phi_nodes (e->dest) == old_phi)
bb_ann (e->dest)->phi_nodes = *phi;
if (phi_nodes (bb) == old_phi)
bb_ann (bb)->phi_nodes = *phi;
else
{
/* Traverse the list looking for the phi node to chain to. */
tree p;
for (p = phi_nodes (e->dest);
for (p = phi_nodes (bb);
p && PHI_CHAIN (p) != old_phi;
p = PHI_CHAIN (p))
;

View File

@ -63,6 +63,7 @@ static htab_t avail_exprs;
have to perform in lookup_avail_expr and finally it allows us to
significantly reduce the number of calls into the hashing routine
itself. */
struct expr_hash_elt
{
/* The value (lhs) of this expression. */
@ -160,18 +161,6 @@ struct vrp_element
static struct opt_stats_d opt_stats;
/* This virtual array holds pairs of edges which describe a scheduled
edge redirection from jump threading.
The first entry in each pair is the edge we are going to redirect.
The second entry in each pair is the edge leading to our final
destination block. By providing this as an edge rather than the
final target block itself we can correctly handle redirections
when the target block had PHIs which required edge insertions/splitting
to remove the PHIs. */
static GTY(()) varray_type redirection_edges;
/* A virtual array holding value range records for the variable identified
by the index, SSA_VERSION. */
static varray_type vrp_data;
@ -267,7 +256,6 @@ static void restore_vars_to_original_value (varray_type locals,
static void restore_currdefs_to_original_value (varray_type locals,
unsigned limit);
static void register_definitions_for_stmt (stmt_ann_t, varray_type *);
static void redirect_edges_and_update_ssa_graph (varray_type);
static edge single_incoming_edge_ignoring_loop_edges (basic_block);
/* Local version of fold that doesn't introduce cruft. */
@ -301,240 +289,6 @@ set_value_for (tree var, tree value, varray_type table)
VARRAY_TREE (table, SSA_NAME_VERSION (var)) = value;
}
/* REDIRECTION_EDGES contains edge pairs where we want to revector the
destination of the first edge to the destination of the second edge.
These redirections may significantly change the SSA graph since we
allow redirection through blocks with PHI nodes and blocks with
real instructions in some cases.
This routine will perform the requested redirections and incrementally
update the SSA graph.
Note in some cases requested redirections may be ignored as they can
not be safely implemented. */
static void
redirect_edges_and_update_ssa_graph (varray_type redirection_edges)
{
basic_block tgt, bb;
tree phi;
unsigned int i;
size_t old_num_referenced_vars = num_referenced_vars;
bitmap virtuals_to_rename = BITMAP_XMALLOC ();
/* First note any variables which we are going to have to take
out of SSA form as well as any virtuals which need updating. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
{
block_stmt_iterator bsi;
edge e;
basic_block tgt;
tree phi;
e = VARRAY_EDGE (redirection_edges, i);
tgt = VARRAY_EDGE (redirection_edges, i + 1)->dest;
/* All variables referenced in PHI nodes we bypass must be
renamed. */
for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi))
{
tree result = SSA_NAME_VAR (PHI_RESULT (phi));
if (is_gimple_reg (PHI_RESULT (phi)))
bitmap_set_bit (vars_to_rename, var_ann (result)->uid);
else
bitmap_set_bit (virtuals_to_rename, var_ann (result)->uid);
}
/* Any variables set by statements at the start of the block we
are bypassing must also be taken our of SSA form. */
for (bsi = bsi_start (e->dest); ! bsi_end_p (bsi); bsi_next (&bsi))
{
unsigned int j;
def_optype defs;
v_may_def_optype v_may_defs;
v_must_def_optype v_must_defs;
tree stmt = bsi_stmt (bsi);
stmt_ann_t ann = stmt_ann (stmt);
if (TREE_CODE (stmt) == COND_EXPR)
break;
get_stmt_operands (stmt);
defs = DEF_OPS (ann);
for (j = 0; j < NUM_DEFS (defs); j++)
{
tree op = DEF_OP (defs, j);
tree var = SSA_NAME_VAR (op);
bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
}
v_may_defs = STMT_V_MAY_DEF_OPS (stmt);
for (j = 0; j < NUM_V_MAY_DEFS (v_may_defs); j++)
{
tree op = V_MAY_DEF_RESULT (v_may_defs, j);
tree var = SSA_NAME_VAR (op);
bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
}
v_must_defs = STMT_V_MUST_DEF_OPS (stmt);
for (j = 0; j < NUM_V_MUST_DEFS (v_must_defs); j++)
{
tree op = V_MUST_DEF_OP (v_must_defs, j);
tree var = SSA_NAME_VAR (op);
bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
}
}
/* Finally, any variables in PHI nodes at our final destination
must also be taken out of SSA form. */
for (phi = phi_nodes (tgt); phi; phi = PHI_CHAIN (phi))
{
tree result = SSA_NAME_VAR (PHI_RESULT (phi));
if (is_gimple_reg (PHI_RESULT (phi)))
bitmap_set_bit (vars_to_rename, var_ann (result)->uid);
else
bitmap_set_bit (virtuals_to_rename, var_ann (result)->uid);
}
}
/* Take those selected variables out of SSA form. This must be
done before we start redirecting edges. */
if (bitmap_first_set_bit (vars_to_rename) >= 0)
rewrite_vars_out_of_ssa (vars_to_rename);
/* The out of SSA translation above may split the edge from
E->src to E->dest. This could potentially cause us to lose
an assignment leading to invalid warnings about uninitialized
variables or incorrect code.
Luckily, we can detect this by looking at the last statement
in E->dest. If it is not a COND_EXPR or SWITCH_EXPR, then
the edge was split and instead of E, we want E->dest->succ. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
{
edge e = VARRAY_EDGE (redirection_edges, i);
tree last = last_stmt (e->dest);
if (last
&& TREE_CODE (last) != COND_EXPR
&& TREE_CODE (last) != SWITCH_EXPR)
{
e = e->dest->succ;
#ifdef ENABLE_CHECKING
/* There should only be a single successor if the
original edge was split. */
if (e->succ_next)
abort ();
#endif
/* Replace the edge in REDIRECTION_EDGES for the
loop below. */
VARRAY_EDGE (redirection_edges, i) = e;
}
}
/* If we created any new variables as part of the out-of-ssa
translation, then any jump threads must be invalidated if they
bypass a block in which we skipped instructions.
This is necessary as instructions which appeared to be NOPS
may be necessary after the out-of-ssa translation. */
if (num_referenced_vars != old_num_referenced_vars)
{
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
{
block_stmt_iterator bsi;
edge e;
e = VARRAY_EDGE (redirection_edges, i);
for (bsi = bsi_start (e->dest); ! bsi_end_p (bsi); bsi_next (&bsi))
{
tree stmt = bsi_stmt (bsi);
if (IS_EMPTY_STMT (stmt)
|| TREE_CODE (stmt) == LABEL_EXPR)
continue;
if (TREE_CODE (stmt) == COND_EXPR)
break;
/* Invalidate the jump thread. */
VARRAY_EDGE (redirection_edges, i) = NULL;
VARRAY_EDGE (redirection_edges, i + 1) = NULL;
break;
}
}
}
/* Now redirect the edges. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
{
basic_block src;
edge e;
e = VARRAY_EDGE (redirection_edges, i);
if (!e)
continue;
tgt = VARRAY_EDGE (redirection_edges, i + 1)->dest;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Threaded jump %d --> %d to %d\n",
e->src->index, e->dest->index, tgt->index);
src = e->src;
e = redirect_edge_and_branch (e, tgt);
PENDING_STMT (e) = NULL_TREE;
/* Updating the dominance information would be nontrivial. */
free_dominance_info (CDI_DOMINATORS);
if ((dump_file && (dump_flags & TDF_DETAILS))
&& e->src != src)
fprintf (dump_file, " basic block %d created\n",
e->src->index);
cfg_altered = true;
}
VARRAY_CLEAR (redirection_edges);
for (i = old_num_referenced_vars; i < num_referenced_vars; i++)
{
bitmap_set_bit (vars_to_rename, i);
var_ann (referenced_var (i))->out_of_ssa_tag = 0;
}
bitmap_a_or_b (vars_to_rename, vars_to_rename, virtuals_to_rename);
/* We must remove any PHIs for virtual variables that we are going to
re-rename. Hopefully we'll be able to simply update these incrementally
soon. */
FOR_EACH_BB (bb)
{
tree next;
for (phi = phi_nodes (bb); phi; phi = next)
{
tree result = PHI_RESULT (phi);
next = PHI_CHAIN (phi);
if (bitmap_bit_p (virtuals_to_rename,
var_ann (SSA_NAME_VAR (result))->uid))
remove_phi_node (phi, NULL, bb);
}
}
BITMAP_XFREE (virtuals_to_rename);
}
/* Jump threading, redundancy elimination and const/copy propagation.
This pass may expose new symbols that need to be renamed into SSA. For
@ -544,7 +298,6 @@ redirect_edges_and_update_ssa_graph (varray_type redirection_edges)
static void
tree_ssa_dominator_optimize (void)
{
basic_block bb;
struct dom_walk_data walk_data;
unsigned int i;
@ -560,7 +313,6 @@ tree_ssa_dominator_optimize (void)
avail_exprs = htab_create (1024, real_avail_expr_hash, avail_expr_eq, free);
VARRAY_TREE_INIT (const_and_copies, num_ssa_names, "const_and_copies");
nonzero_vars = BITMAP_XMALLOC ();
VARRAY_EDGE_INIT (redirection_edges, 20, "redirection_edges");
VARRAY_GENERIC_PTR_INIT (vrp_data, num_ssa_names, "vrp_data");
need_eh_cleanup = BITMAP_XMALLOC ();
@ -583,11 +335,6 @@ tree_ssa_dominator_optimize (void)
/* Now initialize the dominator walker. */
init_walk_dominator_tree (&walk_data);
/* Reset block_forwardable in each block's annotation. We use that
attribute when threading through COND_EXPRs. */
FOR_EACH_BB (bb)
bb_ann (bb)->forwardable = 1;
calculate_dominance_info (CDI_DOMINATORS);
/* If we prove certain blocks are unreachable, then we want to
@ -603,43 +350,36 @@ tree_ssa_dominator_optimize (void)
/* Recursively walk the dominator tree optimizing statements. */
walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR);
/* Wipe the hash tables. */
if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
redirect_edges_and_update_ssa_graph (redirection_edges);
if (bitmap_first_set_bit (need_eh_cleanup) >= 0)
{
cfg_altered = tree_purge_all_dead_eh_edges (need_eh_cleanup);
bitmap_zero (need_eh_cleanup);
}
/* We may have made some basic blocks unreachable, remove them. */
cfg_altered |= delete_unreachable_blocks ();
/* If the CFG was altered, then recompute the dominator tree. This
is not strictly needed if we only removed unreachable blocks, but
may produce better results. If we threaded jumps, then rebuilding
the dominator tree is strictly necessary. Likewise with EH cleanup.
Free the dominance info first so that cleanup_tree_cfg doesn't try
to verify it. */
if (cfg_altered)
{
free_dominance_info (CDI_DOMINATORS);
cleanup_tree_cfg ();
calculate_dominance_info (CDI_DOMINATORS);
}
/* If we are going to iterate (CFG_ALTERED is true), then we must
perform any queued renaming before the next iteration. */
if (cfg_altered
&& bitmap_first_set_bit (vars_to_rename) >= 0)
/* If we exposed any new variables, go ahead and put them into
SSA form now, before we handle jump threading. This simplifies
interactions between rewriting of _DECL nodes into SSA form
and rewriting SSA_NAME nodes into SSA form after block
duplication and CFG manipulation. */
if (bitmap_first_set_bit (vars_to_rename) >= 0)
{
rewrite_into_ssa (false);
bitmap_clear (vars_to_rename);
}
/* The into SSA translation may have created new SSA_NAMES whic
affect the size of CONST_AND_COPIES and VRP_DATA. */
/* Thread jumps, creating duplicate blocks as needed. */
cfg_altered = thread_through_all_blocks ();
/* Removal of statements may make some EH edges dead. Purge
such edges from the CFG as needed. */
if (bitmap_first_set_bit (need_eh_cleanup) >= 0)
{
cfg_altered |= tree_purge_all_dead_eh_edges (need_eh_cleanup);
bitmap_zero (need_eh_cleanup);
}
free_dominance_info (CDI_DOMINATORS);
cfg_altered = cleanup_tree_cfg ();
calculate_dominance_info (CDI_DOMINATORS);
rewrite_ssa_into_ssa ();
if (VARRAY_ACTIVE_SIZE (const_and_copies) <= num_ssa_names)
{
VARRAY_GROW (const_and_copies, num_ssa_names);
VARRAY_GROW (vrp_data, num_ssa_names);
}
@ -655,9 +395,6 @@ tree_ssa_dominator_optimize (void)
}
while (cfg_altered);
/* Remove any unreachable blocks left behind and linearize the CFG. */
cleanup_tree_cfg ();
/* Debugging dumps. */
if (dump_file && (dump_flags & TDF_STATS))
dump_dominator_optimization_stats (dump_file);
@ -946,23 +683,11 @@ thread_across_edge (struct dom_walk_data *walk_data, edge e)
/* If we have a known destination for the conditional, then
we can perform this optimization, which saves at least one
conditional jump each time it applies since we get to
bypass the conditional at our original destination.
Note that we can either thread through a block with PHIs
or to a block with PHIs, but not both. At this time the
bookkeeping to keep the CFG & SSA up-to-date has proven
difficult. */
bypass the conditional at our original destination. */
if (dest)
{
int saved_forwardable = bb_ann (e->src)->forwardable;
edge tmp_edge;
bb_ann (e->src)->forwardable = 0;
tmp_edge = tree_block_forwards_to (dest);
taken_edge = (tmp_edge ? tmp_edge : taken_edge);
bb_ann (e->src)->forwardable = saved_forwardable;
VARRAY_PUSH_EDGE (redirection_edges, e);
VARRAY_PUSH_EDGE (redirection_edges, taken_edge);
e->aux = taken_edge;
bb_ann (e->dest)->incoming_edge_threaded = true;
}
}
}

View File

@ -1827,95 +1827,3 @@ dump_live_info (FILE *f, tree_live_info_p live, int flag)
}
}
}
/* Register partitions in MAP so that we can take VARS out of SSA form.
This requires a walk over all the PHI nodes and all the statements. */
void
register_ssa_partitions_for_vars (bitmap vars, var_map map)
{
basic_block bb;
if (bitmap_first_set_bit (vars) >= 0)
{
/* Find every instance (SSA_NAME) of variables in VARs and
register a new partition for them. This requires examining
every statement and every PHI node once. */
FOR_EACH_BB (bb)
{
block_stmt_iterator bsi;
tree next;
tree phi;
/* Register partitions for SSA_NAMEs appearing in the PHI
nodes in this basic block.
Note we delete PHI nodes in this loop if they are
associated with virtual vars which are going to be
renamed. */
for (phi = phi_nodes (bb); phi; phi = next)
{
tree result = SSA_NAME_VAR (PHI_RESULT (phi));
next = PHI_CHAIN (phi);
if (bitmap_bit_p (vars, var_ann (result)->uid))
{
if (! is_gimple_reg (result))
remove_phi_node (phi, NULL_TREE, bb);
else
{
int i;
/* Register a partition for the result. */
register_ssa_partition (map, PHI_RESULT (phi), 0);
/* Register a partition for each argument as needed. */
for (i = 0; i < PHI_NUM_ARGS (phi); i++)
{
tree arg = PHI_ARG_DEF (phi, i);
if (TREE_CODE (arg) != SSA_NAME)
continue;
if (!bitmap_bit_p (vars,
var_ann (SSA_NAME_VAR (arg))->uid))
continue;
register_ssa_partition (map, arg, 1);
}
}
}
}
/* Now register partitions for SSA_NAMEs appearing in each
statement in this block. */
for (bsi = bsi_start (bb); ! bsi_end_p (bsi); bsi_next (&bsi))
{
stmt_ann_t ann = stmt_ann (bsi_stmt (bsi));
use_optype uses = USE_OPS (ann);
def_optype defs = DEF_OPS (ann);
unsigned int i;
for (i = 0; i < NUM_USES (uses); i++)
{
tree op = USE_OP (uses, i);
if (TREE_CODE (op) == SSA_NAME
&& bitmap_bit_p (vars, var_ann (SSA_NAME_VAR (op))->uid))
register_ssa_partition (map, op, 1);
}
for (i = 0; i < NUM_DEFS (defs); i++)
{
tree op = DEF_OP (defs, i);
if (TREE_CODE (op) == SSA_NAME
&& bitmap_bit_p (vars,
var_ann (SSA_NAME_VAR (op))->uid))
register_ssa_partition (map, op, 0);
}
}
}
}
}

View File

@ -58,22 +58,12 @@ typedef struct _var_map
#define VARMAP_NORMAL 0
#define VARMAP_NO_SINGLE_DEFS 1
/* Flags to pass to remove_ssa_form. */
#define SSANORM_PERFORM_TER 0x1
#define SSANORM_COMBINE_TEMPS 0x2
#define SSANORM_REMOVE_ALL_PHIS 0x4
#define SSANORM_COALESCE_PARTITIONS 0x8
#define SSANORM_USE_COALESCE_LIST 0x10
extern var_map init_var_map (int);
extern void delete_var_map (var_map);
extern void dump_var_map (FILE *, var_map);
extern int var_union (var_map, tree, tree);
extern void change_partition_var (var_map, tree, int);
extern void compact_var_map (var_map, int);
extern void remove_ssa_form (FILE *, var_map, int);
extern void register_ssa_partitions_for_vars (bitmap vars, var_map map);
extern tree make_ssa_temp (tree);
static inline int num_var_partitions (var_map);

421
gcc/tree-ssa-threadupdate.c Normal file
View File

@ -0,0 +1,421 @@
/* Thread edges through blocks and update the control flow and SSA graphs.
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "flags.h"
#include "rtl.h"
#include "tm_p.h"
#include "ggc.h"
#include "basic-block.h"
#include "output.h"
#include "errors.h"
#include "expr.h"
#include "function.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "tree-pass.h"
/* Given a block B, update the CFG and SSA graph to reflect redirecting
one or more in-edges to B to instead reach the destination of an
out-edge from B while preserving any side effects in B.
ie, given A->B and B->C, change A->B to be A->C yet still preserve the
side effects of executing B.
1. Make a copy of B (including its outgoing edges and statements). Call
the copy B'. Note B' has no incoming edges or PHIs at this time.
2. Remove the control statement at the end of B' and all outgoing edges
except B'->C.
3. Add a new argument to each PHI in C with the same value as the existing
argument associated with edge B->C. Associate the new PHI arguments
with the edge B'->C.
4. For each PHI in B, find or create a PHI in B' with an identical
PHI_RESULT. Add an argument to the PHI in B' which as the same
value as the PHI in B associated with the edge A->B. Associate
the new argument in the PHI in B' with the edge A->B.
5. Change the edge A->B to A->B'.
5a. This automatically deletes any PHI arguments associated with the
edge A->B in B.
5b. This automatically associates each new argument added in step 4
with the edge A->B'.
6. Repeat for other incoming edges into B.
7. Put the duplicated resources in B and all the B' blocks into SSA form.
Note that block duplication can be minimized by first collecting the
the set of unique destination blocks that the incoming edges should
be threaded to. Block duplication can be further minimized by using
B instead of creating B' for one destination if all edges into B are
going to be threaded to a successor of B. */
/* Main data structure recording information regarding B's duplicate
blocks. */
struct redirection_data
{
/* A duplicate of B with the trailing control statement removed and which
targets a single successor of B. */
basic_block dup_block;
/* An outgoing edge from B. DUP_BLOCK will have OUTGOING_EDGE->dest as
its single successor. */
edge outgoing_edge;
};
/* For each PHI node in BB, find or create a PHI node in NEW_BB for the
same PHI_RESULT. Add an argument to the PHI node in NEW_BB which
corresponds to the same PHI argument associated with edge E in BB. */
static void
copy_phis_to_block (basic_block new_bb, basic_block bb, edge e)
{
tree phi, arg;
/* Walk over every PHI in BB. */
for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
{
tree new_phi;
/* First try to find a PHI node in NEW_BB which has the same
PHI_RESULT as the PHI from BB we are currently processing. */
for (new_phi = phi_nodes (new_bb); new_phi;
new_phi = PHI_CHAIN (new_phi))
if (PHI_RESULT (new_phi) == PHI_RESULT (phi))
break;
/* If we did not find a suitable PHI in NEW_BB, create one. */
if (!new_phi)
new_phi = create_phi_node (PHI_RESULT (phi), new_bb);
/* Extract the argument corresponding to E from the current PHI
node in BB. */
arg = PHI_ARG_DEF_TREE (phi, phi_arg_from_edge (phi, e));
/* Now add that same argument to the new PHI node in block NEW_BB. */
add_phi_arg (&new_phi, arg, e);
}
}
/* Remove the last statement in block BB which must be a COND_EXPR or
SWITCH_EXPR. Also remove all outgoing edges except the edge which
reaches DEST_BB.
This is only used by jump threading which knows the last statement in
BB should be a COND_EXPR or SWITCH_EXPR. If the block ends with any other
statement, then we abort. */
static void
remove_last_stmt_and_useless_edges (basic_block bb, basic_block dest_bb)
{
block_stmt_iterator bsi;
edge e, next;
bsi = bsi_last (bb);
#ifdef ENABLE_CHECKING
if (TREE_CODE (bsi_stmt (bsi)) != COND_EXPR
&& TREE_CODE (bsi_stmt (bsi)) != SWITCH_EXPR)
abort ();
#endif
bsi_remove (&bsi);
next = NULL;
for (e = bb->succ; e; e = next)
{
next = e->succ_next;
if (e->dest != dest_bb)
ssa_remove_edge (e);
}
/* BB now has a single outgoing edge. We need to update the flags for
that single outgoing edge. */
bb->succ->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
bb->succ->flags |= EDGE_FALLTHRU;
}
/* Create a duplicate of BB which only reaches the destination of the edge
stored in RD. Record the duplicate block in RD. */
static void
create_block_for_threading (basic_block bb, struct redirection_data *rd)
{
tree phi;
/* We can use the generic block duplication code and simply remove
the stuff we do not need. */
rd->dup_block = duplicate_block (bb, NULL);
/* The call to duplicate_block will copy everything, including the
useless COND_EXPR or SWITCH_EXPR at the end of the block. We just remove
the useless COND_EXPR or SWITCH_EXPR here rather than having a
specialized block copier. */
remove_last_stmt_and_useless_edges (rd->dup_block, rd->outgoing_edge->dest);
/* If there are any PHI nodes at the destination of the outgoing edge
from the duplicate block, then we will need to add a new argument
to them. The argument should have the same value as the argument
associated with the outgoing edge stored in RD. */
for (phi = phi_nodes (rd->dup_block->succ->dest); phi;
phi = PHI_CHAIN (phi))
{
int indx = phi_arg_from_edge (phi, rd->outgoing_edge);
add_phi_arg (&phi, PHI_ARG_DEF_TREE (phi, indx), rd->dup_block->succ);
}
}
/* BB is a block which ends with a COND_EXPR or SWITCH_EXPR and when BB
is reached via one or more specific incoming edges, we know which
outgoing edge from BB will be traversed.
We want to redirect those incoming edges to the target of the
appropriate outgoing edge. Doing so avoids a conditional branch
and may expose new optimization opportunities. Note that we have
to update dominator tree and SSA graph after such changes.
The key to keeping the SSA graph update managable is to duplicate
the side effects occuring in BB so that those side effects still
occur on the paths which bypass BB after redirecting edges.
We accomplish this by creating duplicates of BB and arranging for
the duplicates to unconditionally pass control to one specific
successor of BB. We then revector the incoming edges into BB to
the appropriate duplicate of BB.
BB and its duplicates will have assignments to the same set of
SSA_NAMEs. Right now, we just call into rewrite_ssa_into_ssa
to update the SSA graph for those names.
We are also going to experiment with a true incremental update
scheme for the duplicated resources. Of of the interesting
properties we can exploit here is that all the resources set
in BB will have the same IDFS, so we have one IDFS computation
per block with incoming threaded edges, which can lower the
cost of the true incremental update algorithm. */
static void
thread_block (basic_block bb)
{
/* E is an incoming edge into BB that we may or may not want to
redirect to a duplicate of BB. */
edge e;
/* The next edge in a predecessor list. Used in loops where E->pred_next
may change within the loop. */
edge next;
/* ALL indicates whether or not all incoming edges into BB should
be threaded to a duplicate of BB. */
bool all = true;
/* Main data structure to hold information for duplicates of BB. */
varray_type redirection_data;
unsigned int i;
VARRAY_GENERIC_PTR_INIT (redirection_data, 2, "redirection data");
/* Look at each incoming edge into BB. Record each unique outgoing
edge that we want to thread an incoming edge to. Also note if
all incoming edges are threaded or not. */
for (e = bb->pred; e; e = e->pred_next)
{
if (!e->aux)
{
all = false;
}
else
{
unsigned int i;
/* See if we can find an entry for the destination of this
threaded edge that has already been recorded. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
{
struct redirection_data *rd;
edge e2;
rd = VARRAY_GENERIC_PTR (redirection_data, i);
e2 = e->aux;
if (e2->dest == rd->outgoing_edge->dest)
break;
}
/* If the loop did not terminate early, then we have a new
destination for the incoming threaded edges. Record it. */
if (i == VARRAY_ACTIVE_SIZE (redirection_data))
{
struct redirection_data *rd;
rd = xcalloc (1, sizeof (redirection_data));
rd->outgoing_edge = e->aux;
VARRAY_PUSH_GENERIC_PTR (redirection_data, rd);
}
}
}
/* Now create duplicates of BB. Note that if all incoming edges are
threaded, then BB is going to become unreachable. In that case
we use BB for one of the duplicates rather than wasting memory
duplicating BB. Thus the odd starting condition for the loop. */
for (i = (all ? 1 : 0); i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
{
struct redirection_data *rd = VARRAY_GENERIC_PTR (redirection_data, i);
create_block_for_threading (bb, rd);
}
/* The loop above created the duplicate blocks (and the statements
within the duplicate blocks). This loop creates PHI nodes for the
duplicated blocks and redirects the incoming edges into BB to reach
the duplicates of BB.
Note that redirecting the edge will change e->pred_next, so we have
to hold e->pred_next in a temporary.
If this turns out to be a performance problem, then we could create
a list of incoming edges associated with each entry in
REDIRECTION_DATA and walk over that list of edges instead. */
next = NULL;
for (e = bb->pred; e; e = next)
{
edge new_dest = e->aux;
next = e->pred_next;
/* E was not threaded, then there is nothing to do. */
if (!new_dest)
continue;
/* Go ahead and clear E->aux. It's not needed anymore and failure
to clear it will cause all kinds of unpleasant problems later. */
e->aux = NULL;
/* We know E is an edge we want to thread. Find the entry associated
with E's new destination in the REDIRECTION_DATA array. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
{
struct redirection_data *rd;
rd = VARRAY_GENERIC_PTR (redirection_data, i);
/* We have found the right entry if the outgoing edge in this
entry matches E's new destination. Note that if we have not
created a duplicate block (rd->dup_block is NULL), then we
are going to re-use BB as a duplicate and we do not need
to create PHI nodes or redirect the edge. */
if (rd->outgoing_edge == new_dest && rd->dup_block)
{
edge e2;
copy_phis_to_block (rd->dup_block, bb, e);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Threaded jump %d --> %d to %d\n",
e->src->index, e->dest->index, rd->dup_block->index);
e2 = redirect_edge_and_branch (e, rd->dup_block);
PENDING_STMT (e2) = NULL;
if ((dump_file && (dump_flags & TDF_DETAILS))
&& e->src != e2->src)
fprintf (dump_file, " basic block %d created\n",
e2->src->index);
break;
}
}
}
/* If all the incoming edges where threaded, then we used BB as one
of the duplicate blocks. We need to fixup BB in that case so that
it no longer has a COND_EXPR or SWITCH_EXPR and reaches one destination
unconditionally. */
if (all)
{
struct redirection_data *rd;
rd = VARRAY_GENERIC_PTR (redirection_data, 0);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Threaded jump %d --> %d to %d\n",
bb->pred->src->index, bb->index, bb->succ->dest->index);
remove_last_stmt_and_useless_edges (bb, rd->outgoing_edge->dest);
}
/* Done with this block. Free any memory we have allocated, clear
REDIRECTION_DATA and unmark this block as needing incoming
edge redirections. */
for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
{
struct redirection_data *rd = VARRAY_GENERIC_PTR (redirection_data, i);
free (rd);
}
VARRAY_CLEAR (redirection_data);
}
/* Walk through all blocks and thread incoming edges to the block's
destinations as requested. This is the only entry point into this
file.
Blocks which have one or more incoming edges have INCOMING_EDGE_THREADED
set in the block's annotation.
this routine.
Each edge that should be threaded has the new destination edge stored in
the original edge's AUX field.
This routine (or one of its callees) will clear INCOMING_EDGE_THREADED
in the block annotations and the AUX field in the edges.
It is the caller's responsibility to fix the dominance information
and rewrite duplicated SSA_NAMEs back into SSA form.
Returns true if one or more edges were threaded, false otherwise. */
bool
thread_through_all_blocks (void)
{
basic_block bb;
bool retval = false;
FOR_EACH_BB (bb)
{
if (bb_ann (bb)->incoming_edge_threaded)
{
thread_block (bb);
retval = true;
bb_ann (bb)->incoming_edge_threaded = false;
}
}
return retval;
}

View File

@ -3770,4 +3770,7 @@ extern bool in_gimple_form;
tree lower_bound_in_type (tree, tree);
tree upper_bound_in_type (tree, tree);
/* In tree-ssa-threadupdate.c. */
extern bool thread_through_all_blocks (void);
#endif /* GCC_TREE_H */