diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 924532b8889..359f0cfb79e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,32 @@ +2004-08-09 Jeff Law + + * 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 * explow.c (memory_address): Use memory_address_p. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 8f33f93f6cf..984bb9e0bdd 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 \ diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 86ac7ee3a92..c9bc37ce6b9 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -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; } diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index f55943b5cdb..d470035470e 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -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 *); diff --git a/gcc/tree-outof-ssa.c b/gcc/tree-outof-ssa.c index 1fef266646d..33a927e14ae 100644 --- a/gcc/tree-outof-ssa.c +++ b/gcc/tree-outof-ssa.c @@ -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. */ diff --git a/gcc/tree-phinodes.c b/gcc/tree-phinodes.c index 3b54b08a40d..5bf77360601 100644 --- a/gcc/tree-phinodes.c +++ b/gcc/tree-phinodes.c @@ -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)) ; diff --git a/gcc/tree-ssa-dom.c b/gcc/tree-ssa-dom.c index 036706f9f37..26df9ed7591 100644 --- a/gcc/tree-ssa-dom.c +++ b/gcc/tree-ssa-dom.c @@ -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; } } } diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c index 45df501ee1b..bd5e8fb22ae 100644 --- a/gcc/tree-ssa-live.c +++ b/gcc/tree-ssa-live.c @@ -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); - } - } - } - } -} - diff --git a/gcc/tree-ssa-live.h b/gcc/tree-ssa-live.h index 59967eef3df..b8637950496 100644 --- a/gcc/tree-ssa-live.h +++ b/gcc/tree-ssa-live.h @@ -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); diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c new file mode 100644 index 00000000000..37c893073de --- /dev/null +++ b/gcc/tree-ssa-threadupdate.c @@ -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; +} diff --git a/gcc/tree.h b/gcc/tree.h index 2b71b393065..a75a0ea23db 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -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 */