+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.
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 \
$(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 \
/* 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);
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
verify_flow_info ();
#endif
timevar_pop (TV_TREE_CLEANUP_CFG);
+ return retval;
}
/* 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;
};
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);
/* 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 *);
#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.
/* 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;
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. */
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))
;
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. */
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;
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. */
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
static void
tree_ssa_dominator_optimize (void)
{
- basic_block bb;
struct dom_walk_data walk_data;
unsigned int i;
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 ();
/* 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
/* Recursively walk the dominator tree optimizing statements. */
walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR);
- /* Wipe the hash tables. */
+ /* 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);
+ }
- if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
- redirect_edges_and_update_ssa_graph (redirection_edges);
+ /* 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);
+ 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 ();
+ free_dominance_info (CDI_DOMINATORS);
+ cfg_altered = cleanup_tree_cfg ();
+ calculate_dominance_info (CDI_DOMINATORS);
- /* 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);
- }
+ rewrite_ssa_into_ssa ();
- /* 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 (VARRAY_ACTIVE_SIZE (const_and_copies) <= num_ssa_names)
{
- 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. */
VARRAY_GROW (const_and_copies, num_ssa_names);
VARRAY_GROW (vrp_data, num_ssa_names);
}
}
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);
/* 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;
}
}
}
}
}
}
-
-/* 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);
- }
- }
- }
- }
-}
-
#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);
--- /dev/null
+/* 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;
+}
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 */