diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b984524281d..60ff40a5e82 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,103 @@ +2006-12-10 Andrew MacLeod + + * common.opt (-ftree-lrs): Remove live range splitting option. + * makefile.in: Add tree-ssa-coalesce.o and reduce header dependancies. + * opts.c (decode_options): Remove flag_tree_live_range_split. + * tree-flow.h (struct var_ann_d): Rename fields from root_ to base_. + * tree-flow-inline.h (single_imm_use_p): New. Check for single use. + * tree-outof-ssa.c: Remove header files which aren't needed. + (SSANORM_*): Remove flags. + (print_exprs_edge, coalesce_abnormal_edges, coalesce_phi_operands, + coalesce_result_decls_and_copies, coalesce_asm_operands): Remove. + (coalesce_ssa_name): Move to tree-ssa-coalesce.c. + (assign_vars): Use Basevar instead of root_var structure. + (replace_def_variable): Dont do anything if def is replaceable. + (remove_ssa_form): Integrate functional changes. + (rewrite_out_of_ssa): Remove live-range_split option. + * tree-ssa-coalesce.c: New File for ssa-name coalescing. + (coalesce_cost): Calculate the cost of a coalesce. + (coalesce_cost_bb): Calculate the coalesce cost within a BB. + (coalesce_cost_edge): Calculate the coalesce cost on an edge. + (pop_cost_one_pair): Remove the best coalesce with cost 1 from the list. + (pop_best_coalesce): Remove the best coalesce from the list. + (coalesce_pair_map_hash): Calculate coalesce pair hash. + (coalesce_pair_map_eq): Compare 2 coalesce pairs for hash function. + (create_coalesce_list): Create a coalesce list object. + (delete_coalesce_list): Free a coalesce list object. + (find_coalesce_pair): Find matching pair in the coalesce list. + (add_cost_one_coalesce): Add a coalesce to the "cost one" list. + (add_coalesce): Add a coalesce to the coalesce list. + (compare_pairs): Comparision function to determine pair sorted order. + (num_coalesce_pairs): Number of coalesced pairs. + (first_coalesce_pair, end_coalesce_pair_p, next_coalesce_pair): + Coalesce pair iterator functions. + (sort_coalesce_list): Sort coalesce pairs in order of expense. + (dump_coalesce_list): Show coalesce list. + (ssa_conflicts_new): Create an SSA conflict graph. + (ssa_conflicts_delete): Delete an SSA conflict graph. + (ssa_conflicts_test_p): Test for conflicts. + (ssa_conflicts_add_one): Add a single conflict. + (ssa_conflicts_add): Add a conflict pair. + (ssa_conflicts_merge): Merge conflicts. + (struct live_track_d): Struct for tracking live partitions. + (new_live_track): Create new live_track object. + (delete_live_track): Delete a live_track object. + (live_track_remove_partition): Remove a partition from the live list. + (live_track_add_partition): Add a partition from the live list. + (live_track_clear_var): Take VAR from the live list. + (live_track_live_p): Is var live? + (live_track_process_use): Make var come alive. + (live_track_process_def): Make var go dead, add conflicts. + (live_track_init): Initialize to a live on exit set. + (live_track_clear_base_vars): Clear live partitions. + (build_ssa_conflict_graph): Build a conflict graph. + (print_exprs): Common debug output routine. + (abnormal_corrupt): Output info about a failed coalesce across an + abnormal edge. + (fail_abnormal_edge_coalesce): Output info about a failed MUST_COALESCE. + (create_outofssa_var_map): Create a var map and coalesce list. + (attempt_coalesce): Coalesce a pair. + (coalesce_partitions): Coalesce all pairs in a coalesce list. + (coalesce_ssa_name): Entry point. Determine what ssa_names to coalesce. + * tree-ssa-live.c: Remove header files which aren't needed. + (var_map_base_init): New. Initialize a basevar list. + (var_map_base_fini): New. Finish a basevar list. + (init_var_map): Initialize new fields. + (delete_var_map): Free new fields. + (var_union): Use renamed fields. + (compact_var_map): Remove. + (partition_to_view_init): Use renamed fields, change order of an if. + (partition_view_fini): Use renamed fields. + (partition_view_normal): Create basevar list if requested. + (partition_view_bitmap): Create a view based on a bitmap of partitions. + (change_partition_var): Use renamed fields. + (create_ssa_var_map): Remove. + (tpa_init, tpa_remove_partition, tpa_delete, tpa_compact, + root_var_init): Remove. + (partition_pair_map_hash, partition_pair_map_eq, create_coalesce_list, + delete_coalesce_list, find_partition_pair, coalesce_cost, add_coalesce, + compare_pairs, num_coalesce_pairs, first_partition_pair, + end_partition_pair_p, next_partition_pair, sort_coalesce_list, + pop_best_coalesce, add_conflicts_if_valid, set_if_valid, + build_tree_conflict_graph, coalesce_tpa_members, dump_coalesce_list, + tpa_dump): Moved to tree-ssa-coalesce.c and/or renamed there. + (dump_var_map): Use renamed fields. + * tree-ssa-live.h (struct _var_map): Modify fields. + (partition_to_var, version_to_var, var_to_partition): Use renamed + fields. + (basevar_index): New. Index of the base variable of a partition. + (num_basevars): New. Number of unique base variables in partition map. + (register_ssa_partition): Use renamed fields. + (struct tree_partition_associator_d): Remove. + (tpa_num_trees, tpa_tree, tpa_first_partition, tpa_next_partition, + tpa_find_tree, tpa_decompact, root_var_init, root_var_num, + root_var, root_var_first_partition, root_var_next_partition, + root_var_dump, root_var_delete, root_var_remove_partition, + root_var_find, root_var_compact, root_var_decompact): Remove. + (struct partition_pair, struct coalesce_list_d): Moved to + tree-ssa-coalesce.c + * tree-ssa-ter.c: Remove header files which aren't needed. + 2006-12-10 Steven Bosscher * cse.c: (struct cse_basic_block_data): Remove LAST field. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index bf954ebab88..8da131d5ee0 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -985,7 +985,7 @@ OBJS-common = \ tree-vect-generic.o tree-ssa-loop.o tree-ssa-loop-niter.o \ tree-ssa-loop-manip.o tree-ssa-threadupdate.o tree-ssa-threadedge.o \ tree-vectorizer.o tree-vect-analyze.o tree-vect-transform.o \ - tree-vect-patterns.o tree-ssa-loop-prefetch.o \ + tree-vect-patterns.o tree-ssa-loop-prefetch.o tree-ssa-coalesce.o \ tree-ssa-loop-ivcanon.o tree-ssa-propagate.o tree-ssa-address.o \ tree-ssa-math-opts.o \ tree-ssa-loop-ivopts.o tree-if-conv.o tree-ssa-loop-unswitch.o \ @@ -1851,17 +1851,14 @@ tree-into-ssa.o : tree-into-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ bitmap.h $(CFGLOOP_H) $(FLAGS_H) hard-reg-set.h $(HASHTAB_H) \ $(TREE_GIMPLE_H) $(TREE_INLINE_H) $(VARRAY_H) vecprim.h tree-ssa-ter.o : tree-ssa-ter.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ - $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) output.h $(DIAGNOSTIC_H) \ - $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ - langhooks.h tree-pass.h $(TREE_SSA_LIVE_H) $(BASIC_BLOCK_H) bitmap.h \ - $(FLAGS_H) $(GGC_H) hard-reg-set.h $(HASHTAB_H) $(TREE_GIMPLE_H) \ - $(TREE_INLINE_H) $(VARRAY_H) toplev.h vecprim.h + $(TREE_H) $(DIAGNOSTIC_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ + $(TREE_SSA_LIVE_H) bitmap.h +tree-ssa-coalesce.o : tree-ssa-coalesce.c $(TREE_FLOW_H) $(CONFIG_H) \ + $(SYSTEM_H) $(TREE_H) $(DIAGNOSTIC_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ + $(TREE_SSA_LIVE_H) bitmap.h $(FLAGS_H) $(HASHTAB_H) toplev.h tree-outof-ssa.o : tree-outof-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ - $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) output.h $(DIAGNOSTIC_H) \ - $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ - langhooks.h tree-pass.h $(TREE_SSA_LIVE_H) $(BASIC_BLOCK_H) bitmap.h \ - $(FLAGS_H) $(GGC_H) hard-reg-set.h $(HASHTAB_H) $(TREE_GIMPLE_H) \ - $(TREE_INLINE_H) $(VARRAY_H) toplev.h vecprim.h + $(TREE_H) $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ + tree-pass.h $(TREE_SSA_LIVE_H) $(BASIC_BLOCK_H) bitmap.h $(GGC_H) toplev.h tree-ssa-dse.o : tree-ssa-dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \ $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) domwalk.h $(FLAGS_H) \ @@ -1913,10 +1910,8 @@ tree-phinodes.o : tree-phinodes.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ domwalk.o : domwalk.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_H) $(BASIC_BLOCK_H) $(TREE_FLOW_H) domwalk.h $(GGC_H) tree-ssa-live.o : tree-ssa-live.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ - $(TREE_H) $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) \ - $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_SSA_LIVE_H) $(BASIC_BLOCK_H) \ - bitmap.h $(FLAGS_H) $(HASHTAB_H) $(TREE_GIMPLE_H) $(TREE_INLINE_H) \ - $(VARRAY_H) toplev.h vecprim.h + $(TREE_H) $(DIAGNOSTIC_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ + $(TREE_SSA_LIVE_H) bitmap.h toplev.h tree-ssa-copyrename.o : tree-ssa-copyrename.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(TREE_H) $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) tree-pass.h \ $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_SSA_LIVE_H) $(BASIC_BLOCK_H) \ diff --git a/gcc/common.opt b/gcc/common.opt index 2251b86e34a..7ec6f4723cd 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1003,10 +1003,6 @@ ftree-ter Common Report Var(flag_tree_ter) Replace temporary expressions in the SSA->normal pass -ftree-lrs -Common Report Var(flag_tree_live_range_split) -Perform live range splitting during the SSA->normal pass - ftree-vrp Common Report Var(flag_tree_vrp) Init(0) Perform Value Range Propagation on trees diff --git a/gcc/opts.c b/gcc/opts.c index da16f7bf341..b31d67d94a6 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -449,7 +449,6 @@ decode_options (unsigned int argc, const char **argv) flag_tree_dom = 1; flag_tree_dse = 1; flag_tree_ter = 1; - flag_tree_live_range_split = 1; flag_tree_sra = 1; flag_tree_copyrename = 1; flag_tree_fre = 1; diff --git a/gcc/tree-flow-inline.h b/gcc/tree-flow-inline.h index f19faa1c4ff..bc4c3946335 100644 --- a/gcc/tree-flow-inline.h +++ b/gcc/tree-flow-inline.h @@ -541,6 +541,18 @@ has_single_use (tree var) return (ptr != ptr->next && ptr == ptr->next->next); } + +/* If VAR has only a single immediate use, return true. */ +static inline bool +single_imm_use_p (tree var) +{ + ssa_use_operand_t *ptr; + + ptr = &(SSA_NAME_IMM_USE_NODE (var)); + return (ptr != ptr->next && ptr == ptr->next->next); +} + + /* If VAR has only a single immediate use, return true, and set USE_P and STMT to the use pointer and stmt of occurrence. */ static inline bool diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index b3aa655f556..bb6b4ae2113 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -218,8 +218,8 @@ struct var_ann_d GTY(()) been seen yet or not. */ unsigned out_of_ssa_tag : 1; - /* Used when building root_var structures in tree_ssa_live.[ch]. */ - unsigned root_var_processed : 1; + /* Used when building base variable structures in a var_map. */ + unsigned base_var_processed : 1; /* Nonzero if this variable is in the alias set of another variable. */ unsigned is_aliased : 1; @@ -257,8 +257,8 @@ struct var_ann_d GTY(()) variable represents storage for. */ unsigned partition; - /* Used by the root-var object in tree-ssa-live.[ch]. */ - unsigned root_index; + /* Used by var_map for the base index of ssa base variables. */ + unsigned base_index; /* During into-ssa and the dominator optimizer, this field holds the current version of this variable (an SSA_NAME). */ diff --git a/gcc/tree-outof-ssa.c b/gcc/tree-outof-ssa.c index 5edea642805..f591eb5ed97 100644 --- a/gcc/tree-outof-ssa.c +++ b/gcc/tree-outof-ssa.c @@ -1,5 +1,5 @@ /* Convert a program in SSA form into Normal form. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Andrew Macleod This file is part of GCC. @@ -24,34 +24,17 @@ Boston, MA 02110-1301, USA. */ #include "coretypes.h" #include "tm.h" #include "tree.h" -#include "flags.h" -#include "rtl.h" -#include "tm_p.h" #include "ggc.h" -#include "langhooks.h" -#include "hard-reg-set.h" #include "basic-block.h" -#include "output.h" -#include "expr.h" -#include "function.h" #include "diagnostic.h" #include "bitmap.h" #include "tree-flow.h" -#include "tree-gimple.h" -#include "tree-inline.h" -#include "varray.h" #include "timevar.h" -#include "hashtab.h" #include "tree-dump.h" #include "tree-ssa-live.h" #include "tree-pass.h" #include "toplev.h" -#include "vecprim.h" -/* Flags to pass to remove_ssa_form. */ - -#define SSANORM_PERFORM_TER 0x1 -#define SSANORM_COALESCE_PARTITIONS 0x4 /* 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 @@ -101,36 +84,6 @@ typedef struct _elim_graph { } *elim_graph; -/* Local functions. */ -static tree create_temp (tree); -static void insert_copy_on_edge (edge, tree, tree); -static elim_graph new_elim_graph (int); -static inline void delete_elim_graph (elim_graph); -static inline void clear_elim_graph (elim_graph); -static inline int elim_graph_size (elim_graph); -static inline void elim_graph_add_node (elim_graph, tree); -static inline void elim_graph_add_edge (elim_graph, int, int); -static inline int elim_graph_remove_succ_edge (elim_graph, int); - -static inline void eliminate_name (elim_graph, tree); -static void eliminate_build (elim_graph, basic_block); -static void elim_forward (elim_graph, int); -static int elim_unvisited_predecessor (elim_graph, int); -static void elim_backward (elim_graph, int); -static void elim_create (elim_graph, int); -static void eliminate_phi (edge, elim_graph); -static tree_live_info_p coalesce_ssa_name (var_map, int); -static void assign_vars (var_map); -static bool replace_use_variable (var_map, use_operand_p, tree *); -static bool replace_def_variable (var_map, def_operand_p, tree *); -static void eliminate_virtual_phis (void); -static void coalesce_abnormal_edges (var_map, conflict_graph, root_var_p); -static void print_exprs (FILE *, const char *, tree, const char *, tree, - const char *); -static void print_exprs_edge (FILE *, edge, const char *, tree, const char *, - tree); - - /* Create a temporary variable based on the type of variable T. Use T's name as the prefix. */ @@ -489,6 +442,7 @@ elim_create (elim_graph g, int T) } + /* Eliminate all the phi nodes on edge E in graph G. */ static void @@ -541,407 +495,15 @@ eliminate_phi (edge e, elim_graph g) } -/* Shortcut routine to print messages to file F of the form: - "STR1 EXPR1 STR2 EXPR2 STR3." */ - -static void -print_exprs (FILE *f, const char *str1, tree expr1, const char *str2, - tree expr2, const char *str3) -{ - fprintf (f, "%s", str1); - print_generic_expr (f, expr1, TDF_SLIM); - fprintf (f, "%s", str2); - print_generic_expr (f, expr2, TDF_SLIM); - fprintf (f, "%s", str3); -} - - -/* Shortcut routine to print abnormal edge messages to file F of the form: - "STR1 EXPR1 STR2 EXPR2 across edge E. */ - -static void -print_exprs_edge (FILE *f, edge e, const char *str1, tree expr1, - const char *str2, tree expr2) -{ - print_exprs (f, str1, expr1, str2, expr2, " across an abnormal edge"); - fprintf (f, " from BB%d->BB%d\n", e->src->index, - e->dest->index); -} - - -/* Coalesce partitions in MAP which are live across abnormal edges in GRAPH. - RV is the root variable groupings of the partitions in MAP. Since code - cannot be inserted on these edges, failure to coalesce something across - an abnormal edge is an error. */ - -static void -coalesce_abnormal_edges (var_map map, conflict_graph graph, root_var_p rv) -{ - basic_block bb; - edge e; - tree phi, var, tmp; - int x, y, z; - edge_iterator ei; - - /* Code cannot be inserted on abnormal edges. Look for all abnormal - edges, and coalesce any PHI results with their arguments across - that edge. */ - - FOR_EACH_BB (bb) - FOR_EACH_EDGE (e, ei, bb->succs) - if (e->dest != EXIT_BLOCK_PTR && e->flags & EDGE_ABNORMAL) - for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi)) - { - /* Visit each PHI on the destination side of this abnormal - edge, and attempt to coalesce the argument with the result. */ - var = PHI_RESULT (phi); - x = var_to_partition (map, var); - - /* Ignore results which are not relevant. */ - if (x == NO_PARTITION) - continue; - - tmp = PHI_ARG_DEF (phi, e->dest_idx); -#ifdef ENABLE_CHECKING - if (!phi_ssa_name_p (tmp)) - { - print_exprs_edge (stderr, e, - "\nConstant argument in PHI. Can't insert :", - var, " = ", tmp); - internal_error ("SSA corruption"); - } -#else - gcc_assert (phi_ssa_name_p (tmp)); -#endif - y = var_to_partition (map, tmp); - gcc_assert (x != NO_PARTITION); - gcc_assert (y != NO_PARTITION); -#ifdef ENABLE_CHECKING - if (root_var_find (rv, x) != root_var_find (rv, y)) - { - print_exprs_edge (stderr, e, "\nDifferent root vars: ", - root_var (rv, root_var_find (rv, x)), - " and ", - root_var (rv, root_var_find (rv, y))); - internal_error ("SSA corruption"); - } -#else - gcc_assert (root_var_find (rv, x) == root_var_find (rv, y)); -#endif - - if (x != y) - { -#ifdef ENABLE_CHECKING - if (conflict_graph_conflict_p (graph, x, y)) - { - print_exprs_edge (stderr, e, "\n Conflict ", - partition_to_var (map, x), - " and ", partition_to_var (map, y)); - internal_error ("SSA corruption"); - } -#else - gcc_assert (!conflict_graph_conflict_p (graph, x, y)); -#endif - - /* Now map the partitions back to their real variables. */ - var = partition_to_var (map, x); - tmp = partition_to_var (map, y); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - print_exprs_edge (dump_file, e, - "ABNORMAL: Coalescing ", - var, " and ", tmp); - } - z = var_union (map, var, tmp); -#ifdef ENABLE_CHECKING - if (z == NO_PARTITION) - { - print_exprs_edge (stderr, e, "\nUnable to coalesce", - partition_to_var (map, x), " and ", - partition_to_var (map, y)); - internal_error ("SSA corruption"); - } -#else - gcc_assert (z != NO_PARTITION); -#endif - gcc_assert (z == x || z == y); - if (z == x) - conflict_graph_merge_regs (graph, x, y); - else - conflict_graph_merge_regs (graph, y, x); - } - } -} - -/* Coalesce potential copies via PHI arguments. */ - -static void -coalesce_phi_operands (var_map map, coalesce_list_p cl) -{ - basic_block bb; - tree phi; - - FOR_EACH_BB (bb) - { - for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) - { - tree res = PHI_RESULT (phi); - int p = var_to_partition (map, res); - int x; - - if (p == NO_PARTITION) - continue; - - for (x = 0; x < PHI_NUM_ARGS (phi); x++) - { - tree arg = PHI_ARG_DEF (phi, x); - int p2; - - if (TREE_CODE (arg) != SSA_NAME) - continue; - if (SSA_NAME_VAR (res) != SSA_NAME_VAR (arg)) - continue; - p2 = var_to_partition (map, PHI_ARG_DEF (phi, x)); - if (p2 != NO_PARTITION) - { - edge e = PHI_ARG_EDGE (phi, x); - add_coalesce (cl, p, p2, - coalesce_cost (EDGE_FREQUENCY (e), - maybe_hot_bb_p (bb), - EDGE_CRITICAL_P (e))); - } - } - } - } -} - -/* Coalesce all the result decls together. */ - -static void -coalesce_result_decls (var_map map, coalesce_list_p cl) -{ - unsigned int i, x; - tree var = NULL; - - for (i = x = 0; x < num_var_partitions (map); x++) - { - tree p = partition_to_var (map, x); - if (TREE_CODE (SSA_NAME_VAR (p)) == RESULT_DECL) - { - if (var == NULL_TREE) - { - var = p; - i = x; - } - else - add_coalesce (cl, i, x, - coalesce_cost (EXIT_BLOCK_PTR->frequency, - maybe_hot_bb_p (EXIT_BLOCK_PTR), - false)); - } - } -} - -/* Coalesce matching constraints in asms. */ - -static void -coalesce_asm_operands (var_map map, coalesce_list_p cl) -{ - basic_block bb; - - FOR_EACH_BB (bb) - { - block_stmt_iterator bsi; - for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) - { - tree stmt = bsi_stmt (bsi); - unsigned long noutputs, i; - tree *outputs, link; - - if (TREE_CODE (stmt) != ASM_EXPR) - continue; - - noutputs = list_length (ASM_OUTPUTS (stmt)); - outputs = (tree *) alloca (noutputs * sizeof (tree)); - for (i = 0, link = ASM_OUTPUTS (stmt); link; - ++i, link = TREE_CHAIN (link)) - outputs[i] = TREE_VALUE (link); - - for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link)) - { - const char *constraint - = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); - tree input = TREE_VALUE (link); - char *end; - unsigned long match; - int p1, p2; - - if (TREE_CODE (input) != SSA_NAME && !DECL_P (input)) - continue; - - match = strtoul (constraint, &end, 10); - if (match >= noutputs || end == constraint) - continue; - - if (TREE_CODE (outputs[match]) != SSA_NAME - && !DECL_P (outputs[match])) - continue; - - p1 = var_to_partition (map, outputs[match]); - if (p1 == NO_PARTITION) - continue; - p2 = var_to_partition (map, input); - if (p2 == NO_PARTITION) - continue; - - add_coalesce (cl, p1, p2, coalesce_cost (REG_BR_PROB_BASE, - maybe_hot_bb_p (bb), - false)); - } - } - } -} - -/* Reduce the number of live ranges in MAP. Live range information is - returned if FLAGS indicates that we are combining temporaries, otherwise - NULL is returned. The only partitions which are associated with actual - variables at this point are those which are forced to be coalesced for - various reason. (live on entry, live across abnormal edges, etc.). */ - -static tree_live_info_p -coalesce_ssa_name (var_map map, int flags) -{ - unsigned num, x; - sbitmap live; - root_var_p rv; - tree_live_info_p liveinfo; - conflict_graph graph; - coalesce_list_p cl = NULL; - sbitmap_iterator sbi; - - if (num_var_partitions (map) <= 1) - return NULL; - - liveinfo = calculate_live_ranges (map); - rv = root_var_init (map); - - /* Remove single element variable from the list. */ - root_var_compact (rv); - - cl = create_coalesce_list (map); - - coalesce_phi_operands (map, cl); - coalesce_result_decls (map, cl); - coalesce_asm_operands (map, cl); - - /* Build a conflict graph. */ - graph = build_tree_conflict_graph (liveinfo, rv, cl); - - if (cl) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Before sorting:\n"); - dump_coalesce_list (dump_file, cl); - } - - sort_coalesce_list (cl); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\nAfter sorting:\n"); - dump_coalesce_list (dump_file, cl); - } - } - - /* Put the single element variables back in. */ - root_var_decompact (rv); - - /* First, coalesce all live on entry variables to their root variable. - This will ensure the first use is coming from the correct location. */ - - num = num_var_partitions (map); - live = sbitmap_alloc (num); - sbitmap_zero (live); - - /* Set 'live' vector to indicate live on entry partitions. */ - for (x = 0 ; x < num; x++) - { - tree var = partition_to_var (map, x); - if (gimple_default_def (cfun, SSA_NAME_VAR (var)) == var) - SET_BIT (live, x); - } - - delete_tree_live_info (liveinfo); - liveinfo = NULL; - - /* Assign root variable as partition representative for each live on entry - partition. */ - EXECUTE_IF_SET_IN_SBITMAP (live, 0, x, sbi) - { - tree var = root_var (rv, root_var_find (rv, x)); - var_ann_t ann = var_ann (var); - /* If these aren't already coalesced... */ - if (partition_to_var (map, x) != var) - { - /* This root variable should have not already been assigned - to another partition which is not coalesced with this one. */ - gcc_assert (!ann->out_of_ssa_tag); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - print_exprs (dump_file, "Must coalesce ", - partition_to_var (map, x), - " with the root variable ", var, ".\n"); - } - - change_partition_var (map, var, x); - } - } - - sbitmap_free (live); - - /* Coalesce partitions live across abnormal edges. */ - coalesce_abnormal_edges (map, graph, rv); - - if (dump_file && (dump_flags & TDF_DETAILS)) - dump_var_map (dump_file, map); - - /* Coalesce partitions. */ - coalesce_tpa_members (rv, graph, map, cl, - ((dump_flags & TDF_DETAILS) ? dump_file - : NULL)); - - if (flags & SSANORM_COALESCE_PARTITIONS) - coalesce_tpa_members (rv, graph, map, NULL, - ((dump_flags & TDF_DETAILS) ? dump_file - : NULL)); - if (cl) - delete_coalesce_list (cl); - root_var_delete (rv); - conflict_graph_delete (graph); - - return liveinfo; -} - - /* Take the ssa-name var_map MAP, and assign real variables to each partition. */ static void assign_vars (var_map map) { - int x, i, num, rep; - tree t, var; + int x, num; + tree var, root; var_ann_t ann; - root_var_p rv; - - rv = root_var_init (map); - if (!rv) - return; - - /* Coalescing may already have forced some partitions to their root - variable. Find these and tag them. */ num = num_var_partitions (map); for (x = 0; x < num; x++) @@ -949,63 +511,35 @@ assign_vars (var_map map) var = partition_to_var (map, x); if (TREE_CODE (var) != SSA_NAME) { - /* Coalescing will already have verified that more than one - partition doesn't have the same root variable. Simply marked - the variable as assigned. */ ann = var_ann (var); - ann->out_of_ssa_tag = 1; + /* It must already be coalesced. */ + gcc_assert (ann->out_of_ssa_tag == 1); if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "partition %d has variable ", x); + fprintf (dump_file, "partition %d already has variable ", x); print_generic_expr (dump_file, var, TDF_SLIM); fprintf (dump_file, " assigned to it.\n"); } - } - } - - num = root_var_num (rv); - for (x = 0; x < num; x++) - { - var = root_var (rv, x); - ann = var_ann (var); - for (i = root_var_first_partition (rv, x); - i != ROOT_VAR_NONE; - i = root_var_next_partition (rv, i)) - { - t = partition_to_var (map, i); - - if (t == var || TREE_CODE (t) != SSA_NAME) - continue; - - rep = var_to_partition (map, t); - - if (!ann->out_of_ssa_tag) + else + { + root = SSA_NAME_VAR (var); + ann = var_ann (root); + /* If ROOT is already associated, create a new one. */ + if (ann->out_of_ssa_tag) { - if (dump_file && (dump_flags & TDF_DETAILS)) - print_exprs (dump_file, "", t, " --> ", var, "\n"); - change_partition_var (map, var, rep); - continue; + root = create_temp (root); + ann = var_ann (root); } - - if (dump_file && (dump_flags & TDF_DETAILS)) - print_exprs (dump_file, "", t, " not coalesced with ", var, - ""); - - var = create_temp (t); - change_partition_var (map, var, rep); - ann = var_ann (var); - + /* ROOT has not been coalesced yet, so use it. */ if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, " --> New temp: '"); - print_generic_expr (dump_file, var, TDF_SLIM); - fprintf (dump_file, "'\n"); + fprintf (dump_file, "Partition %d is assigned to var ", x); + print_generic_stmt (dump_file, root, TDF_SLIM); } + change_partition_var (map, root, x); } } - - root_var_delete (rv); } @@ -1028,6 +562,7 @@ replace_use_variable (var_map map, use_operand_p p, tree *expr) { tree new_expr = GIMPLE_STMT_OPERAND (expr[version], 1); SET_USE (p, new_expr); + /* Clear the stmt's RHS, or GC might bite us. */ GIMPLE_STMT_OPERAND (expr[version], 1) = NULL_TREE; return true; @@ -1056,19 +591,9 @@ replace_def_variable (var_map map, def_operand_p def_p, tree *expr) tree new_var; tree var = DEF_FROM_PTR (def_p); - /* Check if we are replacing this variable with an expression. */ - if (expr) - { - int version = SSA_NAME_VERSION (var); - if (expr[version]) - { - tree new_expr = TREE_OPERAND (expr[version], 1); - SET_DEF (def_p, new_expr); - /* Clear the stmt's RHS, or GC might bite us. */ - TREE_OPERAND (expr[version], 1) = NULL_TREE; - return true; - } - } + /* Do nothing if we are replacing this variable with an expression. */ + if (expr && expr[SSA_NAME_VERSION (var)]) + return true; new_var = var_to_partition_to_var (map, var); if (new_var) @@ -1144,15 +669,12 @@ rewrite_trees (var_map map, tree *values) FOR_EACH_BB (bb) { tree phi; - for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) { tree T0 = var_to_partition_to_var (map, PHI_RESULT (phi)); - if (T0 == NULL_TREE) { int i; - for (i = 0; i < PHI_NUM_ARGS (phi); i++) { tree arg = PHI_ARG_DEF (phi, i); @@ -1261,8 +783,10 @@ static edge leader_match = NULL; /* Pass this function to make_forwarder_block so that all the edges with - matching PENDING_STMT lists to 'curr_stmt_list' get redirected. */ -static bool + matching PENDING_STMT lists to 'curr_stmt_list' get redirected. E is the + edge to test for a match. */ + +static inline bool same_stmt_list_p (edge e) { return (e->aux == (PTR) leader_match) ? true : false; @@ -1270,6 +794,7 @@ same_stmt_list_p (edge e) /* Return TRUE if S1 and S2 are equivalent copies. */ + static inline bool identical_copies_p (tree s1, tree s2) { @@ -1293,7 +818,7 @@ identical_copies_p (tree s1, tree s2) } -/* Compare the PENDING_STMT list for two edges, and return true if the lists +/* Compare the PENDING_STMT list for edges E1 and E2. Return true if the lists contain the same sequence of copies. */ static inline bool @@ -1380,6 +905,7 @@ analyze_edges_for_bb (basic_block bb) bsi_commit_one_edge_insert (e, NULL); return; } + /* Find out how many edges there are with interesting pending stmts on them. Commit the stmts on edges we are not interested in. */ FOR_EACH_EDGE (e, ei, bb->preds) @@ -1470,11 +996,9 @@ analyze_edges_for_bb (basic_block bb) return; } - if (dump_file) fprintf (dump_file, "\nOpportunities in BB %d for stmt/block reduction:\n", bb->index); - /* For each common list, create a forwarding block and issue the stmt's in that block. */ @@ -1601,28 +1125,23 @@ perform_edge_inserts (void) } -/* Remove the variables specified in MAP from SSA form. FLAGS indicate what - options should be used. */ +/* Remove the ssa-names in the current function and translate them into normal + compiler variables. PERFORM_TER is true if Temporary Expression Replacement + should also be used. */ static void -remove_ssa_form (var_map map, int flags) +remove_ssa_form (bool perform_ter) { - tree_live_info_p liveinfo; basic_block bb; tree phi, next; tree *values = NULL; + var_map map; - /* If we are not combining temps, don't calculate live ranges for variables - with only one SSA version. */ - compact_var_map (map, VARMAP_NO_SINGLE_DEFS); + map = coalesce_ssa_name (); - if (dump_file && (dump_flags & TDF_DETAILS)) - dump_var_map (dump_file, map); - - liveinfo = coalesce_ssa_name (map, flags); - - /* Make sure even single occurrence variables are in the list now. */ - compact_var_map (map, VARMAP_NORMAL); + /* Return to viewing the variable list as just all reference variables after + coalescing has been performed. */ + partition_view_normal (map, false); if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -1630,7 +1149,7 @@ remove_ssa_form (var_map map, int flags) dump_var_map (dump_file, map); } - if (flags & SSANORM_PERFORM_TER) + if (perform_ter) { values = find_replaceable_exprs (map); if (values && dump_file && (dump_flags & TDF_DETAILS)) @@ -1642,13 +1161,10 @@ remove_ssa_form (var_map map, int flags) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "After Root variable replacement:\n"); + fprintf (dump_file, "After Base variable replacement:\n"); dump_var_map (dump_file, map); } - if (liveinfo) - delete_tree_live_info (liveinfo); - rewrite_trees (map, values); if (values) @@ -1669,8 +1185,11 @@ remove_ssa_form (var_map map, int flags) /* If any copies were inserted on edges, analyze and insert them now. */ perform_edge_inserts (); + + delete_var_map (map); } + /* Search every PHI node for arguments associated with backedges which we can trivially determine will need a copy (the argument is either not an SSA_NAME or the argument has a different underlying variable @@ -1704,11 +1223,10 @@ insert_backedge_copies (void) tree arg = PHI_ARG_DEF (phi, i); edge e = PHI_ARG_EDGE (phi, i); - /* If the argument is not an SSA_NAME, then we will - need a constant initialization. If the argument is - an SSA_NAME with a different underlying variable and - we are not combining temporaries, then we will - need a copy statement. */ + /* If the argument is not an SSA_NAME, then we will need a + constant initialization. If the argument is an SSA_NAME with + a different underlying variable then a copy statement will be + needed. */ if ((e->flags & EDGE_DFS_BACK) && (TREE_CODE (arg) != SSA_NAME || SSA_NAME_VAR (arg) != result_var)) @@ -1723,7 +1241,6 @@ insert_backedge_copies (void) /* In theory the only way we ought to get back to the start of a loop should be with a COND_EXPR or GOTO_EXPR. However, better safe than sorry. - If the block ends with a control statement or something that might throw, then we have to insert this assignment before the last @@ -1738,8 +1255,8 @@ insert_backedge_copies (void) continue; } - /* Create a new instance of the underlying - variable of the PHI result. */ + /* Create a new instance of the underlying variable of the + PHI result. */ stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (result_var), NULL_TREE, PHI_ARG_DEF (phi, i)); name = make_ssa_name (result_var, stmt); @@ -1758,16 +1275,13 @@ insert_backedge_copies (void) } } -/* Take the current function out of SSA form, as described in +/* Take the current function out of SSA form, translating PHIs as described in R. Morgan, ``Building an Optimizing Compiler'', Butterworth-Heinemann, Boston, MA, 1998. pp 176-186. */ static unsigned int rewrite_out_of_ssa (void) { - var_map map; - int ssa_flags = 0; - /* If elimination of a PHI requires inserting a copy on a backedge, then we will have to split the backedge which has numerous undesirable performance effects. @@ -1776,27 +1290,16 @@ rewrite_out_of_ssa (void) copies into the loop itself. */ insert_backedge_copies (); - if (!flag_tree_live_range_split) - ssa_flags |= SSANORM_COALESCE_PARTITIONS; - eliminate_virtual_phis (); if (dump_file && (dump_flags & TDF_DETAILS)) dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); - map = create_ssa_var_map (); - - if (flag_tree_ter && !flag_mudflap) - ssa_flags |= SSANORM_PERFORM_TER; - - remove_ssa_form (map, ssa_flags); + remove_ssa_form (flag_tree_ter && !flag_mudflap); if (dump_file && (dump_flags & TDF_DETAILS)) dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); - /* Flush out flow graph and SSA data. */ - delete_var_map (map); - cfun->gimple_df->in_ssa_p = false; return 0; } diff --git a/gcc/tree-ssa-coalesce.c b/gcc/tree-ssa-coalesce.c new file mode 100644 index 00000000000..00793618d67 --- /dev/null +++ b/gcc/tree-ssa-coalesce.c @@ -0,0 +1,1355 @@ +/* Coalesce SSA_NAMES together for the out-of-ssa pass. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + Contributed by Andrew MacLeod + +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, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "flags.h" +#include "diagnostic.h" +#include "bitmap.h" +#include "tree-flow.h" +#include "hashtab.h" +#include "tree-dump.h" +#include "tree-ssa-live.h" +#include "toplev.h" + + +/* This set of routines implements a coalesce_list. This is an object which + is used to track pairs of ssa_names which are desirable to coalesce + together to avoid copies. Costs are associated with each pair, and when + all desired information has been collected, the object can be used to + order the pairs for processing. */ + +/* This structure defines a pair entry. */ + +typedef struct coalesce_pair +{ + int first_element; + int second_element; + int cost; +} * coalesce_pair_p; + +typedef struct cost_one_pair_d +{ + int first_element; + int second_element; + struct cost_one_pair_d *next; +} * cost_one_pair_p; + +/* This structure maintains the list of coalesce pairs. */ + +typedef struct coalesce_list_d +{ + htab_t list; /* Hash table. */ + coalesce_pair_p *sorted; /* List when sorted. */ + int num_sorted; /* Number in the sorted list. */ + cost_one_pair_p cost_one_list;/* Single use coalesces with cost 1. */ +} *coalesce_list_p; + +#define NO_BEST_COALESCE -1 +#define MUST_COALESCE_COST INT_MAX + + +/* Return cost of execution of copy instruction with FREQUENCY + possibly on CRITICAL edge and in HOT basic block. */ + +static inline int +coalesce_cost (int frequency, bool hot, bool critical) +{ + /* Base costs on BB frequencies bounded by 1. */ + int cost = frequency; + + if (!cost) + cost = 1; + + if (optimize_size) + cost = 1; + else + /* It is more important to coalesce in HOT blocks. */ + if (hot) + cost *= 2; + + /* Inserting copy on critical edge costs more than inserting it elsewhere. */ + if (critical) + cost *= 2; + return cost; +} + + +/* Return the cost of executing a copy instruction in basic block BB. */ + +static inline int +coalesce_cost_bb (basic_block bb) +{ + return coalesce_cost (bb->frequency, maybe_hot_bb_p (bb), false); +} + + +/* Return the cost of executing a copy instruction on edge E. */ + +static inline int +coalesce_cost_edge (edge e) +{ + if (e->flags & EDGE_ABNORMAL) + return MUST_COALESCE_COST; + + return coalesce_cost (EDGE_FREQUENCY (e), + maybe_hot_bb_p (e->src), + EDGE_CRITICAL_P (e)); +} + + +/* Retrieve a pair to coalesce from the cost_one_list in CL. Returns the + 2 elements via P1 and P2. 1 is returned by the function if there is a pair, + NO_BEST_COALESCE is returned if there aren't any. */ + +static inline int +pop_cost_one_pair (coalesce_list_p cl, int *p1, int *p2) +{ + cost_one_pair_p ptr; + + ptr = cl->cost_one_list; + if (!ptr) + return NO_BEST_COALESCE; + + *p1 = ptr->first_element; + *p2 = ptr->second_element; + cl->cost_one_list = ptr->next; + + free (ptr); + + return 1; +} + +/* Retrieve the most expensive remaining pair to coalesce from CL. Returns the + 2 elements via P1 and P2. Their calculated cost is returned by the function. + NO_BEST_COALESCE is returned if the coalesce list is empty. */ + +static inline int +pop_best_coalesce (coalesce_list_p cl, int *p1, int *p2) +{ + coalesce_pair_p node; + int ret; + + if (cl->sorted == NULL) + return pop_cost_one_pair (cl, p1, p2); + + if (cl->num_sorted == 0) + return pop_cost_one_pair (cl, p1, p2); + + node = cl->sorted[--(cl->num_sorted)]; + *p1 = node->first_element; + *p2 = node->second_element; + ret = node->cost; + free (node); + + return ret; +} + + +#define COALESCE_HASH_FN(R1, R2) ((R2) * ((R2) - 1) / 2 + (R1)) + +/* Hash function for coalesce list. Calculate hash for PAIR. */ + +static unsigned int +coalesce_pair_map_hash (const void *pair) +{ + hashval_t a = (hashval_t)(((coalesce_pair_p)pair)->first_element); + hashval_t b = (hashval_t)(((coalesce_pair_p)pair)->second_element); + + return COALESCE_HASH_FN (a,b); +} + + +/* Equality function for coalesce list hash table. Compare PAIR1 and PAIR2, + returning TRUE if the two pairs are equivilent. */ + +static int +coalesce_pair_map_eq (const void *pair1, const void *pair2) +{ + coalesce_pair_p p1 = (coalesce_pair_p) pair1; + coalesce_pair_p p2 = (coalesce_pair_p) pair2; + + return (p1->first_element == p2->first_element + && p1->second_element == p2->second_element); +} + + +/* Create a new empty coalesce list object and return it. */ + +static inline coalesce_list_p +create_coalesce_list (void) +{ + coalesce_list_p list; + unsigned size = num_ssa_names * 3; + + if (size < 40) + size = 40; + + list = (coalesce_list_p) xmalloc (sizeof (struct coalesce_list_d)); + list->list = htab_create (size, coalesce_pair_map_hash, + coalesce_pair_map_eq, NULL); + list->sorted = NULL; + list->num_sorted = 0; + list->cost_one_list = NULL; + return list; +} + + +/* Delete coalesce list CL. */ + +static inline void +delete_coalesce_list (coalesce_list_p cl) +{ + gcc_assert (cl->cost_one_list == NULL); + htab_delete (cl->list); + if (cl->sorted) + free (cl->sorted); + gcc_assert (cl->num_sorted == 0); + free (cl); +} + + +/* Find a matching coalesce pair object in CL for the pair P1 and P2. If + one isn't found, return NULL if CREATE is false, otherwise create a new + coalesce pair object and return it. */ + +static coalesce_pair_p +find_coalesce_pair (coalesce_list_p cl, int p1, int p2, bool create) +{ + struct coalesce_pair p, *pair; + void **slot; + unsigned int hash; + + /* Normalize so that p1 is the smaller value. */ + if (p2 < p1) + { + p.first_element = p2; + p.second_element = p1; + } + else + { + p.first_element = p1; + p.second_element = p2; + } + + + hash = coalesce_pair_map_hash (&p); + pair = (struct coalesce_pair *) htab_find_with_hash (cl->list, &p, hash); + + if (create && !pair) + { + gcc_assert (cl->sorted == NULL); + pair = xmalloc (sizeof (struct coalesce_pair)); + pair->first_element = p.first_element; + pair->second_element = p.second_element; + pair->cost = 0; + slot = htab_find_slot_with_hash (cl->list, pair, hash, INSERT); + *(struct coalesce_pair **)slot = pair; + } + + return pair; +} + +static inline void +add_cost_one_coalesce (coalesce_list_p cl, int p1, int p2) +{ + cost_one_pair_p pair; + + pair = xmalloc (sizeof (struct cost_one_pair_d)); + pair->first_element = p1; + pair->second_element = p2; + pair->next = cl->cost_one_list; + cl->cost_one_list = pair; +} + + +/* Add a coalesce between P1 and P2 in list CL with a cost of VALUE. */ + +static inline void +add_coalesce (coalesce_list_p cl, int p1, int p2, + int value) +{ + coalesce_pair_p node; + + gcc_assert (cl->sorted == NULL); + if (p1 == p2) + return; + + node = find_coalesce_pair (cl, p1, p2, true); + + /* Once the value is MUST_COALESCE_COST, leave it that way. */ + if (node->cost != MUST_COALESCE_COST) + { + if (value == MUST_COALESCE_COST) + node->cost = value; + else + node->cost += value; + } +} + + +/* Comparison function to allow qsort to sort P1 and P2 in Ascendiong order. */ + +static int +compare_pairs (const void *p1, const void *p2) +{ + return (*(coalesce_pair_p *)p1)->cost - (*(coalesce_pair_p *)p2)->cost; +} + + +/* Return the number of unique coalesce pairs in CL. */ + +static inline int +num_coalesce_pairs (coalesce_list_p cl) +{ + return htab_elements (cl->list); +} + + +/* Iterator over hash table pairs. */ +typedef struct +{ + htab_iterator hti; +} coalesce_pair_iterator; + + +/* Return first partition pair from list CL, initializing iterator ITER. */ + +static inline coalesce_pair_p +first_coalesce_pair (coalesce_list_p cl, coalesce_pair_iterator *iter) +{ + coalesce_pair_p pair; + + pair = (coalesce_pair_p) first_htab_element (&(iter->hti), cl->list); + return pair; +} + + +/* Return TRUE if there are no more partitions in for ITER to process. */ + +static inline bool +end_coalesce_pair_p (coalesce_pair_iterator *iter) +{ + return end_htab_p (&(iter->hti)); +} + + +/* Return the next parttition pair to be visited by ITER. */ + +static inline coalesce_pair_p +next_coalesce_pair (coalesce_pair_iterator *iter) +{ + coalesce_pair_p pair; + + pair = (coalesce_pair_p) next_htab_element (&(iter->hti)); + return pair; +} + + +/* Iterate over CL using ITER, returning values in PAIR. */ + +#define FOR_EACH_PARTITION_PAIR(PAIR, ITER, CL) \ + for ((PAIR) = first_coalesce_pair ((CL), &(ITER)); \ + !end_coalesce_pair_p (&(ITER)); \ + (PAIR) = next_coalesce_pair (&(ITER))) + + +/* Prepare CL for removal of preferred pairs. When finished they are sorted + in order from most important coalesce to least important. */ + +static void +sort_coalesce_list (coalesce_list_p cl) +{ + unsigned x, num; + coalesce_pair_p p; + coalesce_pair_iterator ppi; + + gcc_assert (cl->sorted == NULL); + + num = num_coalesce_pairs (cl); + cl->num_sorted = num; + if (num == 0) + return; + + /* Allocate a vector for the pair pointers. */ + cl->sorted = XNEWVEC (coalesce_pair_p, num); + + /* Populate the vector with pointers to the pairs. */ + x = 0; + FOR_EACH_PARTITION_PAIR (p, ppi, cl) + cl->sorted[x++] = p; + gcc_assert (x == num); + + /* Already sorted. */ + if (num == 1) + return; + + /* If there are only 2, just pick swap them if the order isn't correct. */ + if (num == 2) + { + if (cl->sorted[0]->cost > cl->sorted[1]->cost) + { + p = cl->sorted[0]; + cl->sorted[0] = cl->sorted[1]; + cl->sorted[1] = p; + } + return; + } + + /* Only call qsort if there are more than 2 items. */ + if (num > 2) + qsort (cl->sorted, num, sizeof (coalesce_pair_p), compare_pairs); +} + + +/* Send debug info for coalesce list CL to file F. */ + +static void +dump_coalesce_list (FILE *f, coalesce_list_p cl) +{ + coalesce_pair_p node; + coalesce_pair_iterator ppi; + int x; + tree var; + + if (cl->sorted == NULL) + { + fprintf (f, "Coalesce List:\n"); + FOR_EACH_PARTITION_PAIR (node, ppi, cl) + { + tree var1 = ssa_name (node->first_element); + tree var2 = ssa_name (node->second_element); + print_generic_expr (f, var1, TDF_SLIM); + fprintf (f, " <-> "); + print_generic_expr (f, var2, TDF_SLIM); + fprintf (f, " (%1d), ", node->cost); + fprintf (f, "\n"); + } + } + else + { + fprintf (f, "Sorted Coalesce list:\n"); + for (x = cl->num_sorted - 1 ; x >=0; x--) + { + node = cl->sorted[x]; + fprintf (f, "(%d) ", node->cost); + var = ssa_name (node->first_element); + print_generic_expr (f, var, TDF_SLIM); + fprintf (f, " <-> "); + var = ssa_name (node->second_element); + print_generic_expr (f, var, TDF_SLIM); + fprintf (f, "\n"); + } + } +} + + +/* This represents a conflict graph. Implemented as an array of bitmaps. + A full matrix isused for conflicts rather than just upper triangular form. + this make sit much simpler and faster to perform conflict merges. */ + +typedef struct ssa_conflicts_d +{ + unsigned size; + bitmap *conflicts; +} * ssa_conflicts_p; + + +/* Return a empty new conflict graph for SIZE elements. */ + +static inline ssa_conflicts_p +ssa_conflicts_new (unsigned size) +{ + ssa_conflicts_p ptr; + + ptr = XNEW (struct ssa_conflicts_d); + ptr->conflicts = XCNEWVEC (bitmap, size); + ptr->size = size; + return ptr; +} + + +/* Free storage for conflict graph PTR. */ + +static inline void +ssa_conflicts_delete (ssa_conflicts_p ptr) +{ + unsigned x; + for (x = 0; x < ptr->size; x++) + if (ptr->conflicts[x]) + BITMAP_FREE (ptr->conflicts[x]); + + free (ptr->conflicts); + free (ptr); +} + + +/* Test if elements X and Y conflict in graph PTR. */ + +static inline bool +ssa_conflicts_test_p (ssa_conflicts_p ptr, unsigned x, unsigned y) +{ + bitmap b; + +#ifdef ENABLE_CHECKING + gcc_assert (x < ptr->size); + gcc_assert (y < ptr->size); + gcc_assert (x != y); +#endif + + b = ptr->conflicts[x]; + if (b) + /* Avoid the lookup if Y has no conflicts. */ + return ptr->conflicts[y] ? bitmap_bit_p (b, y) : false; + else + return false; +} + + +/* Add a conflict with Y to the bitmap for X in graph PTR. */ + +static inline void +ssa_conflicts_add_one (ssa_conflicts_p ptr, unsigned x, unsigned y) +{ + /* If there are no conflicts yet, allocate the bitmap and set bit. */ + if (!ptr->conflicts[x]) + ptr->conflicts[x] = BITMAP_ALLOC (NULL); + bitmap_set_bit (ptr->conflicts[x], y); +} + + +/* Add conflicts between X and Y in graph PTR. */ + +static inline void +ssa_conflicts_add (ssa_conflicts_p ptr, unsigned x, unsigned y) +{ +#ifdef ENABLE_CHECKING + gcc_assert (x < ptr->size); + gcc_assert (y < ptr->size); + gcc_assert (x != y); +#endif + ssa_conflicts_add_one (ptr, x, y); + ssa_conflicts_add_one (ptr, y, x); +} + + +/* Merge all Y's conflict into X in graph PTR. */ + +static inline void +ssa_conflicts_merge (ssa_conflicts_p ptr, unsigned x, unsigned y) +{ + unsigned z; + bitmap_iterator bi; + + gcc_assert (x != y); + if (!(ptr->conflicts[y])) + return; + + /* Add a conflict between X and every one Y has. If the bitmap doesn't + exist, then it has already been coalesced, and we dont need to add a + conflict. */ + EXECUTE_IF_SET_IN_BITMAP (ptr->conflicts[y], 0, z, bi) + if (ptr->conflicts[z]) + bitmap_set_bit (ptr->conflicts[z], x); + + if (ptr->conflicts[x]) + { + /* If X has conflicts, add Y's to X. */ + bitmap_ior_into (ptr->conflicts[x], ptr->conflicts[y]); + BITMAP_FREE (ptr->conflicts[y]); + } + else + { + /* If X has no conflicts, simply use Y's. */ + ptr->conflicts[x] = ptr->conflicts[y]; + ptr->conflicts[y] = NULL; + } +} + + +/* This structure is used to efficiently record the current status of live + SSA_NAMES when building a conflict graph. + LIVE_BASE_VAR has a bit set for each base variable which has at least one + ssa version live. + LIVE_BASE_PARTITIONS is an array of bitmaps using the basevar table as an + index, and is used to track what partitions of each base variable are + live. This makes it easy to add conflicts between just live partitions + with the same base variable. + The values in LIVE_BASE_PARTITIONS are only valid if the base variable is + marked as being live. This delays clearing of these bitmaps until + they are actually needed again. */ + +typedef struct live_track_d +{ + bitmap live_base_var; /* Indicates if a basevar is live. */ + bitmap *live_base_partitions; /* Live partitions for each basevar. */ + var_map map; /* Var_map being used for partition mapping. */ +} * live_track_p; + + +/* This routine will create a new live track structure based on the partitions + in MAP. */ + +static live_track_p +new_live_track (var_map map) +{ + live_track_p ptr; + int lim, x; + + /* Make sure there is a partition view in place. */ + gcc_assert (map->partition_to_base_index != NULL); + + ptr = (live_track_p) xmalloc (sizeof (struct live_track_d)); + ptr->map = map; + lim = num_basevars (map); + ptr->live_base_partitions = (bitmap *) xmalloc(sizeof (bitmap *) * lim); + ptr->live_base_var = BITMAP_ALLOC (NULL); + for (x = 0; x < lim; x++) + ptr->live_base_partitions[x] = BITMAP_ALLOC (NULL); + return ptr; +} + + +/* This routine will free the memory associated with PTR. */ + +static void +delete_live_track (live_track_p ptr) +{ + int x, lim; + + lim = num_basevars (ptr->map); + for (x = 0; x < lim; x++) + BITMAP_FREE (ptr->live_base_partitions[x]); + BITMAP_FREE (ptr->live_base_var); + free (ptr->live_base_partitions); + free (ptr); +} + + +/* This function will remove PARTITION from the live list in PTR. */ + +static inline void +live_track_remove_partition (live_track_p ptr, int partition) +{ + int root; + + root = basevar_index (ptr->map, partition); + bitmap_clear_bit (ptr->live_base_partitions[root], partition); + /* If the element list is empty, make the base variable not live either. */ + if (bitmap_empty_p (ptr->live_base_partitions[root])) + bitmap_clear_bit (ptr->live_base_var, root); +} + + +/* This function will adds PARTITION to the live list in PTR. */ + +static inline void +live_track_add_partition (live_track_p ptr, int partition) +{ + int root; + + root = basevar_index (ptr->map, partition); + /* If this base var wasn't live before, it is now. Clear the element list + since it was delayed until needed. */ + if (!bitmap_bit_p (ptr->live_base_var, root)) + { + bitmap_set_bit (ptr->live_base_var, root); + bitmap_clear (ptr->live_base_partitions[root]); + } + bitmap_set_bit (ptr->live_base_partitions[root], partition); + +} + + +/* Clear the live bit for VAR in PTR. */ + +static inline void +live_track_clear_var (live_track_p ptr, tree var) +{ + int p; + + p = var_to_partition (ptr->map, var); + if (p != NO_PARTITION) + live_track_remove_partition (ptr, p); +} + + +/* Return TRUE if VAR is live in PTR. */ + +static inline bool +live_track_live_p (live_track_p ptr, tree var) +{ + int p, root; + + p = var_to_partition (ptr->map, var); + if (p != NO_PARTITION) + { + root = basevar_index (ptr->map, p); + if (bitmap_bit_p (ptr->live_base_var, root)) + return bitmap_bit_p (ptr->live_base_partitions[root], p); + } + return false; +} + + +/* This routine will add USE to PTR. USE will be marked as live in both the + ssa live map and the live bitmap for the root of USE. */ + +static inline void +live_track_process_use (live_track_p ptr, tree use) +{ + int p; + + p = var_to_partition (ptr->map, use); + if (p == NO_PARTITION) + return; + + /* Mark as live in the appropriate live list. */ + live_track_add_partition (ptr, p); +} + + +/* This routine will process a DEF in PTR. DEF will be removed from the live + lists, and if there are any other live partitions with the same base + variable, conflicts will be added to GRAPH. */ + +static inline void +live_track_process_def (live_track_p ptr, tree def, ssa_conflicts_p graph) +{ + int p, root; + bitmap b; + unsigned x; + bitmap_iterator bi; + + p = var_to_partition (ptr->map, def); + if (p == NO_PARTITION) + return; + + /* Clear the liveness bit. */ + live_track_remove_partition (ptr, p); + + /* If the bitmap isn't empty now, conflicts need to be added. */ + root = basevar_index (ptr->map, p); + if (bitmap_bit_p (ptr->live_base_var, root)) + { + b = ptr->live_base_partitions[root]; + EXECUTE_IF_SET_IN_BITMAP (b, 0, x, bi) + ssa_conflicts_add (graph, p, x); + } +} + + +/* Initialize PTR with the partitions set in INIT. */ + +static inline void +live_track_init (live_track_p ptr, bitmap init) +{ + unsigned p; + bitmap_iterator bi; + + /* Mark all live on exit partitions. */ + EXECUTE_IF_SET_IN_BITMAP (init, 0, p, bi) + live_track_add_partition (ptr, p); +} + + +/* This routine will clear all live partitions in PTR. */ + +static inline void +live_track_clear_base_vars (live_track_p ptr) +{ + /* Simply clear the live base list. Anything marked as live in the element + lists will be cleared later if/when the base variable ever comes alive + again. */ + bitmap_clear (ptr->live_base_var); +} + + +/* Build a conflict graph based on LIVEINFO. Any partitions which are in the + partition view of the var_map liveinfo is based on get entires in the + conflict graph. Only conflicts between ssa_name partitions with the same + base variableare added. */ + +static ssa_conflicts_p +build_ssa_conflict_graph (tree_live_info_p liveinfo) +{ + ssa_conflicts_p graph; + var_map map; + basic_block bb; + ssa_op_iter iter; + live_track_p live; + + map = live_var_map (liveinfo); + graph = ssa_conflicts_new (num_var_partitions (map)); + + live = new_live_track (map); + + FOR_EACH_BB (bb) + { + block_stmt_iterator bsi; + tree phi; + + /* Start with live on exit temporaries. */ + live_track_init (live, live_on_exit (liveinfo, bb)); + + for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi)) + { + tree var; + tree stmt = bsi_stmt (bsi); + + /* A copy between 2 partitions does not introduce an interference + by itself. If they did, you would never be able to coalesce + two things which are copied. If the two variables really do + conflict, they will conflict elsewhere in the program. + + This is handled by simply removing the SRC of the copy from the + live list, and processing the stmt normally. */ + if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT) + { + tree lhs = GIMPLE_STMT_OPERAND (stmt, 0); + tree rhs = GIMPLE_STMT_OPERAND (stmt, 1); + if (TREE_CODE (lhs) == SSA_NAME && TREE_CODE (rhs) == SSA_NAME) + live_track_clear_var (live, rhs); + } + + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_DEF) + live_track_process_def (live, var, graph); + + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE) + live_track_process_use (live, var); + } + + /* If result of a PHI is unused, looping over the statements will not + record any conflicts since the def was never live. Since the PHI node + is going to be translated out of SSA form, it will insert a copy. + There must be a conflict recorded between the result of the PHI and + any variables that are live. Otherwise the out-of-ssa translation + may create incorrect code. */ + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + { + tree result = PHI_RESULT (phi); + if (live_track_live_p (live, result)) + live_track_process_def (live, result, graph); + } + + live_track_clear_base_vars (live); + } + + delete_live_track (live); + return graph; +} + + +/* Shortcut routine to print messages to file F of the form: + "STR1 EXPR1 STR2 EXPR2 STR3." */ + +static inline void +print_exprs (FILE *f, const char *str1, tree expr1, const char *str2, + tree expr2, const char *str3) +{ + fprintf (f, "%s", str1); + print_generic_expr (f, expr1, TDF_SLIM); + fprintf (f, "%s", str2); + print_generic_expr (f, expr2, TDF_SLIM); + fprintf (f, "%s", str3); +} + + +/* Called if a coalesce across and abnormal edge cannot be performed. PHI is + the phi node at fault, I is the argument index at fault. A message is + printed and compilation is then terminated. */ + +static inline void +abnormal_corrupt (tree phi, int i) +{ + edge e = PHI_ARG_EDGE (phi, i); + tree res = PHI_RESULT (phi); + tree arg = PHI_ARG_DEF (phi, i); + + fprintf (stderr, " Corrupt SSA across abnormal edge BB%d->BB%d\n", + e->src->index, e->dest->index); + fprintf (stderr, "Argument %d (", i); + print_generic_expr (stderr, arg, TDF_SLIM); + if (TREE_CODE (arg) != SSA_NAME) + fprintf (stderr, ") is not an SSA_NAME.\n"); + else + { + gcc_assert (SSA_NAME_VAR (res) != SSA_NAME_VAR (arg)); + fprintf (stderr, ") does not have the same base variable as the result "); + print_generic_stmt (stderr, res, TDF_SLIM); + } + + internal_error ("SSA corruption"); +} + + +/* Print a failure to coalesce a MUST_COALESCE pair X and Y. */ + +static inline void +fail_abnormal_edge_coalesce (int x, int y) +{ + fprintf (stderr, "\nUnable to coalesce ssa_names %d and %d ",x, y); + fprintf (stderr, " which are marked as MUST COALESCE.\n"); + print_generic_expr (stderr, ssa_name (x), TDF_SLIM); + fprintf (stderr, " and "); + print_generic_stmt (stderr, ssa_name (y), TDF_SLIM); + + internal_error ("SSA corruption"); +} + + +/* This function creates a var_map for the current function as well as creating + a coalesce list for use later in the out of ssa process. */ + +static var_map +create_outofssa_var_map (coalesce_list_p cl, bitmap used_in_copy) +{ + block_stmt_iterator bsi; + basic_block bb; + tree var; + tree stmt; + tree first; + var_map map; + ssa_op_iter iter; + int v1, v2, cost; + unsigned i; + +#ifdef ENABLE_CHECKING + bitmap used_in_real_ops; + bitmap used_in_virtual_ops; + + used_in_real_ops = BITMAP_ALLOC (NULL); + used_in_virtual_ops = BITMAP_ALLOC (NULL); +#endif + + map = init_var_map (num_ssa_names + 1); + + FOR_EACH_BB (bb) + { + tree phi, arg; + + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + { + int i; + int ver; + tree res; + bool saw_copy = false; + + res = PHI_RESULT (phi); + ver = SSA_NAME_VERSION (res); + register_ssa_partition (map, res); + + /* Register ssa_names and coalesces between the args and the result + of all PHI. */ + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + edge e = PHI_ARG_EDGE (phi, i); + arg = PHI_ARG_DEF (phi, i); + if (TREE_CODE (arg) == SSA_NAME) + register_ssa_partition (map, arg); + if (TREE_CODE (arg) == SSA_NAME + && SSA_NAME_VAR (arg) == SSA_NAME_VAR (res)) + { + saw_copy = true; + bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (arg)); + if ((e->flags & EDGE_ABNORMAL) == 0) + { + int cost = coalesce_cost_edge (e); + if (cost == 1 && single_imm_use_p (arg)) + add_cost_one_coalesce (cl, ver, SSA_NAME_VERSION (arg)); + else + add_coalesce (cl, ver, SSA_NAME_VERSION (arg), cost); + } + } + else + if (e->flags & EDGE_ABNORMAL) + abnormal_corrupt (phi, i); + } + if (saw_copy) + bitmap_set_bit (used_in_copy, ver); + } + + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + stmt = bsi_stmt (bsi); + + /* Register USE and DEF operands in each statement. */ + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, (SSA_OP_DEF|SSA_OP_USE)) + register_ssa_partition (map, var); + + /* Check for copy coalesces. */ + switch (TREE_CODE (stmt)) + { + case GIMPLE_MODIFY_STMT: + { + tree op1 = GIMPLE_STMT_OPERAND (stmt, 0); + tree op2 = GIMPLE_STMT_OPERAND (stmt, 1); + if (TREE_CODE (op1) == SSA_NAME + && TREE_CODE (op2) == SSA_NAME + && SSA_NAME_VAR (op1) == SSA_NAME_VAR (op2)) + { + v1 = SSA_NAME_VERSION (op1); + v2 = SSA_NAME_VERSION (op2); + cost = coalesce_cost_bb (bb); + add_coalesce (cl, v1, v2, cost); + bitmap_set_bit (used_in_copy, v1); + bitmap_set_bit (used_in_copy, v2); + } + } + break; + + case ASM_EXPR: + { + unsigned long noutputs, i; + tree *outputs, link; + noutputs = list_length (ASM_OUTPUTS (stmt)); + outputs = (tree *) alloca (noutputs * sizeof (tree)); + for (i = 0, link = ASM_OUTPUTS (stmt); link; + ++i, link = TREE_CHAIN (link)) + outputs[i] = TREE_VALUE (link); + + for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link)) + { + const char *constraint + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + tree input = TREE_VALUE (link); + char *end; + unsigned long match; + + if (TREE_CODE (input) != SSA_NAME && !DECL_P (input)) + continue; + + match = strtoul (constraint, &end, 10); + if (match >= noutputs || end == constraint) + continue; + + if (TREE_CODE (outputs[match]) != SSA_NAME) + continue; + + v1 = SSA_NAME_VERSION (outputs[match]); + v2 = SSA_NAME_VERSION (input); + + if (SSA_NAME_VAR (outputs[match]) == SSA_NAME_VAR (input)) + { + cost = coalesce_cost (REG_BR_PROB_BASE, + maybe_hot_bb_p (bb), + false); + add_coalesce (cl, v1, v2, cost); + bitmap_set_bit (used_in_copy, v1); + bitmap_set_bit (used_in_copy, v2); + } + } + break; + } + + default: + break; + } + +#ifdef ENABLE_CHECKING + /* Mark real uses and defs. */ + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, (SSA_OP_DEF|SSA_OP_USE)) + bitmap_set_bit (used_in_real_ops, DECL_UID (SSA_NAME_VAR (var))); + + /* Validate that virtual ops don't get used in funny ways. */ + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, + SSA_OP_VIRTUAL_USES | SSA_OP_VMUSTDEF) + { + bitmap_set_bit (used_in_virtual_ops, + DECL_UID (SSA_NAME_VAR (var))); + } + +#endif /* ENABLE_CHECKING */ + } + } + + /* Now process result decls and live on entry variables for entry into + the coalesce list. */ + first = NULL_TREE; + for (i = 1; i < num_ssa_names; i++) + { + var = map->partition_to_var[i]; + if (var != NULL_TREE) + { + /* Add coalesces between all the result decls. */ + if (TREE_CODE (SSA_NAME_VAR (var)) == RESULT_DECL) + { + if (first == NULL_TREE) + first = var; + else + { + gcc_assert (SSA_NAME_VAR (var) == SSA_NAME_VAR (first)); + v1 = SSA_NAME_VERSION (first); + v2 = SSA_NAME_VERSION (var); + bitmap_set_bit (used_in_copy, v1); + bitmap_set_bit (used_in_copy, v2); + cost = coalesce_cost_bb (EXIT_BLOCK_PTR); + add_coalesce (cl, v1, v2, cost); + } + } + /* Mark any default_def variables as being in the coalesce list + since they will have to be coalesced with the base variable. If + not marked as present, they won't be in the coalesce view. */ + if (gimple_default_def (cfun, SSA_NAME_VAR (var)) == var) + bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (var)); + } + } + +#if defined ENABLE_CHECKING + { + unsigned i; + bitmap both = BITMAP_ALLOC (NULL); + bitmap_and (both, used_in_real_ops, used_in_virtual_ops); + if (!bitmap_empty_p (both)) + { + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (both, 0, i, bi) + fprintf (stderr, "Variable %s used in real and virtual operands\n", + get_name (referenced_var (i))); + internal_error ("SSA corruption"); + } + + BITMAP_FREE (used_in_real_ops); + BITMAP_FREE (used_in_virtual_ops); + BITMAP_FREE (both); + } +#endif + + return map; +} + + +/* Attempt to coalesce ssa verisons X and Y together using the partition + mapping in MAP and checking conflicts in GRAPH. Output any debug info to + DEBUG, if it is nun-NULL. */ + +static inline bool +attempt_coalesce (var_map map, ssa_conflicts_p graph, int x, int y, + FILE *debug) +{ + int z; + tree var1, var2; + int p1, p2; + + p1 = var_to_partition (map, ssa_name (x)); + p2 = var_to_partition (map, ssa_name (y)); + + if (debug) + { + fprintf (debug, "(%d)", x); + print_generic_expr (debug, partition_to_var (map, p1), TDF_SLIM); + fprintf (debug, " & (%d)", y); + print_generic_expr (debug, partition_to_var (map, p2), TDF_SLIM); + } + + if (p1 == p2) + { + if (debug) + fprintf (debug, ": Already Coalesced.\n"); + return true; + } + + if (debug) + fprintf (debug, " [map: %d, %d] ", p1, p2); + + + if (!ssa_conflicts_test_p (graph, p1, p2)) + { + var1 = partition_to_var (map, p1); + var2 = partition_to_var (map, p2); + z = var_union (map, var1, var2); + if (z == NO_PARTITION) + { + if (debug) + fprintf (debug, ": Unable to perform partition union.\n"); + return false; + } + + /* z is the new combined partition. Remove the other partition from + the list, and merge the conflicts. */ + if (z == p1) + ssa_conflicts_merge (graph, p1, p2); + else + ssa_conflicts_merge (graph, p2, p1); + + if (debug) + fprintf (debug, ": Success -> %d\n", z); + return true; + } + + if (debug) + fprintf (debug, ": Fail due to conflict\n"); + + return false; +} + + +/* Attempt to Coalesce partitions in MAP which occur in the list CL using + GRAPH. Debug output is sent to DEBUG if it is non-NULL. */ + +static void +coalesce_partitions (var_map map, ssa_conflicts_p graph, coalesce_list_p cl, + FILE *debug) +{ + int x = 0, y = 0; + tree var1, var2, phi; + int cost; + basic_block bb; + edge e; + edge_iterator ei; + + /* First, coalece all the copie across abnormal edges. These are not placed + in the coalesce list becase they do not need to be sorted, and simply + consume extra memory/compilation time in large programs. */ + + FOR_EACH_BB (bb) + { + FOR_EACH_EDGE (e, ei, bb->preds) + if (e->flags & EDGE_ABNORMAL) + { + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + { + tree res = PHI_RESULT (phi); + tree arg = PHI_ARG_DEF (phi, e->dest_idx); + int v1 = SSA_NAME_VERSION (res); + int v2 = SSA_NAME_VERSION (arg); + + if (SSA_NAME_VAR (arg) != SSA_NAME_VAR (res)) + abnormal_corrupt (phi, e->dest_idx); + + if (debug) + fprintf (debug, "Abnormal coalesce: "); + + if (!attempt_coalesce (map, graph, v1, v2, debug)) + fail_abnormal_edge_coalesce (v1, v2); + } + } + } + + /* Now process the items in the coalesce list. */ + + while ((cost = pop_best_coalesce (cl, &x, &y)) != NO_BEST_COALESCE) + { + var1 = ssa_name (x); + var2 = ssa_name (y); + + /* Assert the coalesces have the same base variable. */ + gcc_assert (SSA_NAME_VAR (var1) == SSA_NAME_VAR (var2)); + + if (debug) + fprintf (debug, "Coalesce list: "); + attempt_coalesce (map, graph, x, y, debug); + } +} + + +/* Reduce the number of copies by coalescing variables in the function. Return + a partition map with the resulting coalesces. */ + +extern var_map +coalesce_ssa_name (void) +{ + unsigned num, x; + tree_live_info_p liveinfo; + ssa_conflicts_p graph; + coalesce_list_p cl; + bitmap used_in_copies = BITMAP_ALLOC (NULL); + var_map map; + + cl = create_coalesce_list (); + map = create_outofssa_var_map (cl, used_in_copies); + + /* Don't calculate live ranges for variables not in the coalesce list. */ + partition_view_bitmap (map, used_in_copies, true); + BITMAP_FREE (used_in_copies); + + if (num_var_partitions (map) <= 1) + { + delete_coalesce_list (cl); + return map; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_var_map (dump_file, map); + + liveinfo = calculate_live_ranges (map); + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_live_info (dump_file, liveinfo, LIVEDUMP_ENTRY); + + /* Build a conflict graph. */ + graph = build_ssa_conflict_graph (liveinfo); + delete_tree_live_info (liveinfo); + + sort_coalesce_list (cl); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nAfter sorting:\n"); + dump_coalesce_list (dump_file, cl); + } + + /* First, coalesce all live on entry variables to their base variable. + This will ensure the first use is coming from the correct location. */ + + num = num_var_partitions (map); + for (x = 0 ; x < num; x++) + { + tree var = partition_to_var (map, x); + tree root; + + if (TREE_CODE (var) != SSA_NAME) + continue; + + root = SSA_NAME_VAR (var); + if (gimple_default_def (cfun, root) == var) + { + /* This root variable should have not already been assigned + to another partition which is not coalesced with this one. */ + gcc_assert (!var_ann (root)->out_of_ssa_tag); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_exprs (dump_file, "Must coalesce ", var, + " with the root variable ", root, ".\n"); + } + change_partition_var (map, root, x); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_var_map (dump_file, map); + + /* Now coalesce everything in the list. */ + coalesce_partitions (map, graph, cl, + ((dump_flags & TDF_DETAILS) ? dump_file + : NULL)); + + delete_coalesce_list (cl); + ssa_conflicts_delete (graph); + + return map; +} + diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c index 277e2763bc5..2049e43d0d4 100644 --- a/gcc/tree-ssa-live.c +++ b/gcc/tree-ssa-live.c @@ -24,44 +24,107 @@ Boston, MA 02110-1301, USA. */ #include "coretypes.h" #include "tm.h" #include "tree.h" -#include "flags.h" -#include "basic-block.h" -#include "function.h" #include "diagnostic.h" #include "bitmap.h" #include "tree-flow.h" -#include "tree-gimple.h" -#include "tree-inline.h" -#include "varray.h" -#include "timevar.h" -#include "hashtab.h" #include "tree-dump.h" #include "tree-ssa-live.h" #include "toplev.h" -#include "vecprim.h" -static void live_worklist (tree_live_info_p); -static tree_live_info_p new_tree_live_info (var_map); -static inline void set_if_valid (var_map, bitmap, tree); -static inline void add_conflicts_if_valid (tpa_p, conflict_graph, - var_map, bitmap, tree); -static partition_pair_p find_partition_pair (coalesce_list_p, int, int, bool); #ifdef ENABLE_CHECKING static void verify_live_on_entry (tree_live_info_p); #endif -/* This is where the mapping from SSA version number to real storage variable - is tracked. - All SSA versions of the same variable may not ultimately be mapped back to - the same real variable. In that instance, we need to detect the live - range overlap, and give one of the variable new storage. The vector - 'partition_to_var' tracks which partition maps to which variable. +/* VARMAP maintains a mapping from SSA version number to real variables. - Given a VAR, it is sometimes desirable to know which partition that VAR - represents. There is an additional field in the variable annotation to - track that information. */ + All SSA_NAMES are divided into partitions. Initially each ssa_name is the + only member of it's own partition. Coalescing will attempt to group any + ssa_names which occur in a copy or in a PHI node into the same partition. + At the end of out-of-ssa, each partition becomes a "real" variable and is + rewritten as a compiler variable. + + The var_map datat structure is used to manage these partitions. It allows + partitions to be combined, and determines which partition belongs to what + ssa_name or variable, and vice versa. */ + + +/* This routine will initialize the basevar fields of MAP. */ + +static void +var_map_base_init (var_map map) +{ + int x, num_part, num; + tree var; + var_ann_t ann; + + num = 0; + num_part = num_var_partitions (map); + + /* If a base table already exists, clear it, otherwise create it. */ + if (map->partition_to_base_index != NULL) + { + free (map->partition_to_base_index); + VEC_truncate (tree, map->basevars, 0); + } + else + map->basevars = VEC_alloc (tree, heap, MAX (40, (num_part / 10))); + + map->partition_to_base_index = (int *) xmalloc (sizeof (int) * num_part); + + /* Build the base variable list, and point partitions at their bases. */ + for (x = 0; x < num_part; x++) + { + var = partition_to_var (map, x); + if (TREE_CODE (var) == SSA_NAME) + var = SSA_NAME_VAR (var); + ann = var_ann (var); + /* If base variable hasn't been seen, set it up. */ + if (!ann->base_var_processed) + { + ann->base_var_processed = 1; + VAR_ANN_BASE_INDEX (ann) = num++; + VEC_safe_push (tree, heap, map->basevars, var); + } + map->partition_to_base_index[x] = VAR_ANN_BASE_INDEX (ann); + } + + map->num_basevars = num; + + /* Now clear the processed bit. */ + for (x = 0; x < num; x++) + { + var = VEC_index (tree, map->basevars, x); + var_ann (var)->base_var_processed = 0; + } + +#ifdef ENABLE_CHECKING + for (x = 0; x < num_part; x++) + { + tree var2; + var = SSA_NAME_VAR (partition_to_var (map, x)); + var2 = VEC_index (tree, map->basevars, basevar_index (map, x)); + gcc_assert (var == var2); + } +#endif +} + + +/* Remove the base table in MAP. */ + +static void +var_map_base_fini (var_map map) +{ + /* Free the basevar info if it is present. */ + if (map->partition_to_base_index != NULL) + { + VEC_free (tree, heap, map->basevars); + free (map->partition_to_base_index); + map->partition_to_base_index = NULL; + map->num_basevars = 0; + } +} /* Create a variable partition map of SIZE, initialize and return it. */ var_map @@ -75,10 +138,13 @@ init_var_map (int size) = (tree *)xmalloc (size * sizeof (tree)); memset (map->partition_to_var, 0, size * sizeof (tree)); - map->partition_to_compact = NULL; - map->compact_to_partition = NULL; + map->partition_to_view = NULL; + map->view_to_partition = NULL; map->num_partitions = size; map->partition_size = size; + map->num_basevars = 0; + map->partition_to_base_index = NULL; + map->basevars = NULL; return map; } @@ -88,12 +154,13 @@ init_var_map (int size) void delete_var_map (var_map map) { + var_map_base_fini (map); free (map->partition_to_var); partition_delete (map->var_partition); - if (map->partition_to_compact) - free (map->partition_to_compact); - if (map->compact_to_partition) - free (map->compact_to_partition); + if (map->partition_to_view) + free (map->partition_to_view); + if (map->view_to_partition) + free (map->view_to_partition); free (map); } @@ -109,17 +176,17 @@ var_union (var_map map, tree var1, tree var2) tree root_var = NULL_TREE; tree other_var = NULL_TREE; - /* This is independent of partition_to_compact. If partition_to_compact is + /* This is independent of partition_to_view. If partition_to_view is on, then whichever one of these partitions is absorbed will never have a - dereference into the partition_to_compact array any more. */ + dereference into the partition_to_view array any more. */ if (TREE_CODE (var1) == SSA_NAME) p1 = partition_find (map->var_partition, SSA_NAME_VERSION (var1)); else { p1 = var_to_partition (map, var1); - if (map->compact_to_partition) - p1 = map->compact_to_partition[p1]; + if (map->view_to_partition) + p1 = map->view_to_partition[p1]; root_var = var1; } @@ -128,8 +195,8 @@ var_union (var_map map, tree var1, tree var2) else { p2 = var_to_partition (map, var2); - if (map->compact_to_partition) - p2 = map->compact_to_partition[p2]; + if (map->view_to_partition) + p2 = map->view_to_partition[p2]; /* If there is no root_var set, or it's not a user variable, set the root_var to this one. */ @@ -150,8 +217,8 @@ var_union (var_map map, tree var1, tree var2) else p3 = partition_union (map->var_partition, p1, p2); - if (map->partition_to_compact) - p3 = map->partition_to_compact[p3]; + if (map->partition_to_view) + p3 = map->partition_to_view[p3]; if (root_var) change_partition_var (map, root_var, p3); @@ -161,12 +228,12 @@ var_union (var_map map, tree var1, tree var2) return p3; } - + /* Compress the partition numbers in MAP such that they fall in the range 0..(num_partitions-1) instead of wherever they turned out during the partitioning exercise. This removes any references to unused partitions, thereby allowing bitmaps and other vectors to be much - denser. Compression type is controlled by FLAGS. + denser. This is implemented such that compaction doesn't affect partitioning. Ie., once partitions are created and possibly merged, running one @@ -179,96 +246,140 @@ var_union (var_map map, tree var1, tree var2) definitions, and then 'recompact' later to include all the single definitions for assignment to program variables. */ -void -compact_var_map (var_map map, int flags) + +/* Set MAP back to the initial state of having no partition view. Return a + bitmap which has a bit set for each partition number which is in use in the + varmap. */ + +static bitmap +partition_view_init (var_map map) { - sbitmap used; - int tmp, root, root_i; - unsigned int x, limit, count; - tree var; - root_var_p rv = NULL; + bitmap used; + int tmp; + unsigned int x; - limit = map->partition_size; - used = sbitmap_alloc (limit); - sbitmap_zero (used); + used = BITMAP_ALLOC (NULL); - /* Already compressed? Abandon the old one. */ - if (map->partition_to_compact) + /* Already in a view? Abandon the old one. */ + if (map->partition_to_view) { - free (map->partition_to_compact); - map->partition_to_compact = NULL; + free (map->partition_to_view); + map->partition_to_view = NULL; } - if (map->compact_to_partition) + if (map->view_to_partition) { - free (map->compact_to_partition); - map->compact_to_partition = NULL; + free (map->view_to_partition); + map->view_to_partition = NULL; + } + + /* Find out which partitions are actually referenced. */ + for (x = 0; x < map->partition_size; x++) + { + tmp = partition_find (map->var_partition, x); + if (map->partition_to_var[tmp] != NULL_TREE && !bitmap_bit_p (used, tmp)) + bitmap_set_bit (used, tmp); } map->num_partitions = map->partition_size; + return used; +} - if (flags & VARMAP_NO_SINGLE_DEFS) - rv = root_var_init (map); - map->partition_to_compact = (int *)xmalloc (limit * sizeof (int)); - memset (map->partition_to_compact, 0xff, (limit * sizeof (int))); +/* This routine will finalize the view data for MAP based on the partitions + set in SELECTED. This is either the same bitmap returned from + partition_view_init, or a trimmed down version if some of those partitions + were not desired in this view. SELECTED is freed before returning. */ - /* Find out which partitions are actually referenced. */ - count = 0; - for (x = 0; x < limit; x++) +static void +partition_view_fini (var_map map, bitmap selected) +{ + bitmap_iterator bi; + unsigned count, i, x, limit; + tree var; + + gcc_assert (selected); + + count = bitmap_count_bits (selected); + limit = map->partition_size; + + /* If its a one-to-one ratio, we don't need any view compaction. */ + if (count < limit) { - tmp = partition_find (map->var_partition, x); - if (!TEST_BIT (used, tmp) && map->partition_to_var[tmp] != NULL_TREE) - { - /* It is referenced, check to see if there is more than one version - in the root_var table, if one is available. */ - if (rv) - { - root = root_var_find (rv, tmp); - root_i = root_var_first_partition (rv, root); - /* If there is only one, don't include this in the compaction. */ - if (root_var_next_partition (rv, root_i) == ROOT_VAR_NONE) - continue; - } - SET_BIT (used, tmp); - count++; - } - } + map->partition_to_view = (int *)xmalloc (limit * sizeof (int)); + memset (map->partition_to_view, 0xff, (limit * sizeof (int))); + map->view_to_partition = (int *)xmalloc (count * sizeof (int)); - /* Build a compacted partitioning. */ - if (count != limit) - { - sbitmap_iterator sbi; - - map->compact_to_partition = (int *)xmalloc (count * sizeof (int)); - count = 0; - /* SSA renaming begins at 1, so skip 0 when compacting. */ - EXECUTE_IF_SET_IN_SBITMAP (used, 1, x, sbi) + i = 0; + /* Give each selected partition an index. */ + EXECUTE_IF_SET_IN_BITMAP (selected, 0, x, bi) { - map->partition_to_compact[x] = count; - map->compact_to_partition[count] = x; + map->partition_to_view[x] = i; + map->view_to_partition[i] = x; var = map->partition_to_var[x]; + /* If any one of the members of a partition is not an SSA_NAME, make + sure it is the representative. */ if (TREE_CODE (var) != SSA_NAME) - change_partition_var (map, var, count); - count++; + change_partition_var (map, var, i); + i++; } + gcc_assert (i == count); + map->num_partitions = i; } + + BITMAP_FREE (selected); +} + + +/* Create a partition view which includes all the used partitions in MAP. If + WANT_BASES is true, create the base variable map as well. */ + +extern void +partition_view_normal (var_map map, bool want_bases) +{ + bitmap used; + + used = partition_view_init (map); + partition_view_fini (map, used); + + if (want_bases) + var_map_base_init (map); else + var_map_base_fini (map); +} + + +/* Create a partition view in MAP which includes just partitions which occur in + the bitmap ONLY. If WANT_BASES is true, create the base variable map + as well. */ + +extern void +partition_view_bitmap (var_map map, bitmap only, bool want_bases) +{ + bitmap used; + bitmap new_partitions = BITMAP_ALLOC (NULL); + unsigned x, p; + bitmap_iterator bi; + + used = partition_view_init (map); + EXECUTE_IF_SET_IN_BITMAP (only, 0, x, bi) { - free (map->partition_to_compact); - map->partition_to_compact = NULL; + p = partition_find (map->var_partition, x); + gcc_assert (bitmap_bit_p (used, p)); + bitmap_set_bit (new_partitions, p); } + partition_view_fini (map, new_partitions); - map->num_partitions = count; - - if (rv) - root_var_delete (rv); - sbitmap_free (used); + BITMAP_FREE (used); + if (want_bases) + var_map_base_init (map); + else + var_map_base_fini (map); } /* This function is used to change the representative variable in MAP for VAR's - partition from an SSA_NAME variable to a regular variable. This allows - partitions to be mapped back to real variables. */ + partition to a regular non-ssa variable. This allows partitions to be + mapped back to real variables. */ void change_partition_var (var_map map, tree var, int part) @@ -280,10 +391,11 @@ change_partition_var (var_map map, tree var, int part) ann = var_ann (var); ann->out_of_ssa_tag = 1; VAR_ANN_PARTITION (ann) = part; - if (map->compact_to_partition) - map->partition_to_var[map->compact_to_partition[part]] = var; + if (map->view_to_partition) + map->partition_to_var[map->view_to_partition[part]] = var; } + static inline void mark_all_vars_used (tree *); /* Helper function for mark_all_vars_used, called via walk_tree. */ @@ -319,6 +431,7 @@ mark_all_vars_used_1 (tree *tp, int *walk_subtrees, return NULL; } + /* Mark all VAR_DECLS under *EXPR_P as used, so that they won't be eliminated during the tree->rtl conversion process. */ @@ -394,102 +507,6 @@ remove_unused_locals (void) } } -/* This function looks through the program and uses FLAGS to determine what - SSA versioned variables are given entries in a new partition table. This - new partition map is returned. */ - -var_map -create_ssa_var_map (void) -{ - block_stmt_iterator bsi; - basic_block bb; - tree var; - tree stmt; - var_map map; - ssa_op_iter iter; -#ifdef ENABLE_CHECKING - bitmap used_in_real_ops; - bitmap used_in_virtual_ops; -#endif - - map = init_var_map (num_ssa_names + 1); - -#ifdef ENABLE_CHECKING - used_in_real_ops = BITMAP_ALLOC (NULL); - used_in_virtual_ops = BITMAP_ALLOC (NULL); -#endif - - FOR_EACH_BB (bb) - { - tree phi, arg; - - for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) - { - int i; - register_ssa_partition (map, PHI_RESULT (phi)); - for (i = 0; i < PHI_NUM_ARGS (phi); i++) - { - arg = PHI_ARG_DEF (phi, i); - if (TREE_CODE (arg) == SSA_NAME) - register_ssa_partition (map, arg); - - mark_all_vars_used (&PHI_ARG_DEF_TREE (phi, i)); - } - } - - for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) - { - stmt = bsi_stmt (bsi); - - /* Register USE and DEF operands in each statement. */ - FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, (SSA_OP_DEF|SSA_OP_USE)) - { - register_ssa_partition (map, var); - -#ifdef ENABLE_CHECKING - bitmap_set_bit (used_in_real_ops, DECL_UID (SSA_NAME_VAR (var))); -#endif - } - -#ifdef ENABLE_CHECKING - /* Validate that virtual ops don't get used in funny ways. */ - FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, - SSA_OP_VIRTUAL_USES | SSA_OP_VMUSTDEF) - { - bitmap_set_bit (used_in_virtual_ops, - DECL_UID (SSA_NAME_VAR (var))); - } - -#endif /* ENABLE_CHECKING */ - - mark_all_vars_used (bsi_stmt_ptr (bsi)); - } - } - -#if defined ENABLE_CHECKING - { - unsigned i; - bitmap both = BITMAP_ALLOC (NULL); - bitmap_and (both, used_in_real_ops, used_in_virtual_ops); - if (!bitmap_empty_p (both)) - { - bitmap_iterator bi; - - EXECUTE_IF_SET_IN_BITMAP (both, 0, i, bi) - fprintf (stderr, "Variable %s used in real and virtual operands\n", - get_name (referenced_var (i))); - internal_error ("SSA corruption"); - } - - BITMAP_FREE (used_in_real_ops); - BITMAP_FREE (used_in_virtual_ops); - BITMAP_FREE (both); - } -#endif - - return map; -} - /* Allocate and return a new live range information object base on MAP. */ @@ -541,7 +558,7 @@ delete_tree_live_info (tree_live_info_p live) } -/* Visit basic block BB, and propogate any required live on entry bits from +/* Visit basic block BB and propogate any required live on entry bits from LIVE into the predecessors. VISITED is the bitmap of visited blocks. TMP is a temporary work bitmap which is passed in to avoid reallocting it each time. */ @@ -565,8 +582,8 @@ loe_visit_block (tree_live_info_p live, basic_block bb, sbitmap visited, pred_bb = e->src; if (pred_bb == ENTRY_BLOCK_PTR) continue; - /* tmp is vars live-=on-entry from BB that aren't defined in the - pred. block. This should be the live on entry vars to pred. + /* TMP is variables live-on-entry from BB that aren't defined in the + predecessor block. This should be the live on entry vars to pred. Note that liveout is the DEFs in a block while live on entry is being calculated. */ bitmap_and_compl (tmp, loe, live->liveout[pred_bb->index]); @@ -636,7 +653,7 @@ set_var_live_on_entry (tree ssa_name, tree_live_info_p live) if (stmt) { def_bb = bb_for_stmt (stmt); - /* Mark defs in liveout bitmap for now. */ + /* Mark defs in liveout bitmap temporarily. */ if (def_bb) bitmap_set_bit (live->liveout[def_bb->index], p); } @@ -698,7 +715,7 @@ calculate_live_on_exit (tree_live_info_p liveinfo) edge e; edge_iterator ei; - /* live on entry calculations used the liveouit vector for defs. */ + /* live on entry calculations used liveout vectors for defs, clear them. */ FOR_EACH_BB (bb) bitmap_clear (liveinfo->liveout[bb->index]); @@ -720,7 +737,7 @@ calculate_live_on_exit (tree_live_info_p liveinfo) bitmap_set_bit (liveinfo->liveout[e->src->index], p); } - /* add each successors live on entry to this bock live on exit. */ + /* Add each successors live on entry to this bock live on exit. */ FOR_EACH_EDGE (e, ei, bb->succs) if (e->dest != EXIT_BLOCK_PTR) bitmap_ior_into (liveinfo->liveout[bb->index], @@ -728,6 +745,7 @@ calculate_live_on_exit (tree_live_info_p liveinfo) } } + /* Given partition map MAP, calculate all the live on entry bitmaps for each partition. Return a new live info object. */ @@ -757,936 +775,6 @@ calculate_live_ranges (var_map map) } -/* Initialize a tree_partition_associator object using MAP. */ - -static tpa_p -tpa_init (var_map map) -{ - tpa_p tpa; - int num_partitions = num_var_partitions (map); - int x; - - if (num_partitions == 0) - return NULL; - - tpa = (tpa_p) xmalloc (sizeof (struct tree_partition_associator_d)); - tpa->num_trees = 0; - tpa->uncompressed_num = -1; - tpa->map = map; - tpa->next_partition = (int *)xmalloc (num_partitions * sizeof (int)); - memset (tpa->next_partition, TPA_NONE, num_partitions * sizeof (int)); - - tpa->partition_to_tree_map = (int *)xmalloc (num_partitions * sizeof (int)); - memset (tpa->partition_to_tree_map, TPA_NONE, num_partitions * sizeof (int)); - - x = MAX (40, (num_partitions / 20)); - tpa->trees = VEC_alloc (tree, heap, x); - tpa->first_partition = VEC_alloc (int, heap, x); - - return tpa; - -} - - -/* Remove PARTITION_INDEX from TREE_INDEX's list in the tpa structure TPA. */ - -void -tpa_remove_partition (tpa_p tpa, int tree_index, int partition_index) -{ - int i; - - i = tpa_first_partition (tpa, tree_index); - if (i == partition_index) - { - VEC_replace (int, tpa->first_partition, tree_index, - tpa->next_partition[i]); - } - else - { - for ( ; i != TPA_NONE; i = tpa_next_partition (tpa, i)) - { - if (tpa->next_partition[i] == partition_index) - { - tpa->next_partition[i] = tpa->next_partition[partition_index]; - break; - } - } - } -} - - -/* Free the memory used by tree_partition_associator object TPA. */ - -void -tpa_delete (tpa_p tpa) -{ - if (!tpa) - return; - - VEC_free (tree, heap, tpa->trees); - VEC_free (int, heap, tpa->first_partition); - free (tpa->partition_to_tree_map); - free (tpa->next_partition); - free (tpa); -} - - -/* This function will remove any tree entries from TPA which have only a single - element. This will help keep the size of the conflict graph down. The - function returns the number of remaining tree lists. */ - -int -tpa_compact (tpa_p tpa) -{ - int last, x, y, first, swap_i; - tree swap_t; - - /* Find the last list which has more than 1 partition. */ - for (last = tpa->num_trees - 1; last > 0; last--) - { - first = tpa_first_partition (tpa, last); - if (tpa_next_partition (tpa, first) != NO_PARTITION) - break; - } - - x = 0; - while (x < last) - { - first = tpa_first_partition (tpa, x); - - /* If there is not more than one partition, swap with the current end - of the tree list. */ - if (tpa_next_partition (tpa, first) == NO_PARTITION) - { - swap_t = VEC_index (tree, tpa->trees, last); - swap_i = VEC_index (int, tpa->first_partition, last); - - /* Update the last entry. Since it is known to only have one - partition, there is nothing else to update. */ - VEC_replace (tree, tpa->trees, last, - VEC_index (tree, tpa->trees, x)); - VEC_replace (int, tpa->first_partition, last, - VEC_index (int, tpa->first_partition, x)); - tpa->partition_to_tree_map[tpa_first_partition (tpa, last)] = last; - - /* Since this list is known to have more than one partition, update - the list owner entries. */ - VEC_replace (tree, tpa->trees, x, swap_t); - VEC_replace (int, tpa->first_partition, x, swap_i); - for (y = tpa_first_partition (tpa, x); - y != NO_PARTITION; - y = tpa_next_partition (tpa, y)) - tpa->partition_to_tree_map[y] = x; - - /* Ensure last is a list with more than one partition. */ - last--; - for (; last > x; last--) - { - first = tpa_first_partition (tpa, last); - if (tpa_next_partition (tpa, first) != NO_PARTITION) - break; - } - } - x++; - } - - first = tpa_first_partition (tpa, x); - if (tpa_next_partition (tpa, first) != NO_PARTITION) - x++; - tpa->uncompressed_num = tpa->num_trees; - tpa->num_trees = x; - return last; -} - - -/* Initialize a root_var object with SSA partitions from MAP which are based - on each root variable. */ - -root_var_p -root_var_init (var_map map) -{ - root_var_p rv; - int num_partitions = num_var_partitions (map); - int x, p; - tree t; - var_ann_t ann; - sbitmap seen; - - rv = tpa_init (map); - if (!rv) - return NULL; - - seen = sbitmap_alloc (num_partitions); - sbitmap_zero (seen); - - /* Start at the end and work towards the front. This will provide a list - that is ordered from smallest to largest. */ - for (x = num_partitions - 1; x >= 0; x--) - { - t = partition_to_var (map, x); - - /* The var map may not be compacted yet, so check for NULL. */ - if (!t) - continue; - - p = var_to_partition (map, t); - - gcc_assert (p != NO_PARTITION); - - /* Make sure we only put coalesced partitions into the list once. */ - if (TEST_BIT (seen, p)) - continue; - SET_BIT (seen, p); - if (TREE_CODE (t) == SSA_NAME) - t = SSA_NAME_VAR (t); - ann = var_ann (t); - if (ann->root_var_processed) - { - rv->next_partition[p] = VEC_index (int, rv->first_partition, - VAR_ANN_ROOT_INDEX (ann)); - VEC_replace (int, rv->first_partition, VAR_ANN_ROOT_INDEX (ann), p); - } - else - { - ann->root_var_processed = 1; - VAR_ANN_ROOT_INDEX (ann) = rv->num_trees++; - VEC_safe_push (tree, heap, rv->trees, t); - VEC_safe_push (int, heap, rv->first_partition, p); - } - rv->partition_to_tree_map[p] = VAR_ANN_ROOT_INDEX (ann); - } - - /* Reset the out_of_ssa_tag flag on each variable for later use. */ - for (x = 0; x < rv->num_trees; x++) - { - t = VEC_index (tree, rv->trees, x); - var_ann (t)->root_var_processed = 0; - } - - sbitmap_free (seen); - return rv; -} - - -/* Hash function for 2 integer coalesce pairs. */ -#define COALESCE_HASH_FN(R1, R2) ((R2) * ((R2) - 1) / 2 + (R1)) - - -/* Return hash value for partition pair PAIR. */ - -unsigned int -partition_pair_map_hash (const void *pair) -{ - hashval_t a = (hashval_t)(((partition_pair_p)pair)->first_partition); - hashval_t b = (hashval_t)(((partition_pair_p)pair)->second_partition); - - return COALESCE_HASH_FN (a,b); -} - - -/* Return TRUE if PAIR1 is equivalent to PAIR2. */ - -int -partition_pair_map_eq (const void *pair1, const void *pair2) -{ - partition_pair_p p1 = (partition_pair_p) pair1; - partition_pair_p p2 = (partition_pair_p) pair2; - - return (p1->first_partition == p2->first_partition - && p1->second_partition == p2->second_partition); -} - - -/* Create a new coalesce list object from MAP and return it. */ - -coalesce_list_p -create_coalesce_list (var_map map) -{ - coalesce_list_p list; - unsigned size = num_ssa_names * 3; - - if (size < 40) - size = 40; - - list = xmalloc (sizeof (struct coalesce_list_d)); - list->list = htab_create (size, partition_pair_map_hash, - partition_pair_map_eq, NULL); - - list->map = map; - list->sorted = NULL; - list->add_mode = true; - list->num_sorted = 0; - return list; -} - - -/* Delete coalesce list CL. */ - -void -delete_coalesce_list (coalesce_list_p cl) -{ - htab_delete (cl->list); - if (cl->sorted) - free (cl->sorted); - gcc_assert (cl->num_sorted == 0); - free (cl); -} - - -/* Find a matching coalesce pair object in CL for partitions P1 and P2. If - one isn't found, return NULL if CREATE is false, otherwise create a new - coalesce pair object and return it. */ - -static partition_pair_p -find_partition_pair (coalesce_list_p cl, int p1, int p2, bool create) -{ - struct partition_pair p, *pair; - void **slot; - unsigned int hash; - - /* normalize so that p1 is the smaller value. */ - if (p2 < p1) - { - p.first_partition = p2; - p.second_partition = p1; - } - else - { - p.first_partition = p1; - p.second_partition = p2; - } - - - hash = partition_pair_map_hash (&p); - pair = (struct partition_pair *) htab_find_with_hash (cl->list, &p, hash); - - if (create && !pair) - { - gcc_assert (cl->add_mode); - pair = xmalloc (sizeof (struct partition_pair)); - pair->first_partition = p.first_partition; - pair->second_partition = p.second_partition; - pair->cost = 0; - slot = htab_find_slot_with_hash (cl->list, pair, hash, INSERT); - *(struct partition_pair **)slot = pair; - } - - return pair; -} - -/* Return cost of execution of copy instruction with FREQUENCY - possibly on CRITICAL edge and in HOT basic block. */ -int -coalesce_cost (int frequency, bool hot, bool critical) -{ - /* Base costs on BB frequencies bounded by 1. */ - int cost = frequency; - - if (!cost) - cost = 1; - if (optimize_size || hot) - cost = 1; - /* Inserting copy on critical edge costs more - than inserting it elsewhere. */ - if (critical) - cost *= 2; - return cost; -} - -/* Add a potential coalesce between P1 and P2 in CL with a cost of VALUE. */ - -void -add_coalesce (coalesce_list_p cl, int p1, int p2, - int value) -{ - partition_pair_p node; - - gcc_assert (cl->add_mode); - - if (p1 == p2) - return; - - node = find_partition_pair (cl, p1, p2, true); - - node->cost += value; -} - - -/* Comparison function to allow qsort to sort P1 and P2 in Ascending order. */ - -static -int compare_pairs (const void *p1, const void *p2) -{ - return (*(partition_pair_p *)p1)->cost - (*(partition_pair_p *)p2)->cost; -} - - -static inline int -num_coalesce_pairs (coalesce_list_p cl) -{ - return htab_elements (cl->list); -} - -typedef struct -{ - htab_iterator hti; -} partition_pair_iterator; - -static inline partition_pair_p -first_partition_pair (coalesce_list_p cl, partition_pair_iterator *iter) -{ - partition_pair_p pair; - - pair = (partition_pair_p) first_htab_element (&(iter->hti), cl->list); - return pair; -} - -static inline bool -end_partition_pair_p (partition_pair_iterator *iter) -{ - return end_htab_p (&(iter->hti)); -} - -static inline partition_pair_p -next_partition_pair (partition_pair_iterator *iter) -{ - partition_pair_p pair; - - pair = (partition_pair_p) next_htab_element (&(iter->hti)); - return pair; -} - -#define FOR_EACH_PARTITION_PAIR(PAIR, ITER, CL) \ - for ((PAIR) = first_partition_pair ((CL), &(ITER)); \ - !end_partition_pair_p (&(ITER)); \ - (PAIR) = next_partition_pair (&(ITER))) - - -/* Prepare CL for removal of preferred pairs. When finished, list element - 0 has all the coalesce pairs, sorted in order from most important coalesce - to least important. */ - -void -sort_coalesce_list (coalesce_list_p cl) -{ - unsigned x, num; - partition_pair_p p; - partition_pair_iterator ppi; - - gcc_assert (cl->add_mode); - - cl->add_mode = false; - - /* allocate a vector for the pair pointers. */ - num = num_coalesce_pairs (cl); - cl->num_sorted = num; - if (num == 0) - return; - cl->sorted = XNEWVEC (partition_pair_p, num); - - /* Populate the vector with pointers to the partition pairs. */ - - x = 0; - FOR_EACH_PARTITION_PAIR (p, ppi, cl) - cl->sorted[x++] = p; - gcc_assert (x == num); - - if (num == 1) - return; - - if (num == 2) - { - if (cl->sorted[0]->cost > cl->sorted[1]->cost) - { - p = cl->sorted[0]; - cl->sorted[0] = cl->sorted[1]; - cl->sorted[1] = p; - } - return; - } - - /* Only call qsort if there are more than 2 items. */ - if (num > 2) - qsort (cl->sorted, num, sizeof (partition_pair_p), compare_pairs); -} - - -/* Retrieve the best remaining pair to coalesce from CL. Returns the 2 - partitions via P1 and P2. Their calculated cost is returned by the function. - NO_BEST_COALESCE is returned if the coalesce list is empty. */ - -static int -pop_best_coalesce (coalesce_list_p cl, int *p1, int *p2) -{ - partition_pair_p node; - int ret; - - gcc_assert (!cl->add_mode); - - if (cl->num_sorted == 0) - return NO_BEST_COALESCE; - - node = cl->sorted[--(cl->num_sorted)]; - - *p1 = node->first_partition; - *p2 = node->second_partition; - ret = node->cost; - free (node); - - return ret; -} - - -/* If variable VAR is in a partition in MAP, add a conflict in GRAPH between - VAR and any other live partitions in VEC which are associated via TPA. - Reset the live bit in VEC. */ - -static inline void -add_conflicts_if_valid (tpa_p tpa, conflict_graph graph, - var_map map, bitmap vec, tree var) -{ - int p, y, first; - p = var_to_partition (map, var); - if (p != NO_PARTITION) - { - bitmap_clear_bit (vec, p); - first = tpa_find_tree (tpa, p); - /* If find returns nothing, this object isn't interesting. */ - if (first == TPA_NONE) - return; - /* Only add interferences between objects in the same list. */ - for (y = tpa_first_partition (tpa, first); - y != TPA_NONE; - y = tpa_next_partition (tpa, y)) - { - if (bitmap_bit_p (vec, y)) - conflict_graph_add (graph, p, y); - } - } -} - - -/* If VAR is in a partition of MAP, set the bit for that partition in VEC. */ - -static inline void -set_if_valid (var_map map, bitmap vec, tree var) -{ - int p = var_to_partition (map, var); - if (p != NO_PARTITION) - bitmap_set_bit (vec, p); -} - -/* Return a conflict graph for the information contained in LIVE_INFO. Only - conflicts between items in the same TPA list are added. If optional - coalesce list CL is passed in, any copies encountered are added. */ - -conflict_graph -build_tree_conflict_graph (tree_live_info_p liveinfo, tpa_p tpa, - coalesce_list_p cl) -{ - conflict_graph graph; - var_map map; - bitmap live; - unsigned x, y, i; - basic_block bb; - int *partition_link, *tpa_nodes; - VEC(int,heap) *tpa_to_clear; - unsigned l; - ssa_op_iter iter; - bitmap_iterator bi; - - map = live_var_map (liveinfo); - graph = conflict_graph_new (num_var_partitions (map)); - - if (tpa_num_trees (tpa) == 0) - return graph; - - live = BITMAP_ALLOC (NULL); - - partition_link = XCNEWVEC (int, num_var_partitions (map) + 1); - tpa_nodes = XCNEWVEC (int, tpa_num_trees (tpa)); - tpa_to_clear = VEC_alloc (int, heap, 50); - - FOR_EACH_BB (bb) - { - block_stmt_iterator bsi; - tree phi; - int idx; - - /* Start with live on exit temporaries. */ - bitmap_copy (live, live_on_exit (liveinfo, bb)); - - for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi)) - { - bool is_a_copy = false; - tree stmt = bsi_stmt (bsi); - - /* A copy between 2 partitions does not introduce an interference - by itself. If they did, you would never be able to coalesce - two things which are copied. If the two variables really do - conflict, they will conflict elsewhere in the program. - - This is handled specially here since we may also be interested - in copies between real variables and SSA_NAME variables. We may - be interested in trying to coalesce SSA_NAME variables with - root variables in some cases. */ - - if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT) - { - tree lhs = GIMPLE_STMT_OPERAND (stmt, 0); - tree rhs = GIMPLE_STMT_OPERAND (stmt, 1); - int p1, p2; - int bit; - - if (DECL_P (lhs) || TREE_CODE (lhs) == SSA_NAME) - p1 = var_to_partition (map, lhs); - else - p1 = NO_PARTITION; - - if (DECL_P (rhs) || TREE_CODE (rhs) == SSA_NAME) - p2 = var_to_partition (map, rhs); - else - p2 = NO_PARTITION; - - if (p1 != NO_PARTITION && p2 != NO_PARTITION) - { - is_a_copy = true; - bit = bitmap_bit_p (live, p2); - /* If the RHS is live, make it not live while we add - the conflicts, then make it live again. */ - if (bit) - bitmap_clear_bit (live, p2); - add_conflicts_if_valid (tpa, graph, map, live, lhs); - if (bit) - bitmap_set_bit (live, p2); - if (cl) - add_coalesce (cl, p1, p2, - coalesce_cost (bb->frequency, - maybe_hot_bb_p (bb), false)); - set_if_valid (map, live, rhs); - } - } - - if (!is_a_copy) - { - tree var; - FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_DEF) - { - add_conflicts_if_valid (tpa, graph, map, live, var); - } - - FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE) - { - set_if_valid (map, live, var); - } - } - } - - /* If result of a PHI is unused, then the loops over the statements - will not record any conflicts. However, since the PHI node is - going to be translated out of SSA form we must record a conflict - between the result of the PHI and any variables with are live. - Otherwise the out-of-ssa translation may create incorrect code. */ - for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) - { - tree result = PHI_RESULT (phi); - int p = var_to_partition (map, result); - - if (p != NO_PARTITION && ! bitmap_bit_p (live, p)) - add_conflicts_if_valid (tpa, graph, map, live, result); - } - - /* Anything which is still live at this point interferes. - In order to implement this efficiently, only conflicts between - partitions which have the same TPA root need be added. - TPA roots which have been seen are tracked in 'tpa_nodes'. A nonzero - entry points to an index into 'partition_link', which then indexes - into itself forming a linked list of partitions sharing a tpa root - which have been seen as live up to this point. Since partitions start - at index zero, all entries in partition_link are (partition + 1). - - Conflicts are added between the current partition and any already seen. - tpa_clear contains all the tpa_roots processed, and these are the only - entries which need to be zero'd out for a clean restart. */ - - EXECUTE_IF_SET_IN_BITMAP (live, 0, x, bi) - { - i = tpa_find_tree (tpa, x); - if (i != (unsigned)TPA_NONE) - { - int start = tpa_nodes[i]; - /* If start is 0, a new root reference list is being started. - Register it to be cleared. */ - if (!start) - VEC_safe_push (int, heap, tpa_to_clear, i); - - /* Add interferences to other tpa members seen. */ - for (y = start; y != 0; y = partition_link[y]) - conflict_graph_add (graph, x, y - 1); - tpa_nodes[i] = x + 1; - partition_link[x + 1] = start; - } - } - - /* Now clear the used tpa root references. */ - for (l = 0; VEC_iterate (int, tpa_to_clear, l, idx); l++) - tpa_nodes[idx] = 0; - VEC_truncate (int, tpa_to_clear, 0); - } - - free (tpa_nodes); - free (partition_link); - VEC_free (int, heap, tpa_to_clear); - BITMAP_FREE (live); - return graph; -} - - -/* This routine will attempt to coalesce the elements in TPA subject to the - conflicts found in GRAPH. If optional coalesce_list CL is provided, - only coalesces specified within the coalesce list are attempted. Otherwise - an attempt is made to coalesce as many partitions within each TPA grouping - as possible. If DEBUG is provided, debug output will be sent there. */ - -void -coalesce_tpa_members (tpa_p tpa, conflict_graph graph, var_map map, - coalesce_list_p cl, FILE *debug) -{ - int x, y, z, w; - tree var, tmp; - - /* Attempt to coalesce any items in a coalesce list. */ - if (cl) - { - while (pop_best_coalesce (cl, &x, &y) != NO_BEST_COALESCE) - { - if (debug) - { - fprintf (debug, "Coalesce list: (%d)", x); - print_generic_expr (debug, partition_to_var (map, x), TDF_SLIM); - fprintf (debug, " & (%d)", y); - print_generic_expr (debug, partition_to_var (map, y), TDF_SLIM); - } - - w = tpa_find_tree (tpa, x); - z = tpa_find_tree (tpa, y); - if (w != z || w == TPA_NONE || z == TPA_NONE) - { - if (debug) - { - if (w != z) - fprintf (debug, ": Fail, Non-matching TPA's\n"); - if (w == TPA_NONE) - fprintf (debug, ": Fail %d non TPA.\n", x); - else - fprintf (debug, ": Fail %d non TPA.\n", y); - } - continue; - } - var = partition_to_var (map, x); - tmp = partition_to_var (map, y); - x = var_to_partition (map, var); - y = var_to_partition (map, tmp); - if (debug) - fprintf (debug, " [map: %d, %d] ", x, y); - if (x == y) - { - if (debug) - fprintf (debug, ": Already Coalesced.\n"); - continue; - } - if (!conflict_graph_conflict_p (graph, x, y)) - { - z = var_union (map, var, tmp); - if (z == NO_PARTITION) - { - if (debug) - fprintf (debug, ": Unable to perform partition union.\n"); - continue; - } - - /* z is the new combined partition. We need to remove the other - partition from the list. Set x to be that other partition. */ - if (z == x) - { - conflict_graph_merge_regs (graph, x, y); - w = tpa_find_tree (tpa, y); - tpa_remove_partition (tpa, w, y); - } - else - { - conflict_graph_merge_regs (graph, y, x); - w = tpa_find_tree (tpa, x); - tpa_remove_partition (tpa, w, x); - } - - if (debug) - fprintf (debug, ": Success -> %d\n", z); - } - else - if (debug) - fprintf (debug, ": Fail due to conflict\n"); - } - /* If using a coalesce list, don't try to coalesce anything else. */ - return; - } - - for (x = 0; x < tpa_num_trees (tpa); x++) - { - while (tpa_first_partition (tpa, x) != TPA_NONE) - { - int p1, p2; - /* Coalesce first partition with anything that doesn't conflict. */ - y = tpa_first_partition (tpa, x); - tpa_remove_partition (tpa, x, y); - - var = partition_to_var (map, y); - /* p1 is the partition representative to which y belongs. */ - p1 = var_to_partition (map, var); - - for (z = tpa_next_partition (tpa, y); - z != TPA_NONE; - z = tpa_next_partition (tpa, z)) - { - tmp = partition_to_var (map, z); - /* p2 is the partition representative to which z belongs. */ - p2 = var_to_partition (map, tmp); - if (debug) - { - fprintf (debug, "Coalesce : "); - print_generic_expr (debug, var, TDF_SLIM); - fprintf (debug, " &"); - print_generic_expr (debug, tmp, TDF_SLIM); - fprintf (debug, " (%d ,%d)", p1, p2); - } - - /* If partitions are already merged, don't check for conflict. */ - if (tmp == var) - { - tpa_remove_partition (tpa, x, z); - if (debug) - fprintf (debug, ": Already coalesced\n"); - } - else - if (!conflict_graph_conflict_p (graph, p1, p2)) - { - int v; - if (tpa_find_tree (tpa, y) == TPA_NONE - || tpa_find_tree (tpa, z) == TPA_NONE) - { - if (debug) - fprintf (debug, ": Fail non-TPA member\n"); - continue; - } - if ((v = var_union (map, var, tmp)) == NO_PARTITION) - { - if (debug) - fprintf (debug, ": Fail cannot combine partitions\n"); - continue; - } - - tpa_remove_partition (tpa, x, z); - if (v == p1) - conflict_graph_merge_regs (graph, v, z); - else - { - /* Update the first partition's representative. */ - conflict_graph_merge_regs (graph, v, y); - p1 = v; - } - - /* The root variable of the partition may be changed - now. */ - var = partition_to_var (map, p1); - - if (debug) - fprintf (debug, ": Success -> %d\n", v); - } - else - if (debug) - fprintf (debug, ": Fail, Conflict\n"); - } - } - } -} - - -/* Send debug info for coalesce list CL to file F. */ - -void -dump_coalesce_list (FILE *f, coalesce_list_p cl) -{ - partition_pair_p node; - partition_pair_iterator ppi; - int x; - tree var; - - if (cl->add_mode) - { - fprintf (f, "Coalesce List:\n"); - FOR_EACH_PARTITION_PAIR (node, ppi, cl) - { - tree var1 = partition_to_var (cl->map, node->first_partition); - tree var2 = partition_to_var (cl->map, node->second_partition); - print_generic_expr (f, var1, TDF_SLIM); - fprintf (f, " <-> "); - print_generic_expr (f, var2, TDF_SLIM); - fprintf (f, " (%1d), ", node->cost); - fprintf (f, "\n"); - } - } - else - { - fprintf (f, "Sorted Coalesce list:\n"); - for (x = cl->num_sorted - 1 ; x >=0; x--) - { - node = cl->sorted[x]; - fprintf (f, "(%d) ", node->cost); - var = partition_to_var (cl->map, node->first_partition); - print_generic_expr (f, var, TDF_SLIM); - fprintf (f, " <-> "); - var = partition_to_var (cl->map, node->second_partition); - print_generic_expr (f, var, TDF_SLIM); - fprintf (f, "\n"); - } - } -} - - -/* Output tree_partition_associator object TPA to file F.. */ - -void -tpa_dump (FILE *f, tpa_p tpa) -{ - int x, i; - - if (!tpa) - return; - - for (x = 0; x < tpa_num_trees (tpa); x++) - { - print_generic_expr (f, tpa_tree (tpa, x), TDF_SLIM); - fprintf (f, " : ("); - for (i = tpa_first_partition (tpa, x); - i != TPA_NONE; - i = tpa_next_partition (tpa, i)) - { - fprintf (f, "(%d)",i); - print_generic_expr (f, partition_to_var (tpa->map, i), TDF_SLIM); - fprintf (f, " "); - -#ifdef ENABLE_CHECKING - if (tpa_find_tree (tpa, i) != x) - fprintf (f, "**find tree incorrectly set** "); -#endif - - } - fprintf (f, ")\n"); - } - fflush (f); -} - - /* Output partition map MAP to file F. */ void @@ -1700,8 +788,8 @@ dump_var_map (FILE *f, var_map map) for (x = 0; x < map->num_partitions; x++) { - if (map->compact_to_partition != NULL) - p = map->compact_to_partition[x]; + if (map->view_to_partition != NULL) + p = map->view_to_partition[x]; else p = x; @@ -1712,8 +800,8 @@ dump_var_map (FILE *f, var_map map) for (y = 1; y < num_ssa_names; y++) { p = partition_find (map->var_partition, y); - if (map->partition_to_compact) - p = map->partition_to_compact[p]; + if (map->partition_to_view) + p = map->partition_to_view[p]; if (p == (int)x) { if (t++ == 0) @@ -1771,7 +859,10 @@ dump_live_info (FILE *f, tree_live_info_p live, int flag) } } + #ifdef ENABLE_CHECKING +/* Verify that SSA_VAR is a non-virtual SSA_NAME. */ + void register_ssa_partition_check (tree ssa_var) { @@ -1787,6 +878,7 @@ register_ssa_partition_check (tree ssa_var) /* Verify that the info in LIVE matches the current cfg. */ + static void verify_live_on_entry (tree_live_info_p live) { @@ -1802,7 +894,6 @@ verify_live_on_entry (tree_live_info_p live) /* Check for live on entry partitions and report those with a DEF in the program. This will typically mean an optimization has done something wrong. */ - bb = ENTRY_BLOCK_PTR; num = 0; FOR_EACH_EDGE (e, ei, bb->succs) diff --git a/gcc/tree-ssa-live.h b/gcc/tree-ssa-live.h index 19c708922e8..bddd4ba77c6 100644 --- a/gcc/tree-ssa-live.h +++ b/gcc/tree-ssa-live.h @@ -26,56 +26,83 @@ Boston, MA 02110-1301, USA. */ #include "partition.h" #include "vecprim.h" -/* Used to create the variable mapping when we go out of SSA form. */ + + +/* Used to create the variable mapping when we go out of SSA form. + + Mapping from an ssa_name to a partition number is maintained, as well as + partition number to back to ssa_name. A parition can also be represented + by a non-ssa_name variable. This allows ssa_names and thier partition to + be coalesced with live on entry compiler variables, as well as eventually + having real compiler variables assigned to each partition as part of the + final stage of going of of ssa. + + Non-ssa_names maintain their partition index in the variable annotation. + + This data structure also supports "views", which work on a subset of all + partitions. This allows the coalescer to decide what partitions are + interesting to it, and only work with those partitions. Whenever the view + is changed, the partition numbers change, but none of the partition groupings + change. (ie, it is truly a view since it doesnt change anything) + + The final component of the data structure is the basevar map. This provides + a list of all the different base variables which occue in a partition view, + and a unique index for each one. Routines are provided to quickly produce + the base variable of a partition. + + Note that members of a partition MUST all have the same base variable. */ + typedef struct _var_map { - /* The partition of all variables. */ + /* The partition manager of all variables. */ partition var_partition; - /* Vector for compacting partitions. */ - int *partition_to_compact; - int *compact_to_partition; + /* Vector for managing partitions views. */ + int *partition_to_view; + int *view_to_partition; - /* Mapping of partition numbers to vars. */ + /* Mapping of partition numbers to variables. */ tree *partition_to_var; - /* Current number of partitions. */ + /* Current number of partitions in var_map based on the current view. */ unsigned int num_partitions; - /* Original partition size. */ + /* Original full partition size. */ unsigned int partition_size; + + /* Number of base variables in the base var list. */ + int num_basevars; + + /* Map of partitions numbers to base variable table indexes. */ + int *partition_to_base_index; + + /* Table of base variable's. */ + VEC (tree, heap) *basevars; } *var_map; + +/* Partition number of a non ssa-name variable. */ #define VAR_ANN_PARTITION(ann) (ann->partition) -#define VAR_ANN_ROOT_INDEX(ann) (ann->root_index) +/* Index iot the basevar table of a non ssa-name variable. */ +#define VAR_ANN_BASE_INDEX(ann) (ann->base_index) + +/* Value used to represent no partition number. */ #define NO_PARTITION -1 -/* Flags to pass to compact_var_map */ - -#define VARMAP_NORMAL 0 -#define VARMAP_NO_SINGLE_DEFS 1 - 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 partition_view_normal (var_map, bool); +extern void partition_view_bitmap (var_map, bitmap, bool); #ifdef ENABLE_CHECKING extern void register_ssa_partition_check (tree ssa_var); #endif -static inline unsigned num_var_partitions (var_map); -static inline tree var_to_partition_to_var (var_map, tree); -static inline tree partition_to_var (var_map, int); -static inline int var_to_partition (var_map, tree); -static inline tree version_to_var (var_map, int); -static inline void register_ssa_partition (var_map, tree); -extern var_map create_ssa_var_map (void); - -/* Number of partitions in MAP. */ +/* Return number of partitions in MAP. */ static inline unsigned num_var_partitions (var_map map) @@ -90,8 +117,8 @@ num_var_partitions (var_map map) static inline tree partition_to_var (var_map map, int i) { - if (map->compact_to_partition) - i = map->compact_to_partition[i]; + if (map->view_to_partition) + i = map->view_to_partition[i]; i = partition_find (map->var_partition, i); return map->partition_to_var[i]; } @@ -100,12 +127,13 @@ partition_to_var (var_map map, int i) /* Given ssa_name VERSION, if it has a partition in MAP, return the var it is associated with. Otherwise return NULL. */ -static inline tree version_to_var (var_map map, int version) +static inline tree +version_to_var (var_map map, int version) { int part; part = partition_find (map->var_partition, version); - if (map->partition_to_compact) - part = map->partition_to_compact[part]; + if (map->partition_to_view) + part = map->partition_to_view[part]; if (part == NO_PARTITION) return NULL_TREE; @@ -125,8 +153,8 @@ var_to_partition (var_map map, tree var) if (TREE_CODE (var) == SSA_NAME) { part = partition_find (map->var_partition, SSA_NAME_VERSION (var)); - if (map->partition_to_compact) - part = map->partition_to_compact[part]; + if (map->partition_to_view) + part = map->partition_to_view[part]; } else { @@ -155,9 +183,29 @@ var_to_partition_to_var (var_map map, tree var) } -/* This routine registers a partition for SSA_VAR with MAP. IS_USE is used - to count references. Any unregistered partitions may be compacted out - later. */ +/* Return the index into the basevar table for PARTITION's base in MAP. */ + +static inline int +basevar_index (var_map map, int partition) +{ + gcc_assert (partition >= 0 + && partition <= (int) num_var_partitions (map)); + return map->partition_to_base_index[partition]; +} + + +/* Return the number of different base variables in MAP. */ + +static inline int +num_basevars (var_map map) +{ + return map->num_basevars; +} + + + +/* This routine registers a partition for SSA_VAR with MAP. Any unregistered + partitions may be filtered out by a view later. */ static inline void register_ssa_partition (var_map map, tree ssa_var) @@ -170,7 +218,7 @@ register_ssa_partition (var_map map, tree ssa_var) version = SSA_NAME_VERSION (ssa_var); if (map->partition_to_var[version] == NULL_TREE) - map->partition_to_var[SSA_NAME_VERSION (ssa_var)] = ssa_var; + map->partition_to_var[version] = ssa_var; } @@ -238,13 +286,6 @@ extern void delete_tree_live_info (tree_live_info_p); #define LIVEDUMP_ALL (LIVEDUMP_ENTRY | LIVEDUMP_EXIT) extern void dump_live_info (FILE *, tree_live_info_p, int); -static inline int partition_is_global (tree_live_info_p, int); -static inline bitmap live_on_entry (tree_live_info_p, basic_block); -static inline bitmap live_on_exit (tree_live_info_p, basic_block); -static inline var_map live_var_map (tree_live_info_p); -static inline void live_merge_and_clear (tree_live_info_p, int, int); -static inline void make_live_on_entry (tree_live_info_p, basic_block, int); - /* Return TRUE if P is marked as a global in LIVE. */ @@ -316,275 +357,9 @@ make_live_on_entry (tree_live_info_p live, basic_block bb , int p) } -/* A tree_partition_associator (TPA)object is a base structure which allows - partitions to be associated with a tree object. +/* From tree-ssa-coalesce.c */ +extern var_map coalesce_ssa_name (void); - A varray of tree elements represent each distinct tree item. - A parallel int array represents the first partition number associated with - the tree. - This partition number is then used as in index into the next_partition - array, which returns the index of the next partition which is associated - with the tree. TPA_NONE indicates the end of the list. - A varray paralleling the partition list 'partition_to_tree_map' is used - to indicate which tree index the partition is in. */ - -typedef struct tree_partition_associator_d -{ - VEC(tree,heap) *trees; - VEC(int,heap) *first_partition; - int *next_partition; - int *partition_to_tree_map; - int num_trees; - int uncompressed_num; - var_map map; -} *tpa_p; - -/* Value returned when there are no more partitions associated with a tree. */ -#define TPA_NONE -1 - -static inline tree tpa_tree (tpa_p, int); -static inline int tpa_first_partition (tpa_p, int); -static inline int tpa_next_partition (tpa_p, int); -static inline int tpa_num_trees (tpa_p); -static inline int tpa_find_tree (tpa_p, int); -static inline void tpa_decompact (tpa_p); -extern void tpa_delete (tpa_p); -extern void tpa_dump (FILE *, tpa_p); -extern void tpa_remove_partition (tpa_p, int, int); -extern int tpa_compact (tpa_p); - - -/* Return the number of distinct tree nodes in TPA. */ - -static inline int -tpa_num_trees (tpa_p tpa) -{ - return tpa->num_trees; -} - - -/* Return the tree node for index I in TPA. */ - -static inline tree -tpa_tree (tpa_p tpa, int i) -{ - return VEC_index (tree, tpa->trees, i); -} - - -/* Return the first partition associated with tree list I in TPA. */ - -static inline int -tpa_first_partition (tpa_p tpa, int i) -{ - return VEC_index (int, tpa->first_partition, i); -} - - -/* Return the next partition after partition I in TPA's list. */ - -static inline int -tpa_next_partition (tpa_p tpa, int i) -{ - return tpa->next_partition[i]; -} - - -/* Return the tree index from TPA whose list contains partition I. - TPA_NONE is returned if I is not associated with any list. */ - -static inline int -tpa_find_tree (tpa_p tpa, int i) -{ - int index; - - index = tpa->partition_to_tree_map[i]; - /* When compressed, any index higher than the number of tree elements is - a compressed element, so return TPA_NONE. */ - if (index != TPA_NONE && index >= tpa_num_trees (tpa)) - { - gcc_assert (tpa->uncompressed_num != -1); - index = TPA_NONE; - } - - return index; -} - - -/* This function removes any compaction which was performed on TPA. */ - -static inline void -tpa_decompact(tpa_p tpa) -{ - gcc_assert (tpa->uncompressed_num != -1); - tpa->num_trees = tpa->uncompressed_num; -} - - -/* Once a var_map has been created and compressed, a complementary root_var - object can be built. This creates a list of all the root variables from - which ssa version names are derived. Each root variable has a list of - which partitions are versions of that root. - - This is implemented using the tree_partition_associator. - - The tree vector is used to represent the root variable. - The list of partitions represent SSA versions of the root variable. */ - -typedef tpa_p root_var_p; - -static inline tree root_var (root_var_p, int); -static inline int root_var_first_partition (root_var_p, int); -static inline int root_var_next_partition (root_var_p, int); -static inline int root_var_num (root_var_p); -static inline void root_var_dump (FILE *, root_var_p); -static inline void root_var_remove_partition (root_var_p, int, int); -static inline void root_var_delete (root_var_p); -static inline int root_var_find (root_var_p, int); -static inline int root_var_compact (root_var_p); -static inline void root_var_decompact (tpa_p); - -extern root_var_p root_var_init (var_map); - -/* Value returned when there are no more partitions associated with a root - variable. */ -#define ROOT_VAR_NONE TPA_NONE - - -/* Return the number of distinct root variables in RV. */ - -static inline int -root_var_num (root_var_p rv) -{ - return tpa_num_trees (rv); -} - - -/* Return root variable I from RV. */ - -static inline tree -root_var (root_var_p rv, int i) -{ - return tpa_tree (rv, i); -} - - -/* Return the first partition in RV belonging to root variable list I. */ - -static inline int -root_var_first_partition (root_var_p rv, int i) -{ - return tpa_first_partition (rv, i); -} - - -/* Return the next partition after partition I in a root list from RV. */ - -static inline int -root_var_next_partition (root_var_p rv, int i) -{ - return tpa_next_partition (rv, i); -} - - -/* Send debug info for root_var list RV to file F. */ - -static inline void -root_var_dump (FILE *f, root_var_p rv) -{ - fprintf (f, "\nRoot Var dump\n"); - tpa_dump (f, rv); - fprintf (f, "\n"); -} - - -/* Destroy root_var object RV. */ - -static inline void -root_var_delete (root_var_p rv) -{ - tpa_delete (rv); -} - - -/* Remove partition PARTITION_INDEX from root_var list ROOT_INDEX in RV. */ - -static inline void -root_var_remove_partition (root_var_p rv, int root_index, int partition_index) -{ - tpa_remove_partition (rv, root_index, partition_index); -} - - -/* Return the root_var list index for partition I in RV. */ - -static inline int -root_var_find (root_var_p rv, int i) -{ - return tpa_find_tree (rv, i); -} - - -/* Hide single element lists in RV. */ - -static inline int -root_var_compact (root_var_p rv) -{ - return tpa_compact (rv); -} - - -/* Expose the single element lists in RV. */ - -static inline void -root_var_decompact (root_var_p rv) -{ - tpa_decompact (rv); -} - - -/* This set of routines implements a coalesce_list. This is an object which - is used to track pairs of partitions which are desirable to coalesce - together at some point. Costs are associated with each pair, and when - all desired information has been collected, the object can be used to - order the pairs for processing. */ - -/* This structure defines a pair entry. */ - -typedef struct partition_pair -{ - int first_partition; - int second_partition; - int cost; -} * partition_pair_p; - -extern unsigned int partition_pair_map_hash (const void *); -extern int partition_pair_map_eq (const void *, const void *); - -/* This structure maintains the list of coalesce pairs. */ - -typedef struct coalesce_list_d -{ - var_map map; - htab_t list; - partition_pair_p *sorted; - int num_sorted; - bool add_mode; -} *coalesce_list_p; - -extern coalesce_list_p create_coalesce_list (var_map); -extern void add_coalesce (coalesce_list_p, int, int, int); -extern int coalesce_cost (int, bool, bool); -extern void sort_coalesce_list (coalesce_list_p); -extern void dump_coalesce_list (FILE *, coalesce_list_p); -extern void delete_coalesce_list (coalesce_list_p); - -#define NO_BEST_COALESCE -1 - -extern conflict_graph build_tree_conflict_graph (tree_live_info_p, tpa_p, - coalesce_list_p); -extern void coalesce_tpa_members (tpa_p tpa, conflict_graph graph, var_map map, - coalesce_list_p cl, FILE *); /* From tree-ssa-ter.c */ extern tree *find_replaceable_exprs (var_map); diff --git a/gcc/tree-ssa-ter.c b/gcc/tree-ssa-ter.c index 2bd58cc38f2..78f8a121f09 100644 --- a/gcc/tree-ssa-ter.c +++ b/gcc/tree-ssa-ter.c @@ -25,29 +25,11 @@ Boston, MA 02110-1301, USA. */ #include "coretypes.h" #include "tm.h" #include "tree.h" -#include "flags.h" -#include "rtl.h" -#include "tm_p.h" -#include "ggc.h" -#include "langhooks.h" -#include "hard-reg-set.h" -#include "basic-block.h" -#include "output.h" -#include "expr.h" -#include "function.h" #include "diagnostic.h" #include "bitmap.h" #include "tree-flow.h" -#include "tree-gimple.h" -#include "tree-inline.h" -#include "varray.h" -#include "timevar.h" -#include "hashtab.h" #include "tree-dump.h" #include "tree-ssa-live.h" -#include "tree-pass.h" -#include "toplev.h" -#include "vecprim.h" /* Temporary Expression Replacement (TER)