| /* Coalesce SSA_NAMES together for the out-of-ssa pass. |
| Copyright (C) 2004-2022 Free Software Foundation, Inc. |
| Contributed by Andrew MacLeod <amacleod@redhat.com> |
| |
| 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "predict.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "ssa.h" |
| #include "tree-ssa.h" |
| #include "tree-pretty-print.h" |
| #include "diagnostic-core.h" |
| #include "dumpfile.h" |
| #include "gimple-iterator.h" |
| #include "tree-ssa-live.h" |
| #include "tree-ssa-coalesce.h" |
| #include "explow.h" |
| #include "tree-dfa.h" |
| #include "stor-layout.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. */ |
| |
| struct coalesce_pair |
| { |
| int first_element; |
| int second_element; |
| int cost; |
| |
| /* A count of the number of unique partitions this pair would conflict |
| with if coalescing was successful. This is the secondary sort key, |
| given two pairs with equal costs, we will prefer the pair with a smaller |
| conflict set. |
| |
| This is lazily initialized when we discover two coalescing pairs have |
| the same primary cost. |
| |
| Note this is not updated and propagated as pairs are coalesced. */ |
| int conflict_count; |
| |
| /* The order in which coalescing pairs are discovered is recorded in this |
| field, which is used as the final tie breaker when sorting coalesce |
| pairs. */ |
| int index; |
| }; |
| |
| /* This represents a conflict graph. Implemented as an array of bitmaps. |
| A full matrix is used for conflicts rather than just upper triangular form. |
| this makes it much simpler and faster to perform conflict merges. */ |
| |
| struct ssa_conflicts |
| { |
| bitmap_obstack obstack; /* A place to allocate our bitmaps. */ |
| vec<bitmap> conflicts; |
| }; |
| |
| /* The narrow API of the qsort comparison function doesn't allow easy |
| access to additional arguments. So we have two globals (ick) to hold |
| the data we need. They're initialized before the call to qsort and |
| wiped immediately after. */ |
| static ssa_conflicts *conflicts_; |
| static var_map map_; |
| |
| /* Coalesce pair hashtable helpers. */ |
| |
| struct coalesce_pair_hasher : nofree_ptr_hash <coalesce_pair> |
| { |
| static inline hashval_t hash (const coalesce_pair *); |
| static inline bool equal (const coalesce_pair *, const coalesce_pair *); |
| }; |
| |
| /* Hash function for coalesce list. Calculate hash for PAIR. */ |
| |
| inline hashval_t |
| coalesce_pair_hasher::hash (const coalesce_pair *pair) |
| { |
| hashval_t a = (hashval_t)(pair->first_element); |
| hashval_t b = (hashval_t)(pair->second_element); |
| |
| return b * (b - 1) / 2 + a; |
| } |
| |
| /* Equality function for coalesce list hash table. Compare PAIR1 and PAIR2, |
| returning TRUE if the two pairs are equivalent. */ |
| |
| inline bool |
| coalesce_pair_hasher::equal (const coalesce_pair *p1, const coalesce_pair *p2) |
| { |
| return (p1->first_element == p2->first_element |
| && p1->second_element == p2->second_element); |
| } |
| |
| typedef hash_table<coalesce_pair_hasher> coalesce_table_type; |
| typedef coalesce_table_type::iterator coalesce_iterator_type; |
| |
| |
| struct cost_one_pair |
| { |
| int first_element; |
| int second_element; |
| cost_one_pair *next; |
| }; |
| |
| /* This structure maintains the list of coalesce pairs. */ |
| |
| struct coalesce_list |
| { |
| coalesce_table_type *list; /* Hash table. */ |
| coalesce_pair **sorted; /* List when sorted. */ |
| int num_sorted; /* Number in the sorted list. */ |
| cost_one_pair *cost_one_list;/* Single use coalesces with cost 1. */ |
| obstack ob; |
| }; |
| |
| #define NO_BEST_COALESCE -1 |
| #define MUST_COALESCE_COST INT_MAX |
| |
| |
| /* Return cost of execution of copy instruction with FREQUENCY. */ |
| |
| static inline int |
| coalesce_cost (int frequency, bool optimize_for_size) |
| { |
| /* Base costs on BB frequencies bounded by 1. */ |
| int cost = frequency; |
| |
| if (!cost) |
| cost = 1; |
| |
| if (optimize_for_size) |
| cost = 1; |
| |
| 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->count.to_frequency (cfun), |
| optimize_bb_for_size_p (bb)); |
| } |
| |
| |
| /* Return the cost of executing a copy instruction on edge E. */ |
| |
| static inline int |
| coalesce_cost_edge (edge e) |
| { |
| int mult = 1; |
| |
| /* Inserting copy on critical edge costs more than inserting it elsewhere. */ |
| if (EDGE_CRITICAL_P (e)) |
| mult = 2; |
| if (e->flags & EDGE_ABNORMAL) |
| return MUST_COALESCE_COST; |
| if (e->flags & EDGE_EH) |
| { |
| edge e2; |
| edge_iterator ei; |
| FOR_EACH_EDGE (e2, ei, e->dest->preds) |
| if (e2 != e) |
| { |
| /* Putting code on EH edge that leads to BB |
| with multiple predecestors imply splitting of |
| edge too. */ |
| if (mult < 2) |
| mult = 2; |
| /* If there are multiple EH predecestors, we |
| also copy EH regions and produce separate |
| landing pad. This is expensive. */ |
| if (e2->flags & EDGE_EH) |
| { |
| mult = 5; |
| break; |
| } |
| } |
| } |
| |
| return coalesce_cost (EDGE_FREQUENCY (e), |
| optimize_edge_for_size_p (e)) * mult; |
| } |
| |
| |
| /* 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 *cl, int *p1, int *p2) |
| { |
| cost_one_pair *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; |
| |
| 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 *cl, int *p1, int *p2) |
| { |
| coalesce_pair *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; |
| |
| return ret; |
| } |
| |
| |
| /* Create a new empty coalesce list object and return it. */ |
| |
| static inline coalesce_list * |
| create_coalesce_list (void) |
| { |
| coalesce_list *list; |
| unsigned size = num_ssa_names * 3; |
| |
| if (size < 40) |
| size = 40; |
| |
| list = (coalesce_list *) xmalloc (sizeof (struct coalesce_list)); |
| list->list = new coalesce_table_type (size); |
| list->sorted = NULL; |
| list->num_sorted = 0; |
| list->cost_one_list = NULL; |
| gcc_obstack_init (&list->ob); |
| return list; |
| } |
| |
| |
| /* Delete coalesce list CL. */ |
| |
| static inline void |
| delete_coalesce_list (coalesce_list *cl) |
| { |
| gcc_assert (cl->cost_one_list == NULL); |
| delete cl->list; |
| cl->list = NULL; |
| free (cl->sorted); |
| gcc_assert (cl->num_sorted == 0); |
| obstack_free (&cl->ob, NULL); |
| free (cl); |
| } |
| |
| /* Return the number of unique coalesce pairs in CL. */ |
| |
| static inline int |
| num_coalesce_pairs (coalesce_list *cl) |
| { |
| return cl->list->elements (); |
| } |
| |
| /* 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 * |
| find_coalesce_pair (coalesce_list *cl, int p1, int p2, bool create) |
| { |
| struct coalesce_pair p; |
| coalesce_pair **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_hasher::hash (&p); |
| slot = cl->list->find_slot_with_hash (&p, hash, create ? INSERT : NO_INSERT); |
| if (!slot) |
| return NULL; |
| |
| if (!*slot) |
| { |
| struct coalesce_pair * pair = XOBNEW (&cl->ob, struct coalesce_pair); |
| gcc_assert (cl->sorted == NULL); |
| pair->first_element = p.first_element; |
| pair->second_element = p.second_element; |
| pair->cost = 0; |
| pair->index = num_coalesce_pairs (cl); |
| pair->conflict_count = 0; |
| *slot = pair; |
| } |
| |
| return (struct coalesce_pair *) *slot; |
| } |
| |
| static inline void |
| add_cost_one_coalesce (coalesce_list *cl, int p1, int p2) |
| { |
| cost_one_pair *pair; |
| |
| pair = XOBNEW (&cl->ob, cost_one_pair); |
| 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 *cl, int p1, int p2, int value) |
| { |
| coalesce_pair *node; |
| |
| gcc_assert (cl->sorted == NULL); |
| if (p1 == p2) |
| return; |
| |
| node = find_coalesce_pair (cl, p1, p2, true); |
| |
| /* Once the value is at least MUST_COALESCE_COST - 1, leave it that way. */ |
| if (node->cost < MUST_COALESCE_COST - 1) |
| { |
| if (value < MUST_COALESCE_COST - 1) |
| node->cost += value; |
| else |
| node->cost = value; |
| } |
| } |
| |
| /* Compute and record how many unique conflicts would exist for the |
| representative partition for each coalesce pair in CL. |
| |
| CONFLICTS is the conflict graph and MAP is the current partition view. */ |
| |
| static void |
| initialize_conflict_count (coalesce_pair *p, |
| ssa_conflicts *conflicts, |
| var_map map) |
| { |
| int p1 = var_to_partition (map, ssa_name (p->first_element)); |
| int p2 = var_to_partition (map, ssa_name (p->second_element)); |
| |
| /* 4 cases. If both P1 and P2 have conflicts, then build their |
| union and count the members. Else handle the degenerate cases |
| in the obvious ways. */ |
| if (conflicts->conflicts[p1] && conflicts->conflicts[p2]) |
| p->conflict_count = bitmap_count_unique_bits (conflicts->conflicts[p1], |
| conflicts->conflicts[p2]); |
| else if (conflicts->conflicts[p1]) |
| p->conflict_count = bitmap_count_bits (conflicts->conflicts[p1]); |
| else if (conflicts->conflicts[p2]) |
| p->conflict_count = bitmap_count_bits (conflicts->conflicts[p2]); |
| else |
| p->conflict_count = 0; |
| } |
| |
| |
| /* Comparison function to allow qsort to sort P1 and P2 in Ascending order. */ |
| |
| static int |
| compare_pairs (const void *p1, const void *p2) |
| { |
| coalesce_pair *const *const pp1 = (coalesce_pair *const *) p1; |
| coalesce_pair *const *const pp2 = (coalesce_pair *const *) p2; |
| int result; |
| |
| result = (* pp1)->cost - (* pp2)->cost; |
| /* We use the size of the resulting conflict set as the secondary sort key. |
| Given two equal costing coalesce pairs, we want to prefer the pair that |
| has the smaller conflict set. */ |
| if (result == 0) |
| { |
| if (flag_expensive_optimizations) |
| { |
| /* Lazily initialize the conflict counts as it's fairly expensive |
| to compute. */ |
| if ((*pp2)->conflict_count == 0) |
| initialize_conflict_count (*pp2, conflicts_, map_); |
| if ((*pp1)->conflict_count == 0) |
| initialize_conflict_count (*pp1, conflicts_, map_); |
| |
| result = (*pp2)->conflict_count - (*pp1)->conflict_count; |
| } |
| |
| /* And if everything else is equal, then sort based on which |
| coalesce pair was found first. */ |
| if (result == 0) |
| result = (*pp2)->index - (*pp1)->index; |
| } |
| |
| return result; |
| } |
| |
| /* Iterate over CL using ITER, returning values in PAIR. */ |
| |
| #define FOR_EACH_PARTITION_PAIR(PAIR, ITER, CL) \ |
| FOR_EACH_HASH_TABLE_ELEMENT (*(CL)->list, (PAIR), coalesce_pair_p, (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 *cl, ssa_conflicts *conflicts, var_map map) |
| { |
| unsigned x, num; |
| coalesce_pair *p; |
| coalesce_iterator_type 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 *, 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; |
| |
| /* We don't want to depend on qsort_r, so we have to stuff away |
| additional data into globals so it can be referenced in |
| compare_pairs. */ |
| conflicts_ = conflicts; |
| map_ = map; |
| qsort (cl->sorted, num, sizeof (coalesce_pair *), compare_pairs); |
| conflicts_ = NULL; |
| map_ = NULL; |
| } |
| |
| |
| /* Send debug info for coalesce list CL to file F. */ |
| |
| static void |
| dump_coalesce_list (FILE *f, coalesce_list *cl) |
| { |
| coalesce_pair *node; |
| coalesce_iterator_type 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, %1d), ", node->cost, node->conflict_count); |
| 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, %d) ", node->cost, node->conflict_count); |
| 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"); |
| } |
| } |
| } |
| |
| |
| /* Return an empty new conflict graph for SIZE elements. */ |
| |
| static inline ssa_conflicts * |
| ssa_conflicts_new (unsigned size) |
| { |
| ssa_conflicts *ptr; |
| |
| ptr = XNEW (ssa_conflicts); |
| bitmap_obstack_initialize (&ptr->obstack); |
| ptr->conflicts.create (size); |
| ptr->conflicts.safe_grow_cleared (size, true); |
| return ptr; |
| } |
| |
| |
| /* Free storage for conflict graph PTR. */ |
| |
| static inline void |
| ssa_conflicts_delete (ssa_conflicts *ptr) |
| { |
| bitmap_obstack_release (&ptr->obstack); |
| ptr->conflicts.release (); |
| free (ptr); |
| } |
| |
| |
| /* Test if elements X and Y conflict in graph PTR. */ |
| |
| static inline bool |
| ssa_conflicts_test_p (ssa_conflicts *ptr, unsigned x, unsigned y) |
| { |
| bitmap bx = ptr->conflicts[x]; |
| bitmap by = ptr->conflicts[y]; |
| |
| gcc_checking_assert (x != y); |
| |
| if (bx) |
| /* Avoid the lookup if Y has no conflicts. */ |
| return by ? bitmap_bit_p (bx, 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 *ptr, unsigned x, unsigned y) |
| { |
| bitmap bx = ptr->conflicts[x]; |
| /* If there are no conflicts yet, allocate the bitmap and set bit. */ |
| if (! bx) |
| bx = ptr->conflicts[x] = BITMAP_ALLOC (&ptr->obstack); |
| bitmap_set_bit (bx, y); |
| } |
| |
| |
| /* Add conflicts between X and Y in graph PTR. */ |
| |
| static inline void |
| ssa_conflicts_add (ssa_conflicts *ptr, unsigned x, unsigned y) |
| { |
| gcc_checking_assert (x != y); |
| 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 *ptr, unsigned x, unsigned y) |
| { |
| unsigned z; |
| bitmap_iterator bi; |
| bitmap bx = ptr->conflicts[x]; |
| bitmap by = ptr->conflicts[y]; |
| |
| gcc_checking_assert (x != y); |
| if (! by) |
| 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 don't need to add a |
| conflict. */ |
| EXECUTE_IF_SET_IN_BITMAP (by, 0, z, bi) |
| { |
| bitmap bz = ptr->conflicts[z]; |
| if (bz) |
| { |
| bool was_there = bitmap_clear_bit (bz, y); |
| gcc_checking_assert (was_there); |
| bitmap_set_bit (bz, x); |
| } |
| } |
| |
| if (bx) |
| { |
| /* If X has conflicts, add Y's to X. */ |
| bitmap_ior_into (bx, by); |
| BITMAP_FREE (by); |
| ptr->conflicts[y] = NULL; |
| } |
| else |
| { |
| /* If X has no conflicts, simply use Y's. */ |
| ptr->conflicts[x] = by; |
| ptr->conflicts[y] = NULL; |
| } |
| } |
| |
| |
| /* Dump a conflicts graph. */ |
| |
| static void |
| ssa_conflicts_dump (FILE *file, ssa_conflicts *ptr) |
| { |
| unsigned x; |
| bitmap b; |
| |
| fprintf (file, "\nConflict graph:\n"); |
| |
| FOR_EACH_VEC_ELT (ptr->conflicts, x, b) |
| if (b) |
| { |
| fprintf (file, "%d: ", x); |
| dump_bitmap (file, b); |
| } |
| } |
| |
| |
| /* 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. */ |
| |
| class live_track |
| { |
| public: |
| bitmap_obstack obstack; /* A place to allocate our bitmaps. */ |
| bitmap_head live_base_var; /* Indicates if a basevar is live. */ |
| bitmap_head *live_base_partitions; /* Live partitions for each basevar. */ |
| var_map map; /* Var_map being used for partition mapping. */ |
| }; |
| |
| |
| /* This routine will create a new live track structure based on the partitions |
| in MAP. */ |
| |
| static live_track * |
| new_live_track (var_map map) |
| { |
| live_track *ptr; |
| int lim, x; |
| |
| /* Make sure there is a partition view in place. */ |
| gcc_assert (map->partition_to_base_index != NULL); |
| |
| ptr = XNEW (live_track); |
| ptr->map = map; |
| lim = num_basevars (map); |
| bitmap_obstack_initialize (&ptr->obstack); |
| ptr->live_base_partitions = XNEWVEC (bitmap_head, lim); |
| bitmap_initialize (&ptr->live_base_var, &ptr->obstack); |
| for (x = 0; x < lim; x++) |
| bitmap_initialize (&ptr->live_base_partitions[x], &ptr->obstack); |
| return ptr; |
| } |
| |
| |
| /* This routine will free the memory associated with PTR. */ |
| |
| static void |
| delete_live_track (live_track *ptr) |
| { |
| bitmap_obstack_release (&ptr->obstack); |
| XDELETEVEC (ptr->live_base_partitions); |
| XDELETE (ptr); |
| } |
| |
| |
| /* This function will remove PARTITION from the live list in PTR. */ |
| |
| static inline void |
| live_track_remove_partition (live_track *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 *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_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 *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 *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 *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 *ptr, tree def, ssa_conflicts *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 *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 *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 entries in the |
| conflict graph. Only conflicts between ssa_name partitions with the same |
| base variable are added. */ |
| |
| static ssa_conflicts * |
| build_ssa_conflict_graph (tree_live_info_p liveinfo) |
| { |
| ssa_conflicts *graph; |
| var_map map; |
| basic_block bb; |
| ssa_op_iter iter; |
| live_track *live; |
| basic_block entry; |
| |
| /* If inter-variable coalescing is enabled, we may attempt to |
| coalesce variables from different base variables, including |
| different parameters, so we have to make sure default defs live |
| at the entry block conflict with each other. */ |
| if (flag_tree_coalesce_vars) |
| entry = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); |
| else |
| entry = NULL; |
| |
| map = live_var_map (liveinfo); |
| graph = ssa_conflicts_new (num_var_partitions (map)); |
| |
| live = new_live_track (map); |
| |
| for (unsigned i = 0; liveinfo->map->vec_bbs.iterate (i, &bb); ++i) |
| { |
| /* Start with live on exit temporaries. */ |
| live_track_init (live, live_on_exit (liveinfo, bb)); |
| |
| for (gimple_stmt_iterator gsi = gsi_last_bb (bb); !gsi_end_p (gsi); |
| gsi_prev (&gsi)) |
| { |
| tree var; |
| gimple *stmt = gsi_stmt (gsi); |
| |
| /* 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 (is_gimple_assign (stmt)) |
| { |
| tree lhs = gimple_assign_lhs (stmt); |
| tree rhs1 = gimple_assign_rhs1 (stmt); |
| if (gimple_assign_copy_p (stmt) |
| && TREE_CODE (lhs) == SSA_NAME |
| && TREE_CODE (rhs1) == SSA_NAME) |
| live_track_clear_var (live, rhs1); |
| } |
| else if (is_gimple_debug (stmt)) |
| continue; |
| |
| /* For stmts with more than one SSA_NAME definition pretend all the |
| SSA_NAME outputs but the first one are live at this point, so |
| that conflicts are added in between all those even when they are |
| actually not really live after the asm, because expansion might |
| copy those into pseudos after the asm and if multiple outputs |
| share the same partition, it might overwrite those that should |
| be live. E.g. |
| asm volatile (".." : "=r" (a) : "=r" (b) : "0" (a), "1" (a)); |
| return a; |
| See PR70593. */ |
| bool first = true; |
| FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_DEF) |
| if (first) |
| first = false; |
| else |
| live_track_process_use (live, var); |
| |
| 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 (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| { |
| gphi *phi = gsi.phi (); |
| tree result = PHI_RESULT (phi); |
| if (virtual_operand_p (result)) |
| continue; |
| if (live_track_live_p (live, result)) |
| live_track_process_def (live, result, graph); |
| } |
| |
| /* Pretend there are defs for params' default defs at the start |
| of the (post-)entry block. This will prevent PARM_DECLs from |
| coalescing into the same partition. Although RESULT_DECLs' |
| default defs don't have a useful initial value, we have to |
| prevent them from coalescing with PARM_DECLs' default defs |
| too, otherwise assign_parms would attempt to assign different |
| RTL to the same partition. */ |
| if (bb == entry) |
| { |
| unsigned i; |
| tree var; |
| |
| FOR_EACH_SSA_NAME (i, var, cfun) |
| { |
| if (!SSA_NAME_IS_DEFAULT_DEF (var) |
| || !SSA_NAME_VAR (var) |
| || VAR_P (SSA_NAME_VAR (var))) |
| continue; |
| |
| live_track_process_def (live, var, graph); |
| /* Process a use too, so that it remains live and |
| conflicts with other parms' default defs, even unused |
| ones. */ |
| live_track_process_use (live, var); |
| } |
| } |
| |
| live_track_clear_base_vars (live); |
| } |
| |
| delete_live_track (live); |
| return graph; |
| } |
| |
| /* 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"); |
| } |
| |
| /* If VAR is an SSA_NAME associated with a PARM_DECL or a RESULT_DECL, |
| and the DECL's default def is unused (i.e., it was introduced by |
| create_default_def for out-of-ssa), mark VAR and the default def for |
| coalescing. */ |
| |
| static void |
| coalesce_with_default (tree var, coalesce_list *cl, bitmap used_in_copy) |
| { |
| if (SSA_NAME_IS_DEFAULT_DEF (var) |
| || !SSA_NAME_VAR (var) |
| || VAR_P (SSA_NAME_VAR (var))) |
| return; |
| |
| tree ssa = ssa_default_def (cfun, SSA_NAME_VAR (var)); |
| if (!has_zero_uses (ssa)) |
| return; |
| |
| add_cost_one_coalesce (cl, SSA_NAME_VERSION (ssa), SSA_NAME_VERSION (var)); |
| bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (var)); |
| /* Default defs will have their used_in_copy bits set at the beginning of |
| populate_coalesce_list_for_outofssa. */ |
| } |
| |
| |
| /* Given var_map MAP for a region, this function creates and returns a coalesce |
| list as well as recording related ssa names in USED_IN_COPIES for use later |
| in the out-of-ssa or live range computation process. */ |
| |
| static coalesce_list * |
| create_coalesce_list_for_region (var_map map, bitmap used_in_copy) |
| { |
| gimple_stmt_iterator gsi; |
| basic_block bb; |
| coalesce_list *cl = create_coalesce_list (); |
| gimple *stmt; |
| int v1, v2, cost; |
| |
| for (unsigned j = 0; map->vec_bbs.iterate (j, &bb); ++j) |
| { |
| tree arg; |
| |
| for (gphi_iterator gpi = gsi_start_phis (bb); |
| !gsi_end_p (gpi); |
| gsi_next (&gpi)) |
| { |
| gphi *phi = gpi.phi (); |
| size_t i; |
| int ver; |
| tree res; |
| bool saw_copy = false; |
| |
| res = gimple_phi_result (phi); |
| if (virtual_operand_p (res)) |
| continue; |
| ver = SSA_NAME_VERSION (res); |
| |
| /* Register ssa_names and coalesces between the args and the result |
| of all PHI. */ |
| for (i = 0; i < gimple_phi_num_args (phi); i++) |
| { |
| edge e = gimple_phi_arg_edge (phi, i); |
| arg = PHI_ARG_DEF (phi, i); |
| if (TREE_CODE (arg) != SSA_NAME) |
| continue; |
| |
| if (gimple_can_coalesce_p (arg, res) |
| || (e->flags & EDGE_ABNORMAL)) |
| { |
| 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 && has_single_use (arg)) |
| add_cost_one_coalesce (cl, ver, SSA_NAME_VERSION (arg)); |
| else |
| add_coalesce (cl, ver, SSA_NAME_VERSION (arg), cost); |
| } |
| } |
| } |
| if (saw_copy) |
| bitmap_set_bit (used_in_copy, ver); |
| } |
| |
| for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| stmt = gsi_stmt (gsi); |
| |
| if (is_gimple_debug (stmt)) |
| continue; |
| |
| /* Check for copy coalesces. */ |
| switch (gimple_code (stmt)) |
| { |
| case GIMPLE_ASSIGN: |
| { |
| tree lhs = gimple_assign_lhs (stmt); |
| tree rhs1 = gimple_assign_rhs1 (stmt); |
| if (gimple_assign_ssa_name_copy_p (stmt) |
| && gimple_can_coalesce_p (lhs, rhs1)) |
| { |
| v1 = SSA_NAME_VERSION (lhs); |
| v2 = SSA_NAME_VERSION (rhs1); |
| 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 GIMPLE_RETURN: |
| { |
| tree res = DECL_RESULT (current_function_decl); |
| if (VOID_TYPE_P (TREE_TYPE (res)) |
| || !is_gimple_reg (res)) |
| break; |
| tree rhs1 = gimple_return_retval (as_a <greturn *> (stmt)); |
| if (!rhs1) |
| break; |
| tree lhs = ssa_default_def (cfun, res); |
| gcc_assert (lhs); |
| if (TREE_CODE (rhs1) == SSA_NAME |
| && gimple_can_coalesce_p (lhs, rhs1)) |
| { |
| v1 = SSA_NAME_VERSION (lhs); |
| v2 = SSA_NAME_VERSION (rhs1); |
| 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 GIMPLE_ASM: |
| { |
| gasm *asm_stmt = as_a <gasm *> (stmt); |
| unsigned long noutputs, i; |
| unsigned long ninputs; |
| tree *outputs, link; |
| noutputs = gimple_asm_noutputs (asm_stmt); |
| ninputs = gimple_asm_ninputs (asm_stmt); |
| outputs = (tree *) alloca (noutputs * sizeof (tree)); |
| for (i = 0; i < noutputs; ++i) |
| { |
| link = gimple_asm_output_op (asm_stmt, i); |
| outputs[i] = TREE_VALUE (link); |
| } |
| |
| for (i = 0; i < ninputs; ++i) |
| { |
| const char *constraint; |
| tree input; |
| char *end; |
| unsigned long match; |
| |
| link = gimple_asm_input_op (asm_stmt, i); |
| constraint |
| = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); |
| input = TREE_VALUE (link); |
| |
| if (TREE_CODE (input) != SSA_NAME) |
| 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 (gimple_can_coalesce_p (outputs[match], input)) |
| { |
| cost = coalesce_cost (REG_BR_PROB_BASE, |
| optimize_bb_for_size_p (bb)); |
| add_coalesce (cl, v1, v2, cost); |
| bitmap_set_bit (used_in_copy, v1); |
| bitmap_set_bit (used_in_copy, v2); |
| } |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| return cl; |
| } |
| |
| |
| /* Hashtable support for storing SSA names hashed by their SSA_NAME_VAR. */ |
| |
| struct ssa_name_var_hash : nofree_ptr_hash <tree_node> |
| { |
| static inline hashval_t hash (const tree_node *); |
| static inline int equal (const tree_node *, const tree_node *); |
| }; |
| |
| inline hashval_t |
| ssa_name_var_hash::hash (const_tree n) |
| { |
| return DECL_UID (SSA_NAME_VAR (n)); |
| } |
| |
| inline int |
| ssa_name_var_hash::equal (const tree_node *n1, const tree_node *n2) |
| { |
| return SSA_NAME_VAR (n1) == SSA_NAME_VAR (n2); |
| } |
| |
| |
| /* This function populates coalesce list CL as well as recording related ssa |
| names in USED_IN_COPIES for use later in the out-of-ssa process. */ |
| |
| static void |
| populate_coalesce_list_for_outofssa (coalesce_list *cl, bitmap used_in_copy) |
| { |
| tree var; |
| tree first; |
| int v1, v2, cost; |
| unsigned i; |
| |
| /* Process result decls and live on entry variables for entry into the |
| coalesce list. */ |
| first = NULL_TREE; |
| FOR_EACH_SSA_NAME (i, var, cfun) |
| { |
| if (!virtual_operand_p (var)) |
| { |
| coalesce_with_default (var, cl, used_in_copy); |
| |
| /* Add coalesces between all the result decls. */ |
| if (SSA_NAME_VAR (var) |
| && TREE_CODE (SSA_NAME_VAR (var)) == RESULT_DECL) |
| { |
| bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (var)); |
| if (first == NULL_TREE) |
| first = var; |
| else |
| { |
| gcc_assert (gimple_can_coalesce_p (var, first)); |
| v1 = SSA_NAME_VERSION (first); |
| v2 = SSA_NAME_VERSION (var); |
| cost = coalesce_cost_bb (EXIT_BLOCK_PTR_FOR_FN (cfun)); |
| 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 (SSA_NAME_IS_DEFAULT_DEF (var) |
| && (!has_zero_uses (var) |
| || (SSA_NAME_VAR (var) |
| && !VAR_P (SSA_NAME_VAR (var))))) |
| bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (var)); |
| } |
| } |
| |
| /* If this optimization is disabled, we need to coalesce all the |
| names originating from the same SSA_NAME_VAR so debug info |
| remains undisturbed. */ |
| if (!flag_tree_coalesce_vars) |
| { |
| tree a; |
| hash_table<ssa_name_var_hash> ssa_name_hash (10); |
| |
| FOR_EACH_SSA_NAME (i, a, cfun) |
| { |
| if (SSA_NAME_VAR (a) |
| && !DECL_IGNORED_P (SSA_NAME_VAR (a)) |
| && (!has_zero_uses (a) || !SSA_NAME_IS_DEFAULT_DEF (a) |
| || !VAR_P (SSA_NAME_VAR (a)))) |
| { |
| tree *slot = ssa_name_hash.find_slot (a, INSERT); |
| |
| if (!*slot) |
| *slot = a; |
| else |
| { |
| /* If the variable is a PARM_DECL or a RESULT_DECL, we |
| _require_ that all the names originating from it be |
| coalesced, because there must be a single partition |
| containing all the names so that it can be assigned |
| the canonical RTL location of the DECL safely. |
| If in_lto_p, a function could have been compiled |
| originally with optimizations and only the link |
| performed at -O0, so we can't actually require it. */ |
| const int cost |
| = (TREE_CODE (SSA_NAME_VAR (a)) == VAR_DECL || in_lto_p) |
| ? MUST_COALESCE_COST - 1 : MUST_COALESCE_COST; |
| add_coalesce (cl, SSA_NAME_VERSION (a), |
| SSA_NAME_VERSION (*slot), cost); |
| bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (a)); |
| bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (*slot)); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* Attempt to coalesce ssa versions 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 *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 *graph, coalesce_list *cl, |
| FILE *debug) |
| { |
| int x = 0, y = 0; |
| tree var1, var2; |
| int cost; |
| basic_block bb; |
| edge e; |
| edge_iterator ei; |
| |
| /* First, coalesce all the copies across abnormal edges. These are not placed |
| in the coalesce list because they do not need to be sorted, and simply |
| consume extra memory/compilation time in large programs. */ |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| FOR_EACH_EDGE (e, ei, bb->preds) |
| if (e->flags & EDGE_ABNORMAL) |
| { |
| gphi_iterator gsi; |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| { |
| gphi *phi = gsi.phi (); |
| tree res = PHI_RESULT (phi); |
| if (virtual_operand_p (res)) |
| continue; |
| tree arg = PHI_ARG_DEF (phi, e->dest_idx); |
| if (SSA_NAME_IS_DEFAULT_DEF (arg) |
| && (!SSA_NAME_VAR (arg) |
| || TREE_CODE (SSA_NAME_VAR (arg)) != PARM_DECL)) |
| continue; |
| |
| int v1 = SSA_NAME_VERSION (res); |
| int v2 = SSA_NAME_VERSION (arg); |
| |
| 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 (gimple_can_coalesce_p (var1, var2)); |
| |
| if (debug) |
| fprintf (debug, "Coalesce list: "); |
| attempt_coalesce (map, graph, x, y, debug); |
| } |
| } |
| |
| |
| /* Output partition map MAP with coalescing plan PART to file F. */ |
| |
| void |
| dump_part_var_map (FILE *f, partition part, var_map map) |
| { |
| int t; |
| unsigned x, y; |
| int p; |
| |
| fprintf (f, "\nCoalescible Partition map \n\n"); |
| |
| for (x = 0; x < map->num_partitions; x++) |
| { |
| if (map->view_to_partition != NULL) |
| p = map->view_to_partition[x]; |
| else |
| p = x; |
| |
| if (ssa_name (p) == NULL_TREE |
| || virtual_operand_p (ssa_name (p))) |
| continue; |
| |
| t = 0; |
| for (y = 1; y < num_ssa_names; y++) |
| { |
| tree var = version_to_var (map, y); |
| if (!var) |
| continue; |
| int q = var_to_partition (map, var); |
| p = partition_find (part, q); |
| gcc_assert (map->partition_to_base_index[q] |
| == map->partition_to_base_index[p]); |
| |
| if (p == (int)x) |
| { |
| if (t++ == 0) |
| { |
| fprintf (f, "Partition %d, base %d (", x, |
| map->partition_to_base_index[q]); |
| print_generic_expr (f, partition_to_var (map, q), TDF_SLIM); |
| fprintf (f, " - "); |
| } |
| fprintf (f, "%d ", y); |
| } |
| } |
| if (t != 0) |
| fprintf (f, ")\n"); |
| } |
| fprintf (f, "\n"); |
| } |
| |
| /* Given SSA_NAMEs NAME1 and NAME2, return true if they are candidates for |
| coalescing together, false otherwise. |
| |
| This must stay consistent with compute_samebase_partition_bases and |
| compute_optimized_partition_bases. */ |
| |
| bool |
| gimple_can_coalesce_p (tree name1, tree name2) |
| { |
| /* First check the SSA_NAME's associated DECL. Without |
| optimization, we only want to coalesce if they have the same DECL |
| or both have no associated DECL. */ |
| tree var1 = SSA_NAME_VAR (name1); |
| tree var2 = SSA_NAME_VAR (name2); |
| var1 = (var1 && (!VAR_P (var1) || !DECL_IGNORED_P (var1))) ? var1 : NULL_TREE; |
| var2 = (var2 && (!VAR_P (var2) || !DECL_IGNORED_P (var2))) ? var2 : NULL_TREE; |
| if (var1 != var2 && !flag_tree_coalesce_vars) |
| return false; |
| |
| /* Now check the types. If the types are the same, then we should |
| try to coalesce V1 and V2. */ |
| tree t1 = TREE_TYPE (name1); |
| tree t2 = TREE_TYPE (name2); |
| if (t1 == t2) |
| { |
| check_modes: |
| /* If the base variables are the same, we're good: none of the |
| other tests below could possibly fail. */ |
| var1 = SSA_NAME_VAR (name1); |
| var2 = SSA_NAME_VAR (name2); |
| if (var1 == var2) |
| return true; |
| |
| /* We don't want to coalesce two SSA names if one of the base |
| variables is supposed to be a register while the other is |
| supposed to be on the stack. Anonymous SSA names most often |
| take registers, but when not optimizing, user variables |
| should go on the stack, so coalescing them with the anonymous |
| variable as the partition leader would end up assigning the |
| user variable to a register. Don't do that! */ |
| bool reg1 = use_register_for_decl (name1); |
| bool reg2 = use_register_for_decl (name2); |
| if (reg1 != reg2) |
| return false; |
| |
| /* Check that the promoted modes and unsignedness are the same. |
| We don't want to coalesce if the promoted modes would be |
| different, or if they would sign-extend differently. Only |
| PARM_DECLs and RESULT_DECLs have different promotion rules, |
| so skip the test if both are variables, or both are anonymous |
| SSA_NAMEs. */ |
| int unsigned1, unsigned2; |
| return ((!var1 || VAR_P (var1)) && (!var2 || VAR_P (var2))) |
| || ((promote_ssa_mode (name1, &unsigned1) |
| == promote_ssa_mode (name2, &unsigned2)) |
| && unsigned1 == unsigned2); |
| } |
| |
| /* If alignment requirements are different, we can't coalesce. */ |
| if (MINIMUM_ALIGNMENT (t1, |
| var1 ? DECL_MODE (var1) : TYPE_MODE (t1), |
| var1 ? LOCAL_DECL_ALIGNMENT (var1) : TYPE_ALIGN (t1)) |
| != MINIMUM_ALIGNMENT (t2, |
| var2 ? DECL_MODE (var2) : TYPE_MODE (t2), |
| var2 ? LOCAL_DECL_ALIGNMENT (var2) : TYPE_ALIGN (t2))) |
| return false; |
| |
| /* If the types are not the same, see whether they are compatible. This |
| (for example) allows coalescing when the types are fundamentally the |
| same, but just have different names. */ |
| if (types_compatible_p (t1, t2)) |
| goto check_modes; |
| |
| return false; |
| } |
| |
| /* Fill in MAP's partition_to_base_index, with one index for each |
| partition of SSA names USED_IN_COPIES and related by CL coalesce |
| possibilities. This must match gimple_can_coalesce_p in the |
| optimized case. */ |
| |
| static void |
| compute_optimized_partition_bases (var_map map, bitmap used_in_copies, |
| coalesce_list *cl) |
| { |
| int parts = num_var_partitions (map); |
| partition tentative = partition_new (parts); |
| |
| /* Partition the SSA versions so that, for each coalescible |
| pair, both of its members are in the same partition in |
| TENTATIVE. */ |
| gcc_assert (!cl->sorted); |
| coalesce_pair *node; |
| coalesce_iterator_type ppi; |
| FOR_EACH_PARTITION_PAIR (node, ppi, cl) |
| { |
| tree v1 = ssa_name (node->first_element); |
| int p1 = partition_find (tentative, var_to_partition (map, v1)); |
| tree v2 = ssa_name (node->second_element); |
| int p2 = partition_find (tentative, var_to_partition (map, v2)); |
| |
| if (p1 == p2) |
| continue; |
| |
| partition_union (tentative, p1, p2); |
| } |
| |
| /* We have to deal with cost one pairs too. */ |
| for (cost_one_pair *co = cl->cost_one_list; co; co = co->next) |
| { |
| tree v1 = ssa_name (co->first_element); |
| int p1 = partition_find (tentative, var_to_partition (map, v1)); |
| tree v2 = ssa_name (co->second_element); |
| int p2 = partition_find (tentative, var_to_partition (map, v2)); |
| |
| if (p1 == p2) |
| continue; |
| |
| partition_union (tentative, p1, p2); |
| } |
| |
| /* And also with abnormal edges. */ |
| basic_block bb; |
| edge e; |
| unsigned i; |
| edge_iterator ei; |
| for (i = 0; map->vec_bbs.iterate (i, &bb); ++i) |
| { |
| FOR_EACH_EDGE (e, ei, bb->preds) |
| if (e->flags & EDGE_ABNORMAL) |
| { |
| gphi_iterator gsi; |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| { |
| gphi *phi = gsi.phi (); |
| tree res = PHI_RESULT (phi); |
| if (virtual_operand_p (res)) |
| continue; |
| tree arg = PHI_ARG_DEF (phi, e->dest_idx); |
| if (SSA_NAME_IS_DEFAULT_DEF (arg) |
| && (!SSA_NAME_VAR (arg) |
| || TREE_CODE (SSA_NAME_VAR (arg)) != PARM_DECL)) |
| continue; |
| |
| int p1 = partition_find (tentative, var_to_partition (map, res)); |
| int p2 = partition_find (tentative, var_to_partition (map, arg)); |
| |
| if (p1 == p2) |
| continue; |
| |
| partition_union (tentative, p1, p2); |
| } |
| } |
| } |
| |
| map->partition_to_base_index = XCNEWVEC (int, parts); |
| auto_vec<unsigned int> index_map (parts); |
| if (parts) |
| index_map.quick_grow (parts); |
| |
| const unsigned no_part = -1; |
| unsigned count = parts; |
| while (count) |
| index_map[--count] = no_part; |
| |
| /* Initialize MAP's mapping from partition to base index, using |
| as base indices an enumeration of the TENTATIVE partitions in |
| which each SSA version ended up, so that we compute conflicts |
| between all SSA versions that ended up in the same potential |
| coalesce partition. */ |
| bitmap_iterator bi; |
| EXECUTE_IF_SET_IN_BITMAP (used_in_copies, 0, i, bi) |
| { |
| int pidx = var_to_partition (map, ssa_name (i)); |
| int base = partition_find (tentative, pidx); |
| if (index_map[base] != no_part) |
| continue; |
| index_map[base] = count++; |
| } |
| |
| map->num_basevars = count; |
| |
| EXECUTE_IF_SET_IN_BITMAP (used_in_copies, 0, i, bi) |
| { |
| int pidx = var_to_partition (map, ssa_name (i)); |
| int base = partition_find (tentative, pidx); |
| gcc_assert (index_map[base] < count); |
| map->partition_to_base_index[pidx] = index_map[base]; |
| } |
| |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| dump_part_var_map (dump_file, tentative, map); |
| |
| partition_delete (tentative); |
| } |
| |
| /* Given an initial var_map MAP, coalesce variables and return a partition map |
| with the resulting coalesce. Note that this function is called in either |
| live range computation context or out-of-ssa context, indicated by MAP. */ |
| |
| extern void |
| coalesce_ssa_name (var_map map) |
| { |
| tree_live_info_p liveinfo; |
| ssa_conflicts *graph; |
| coalesce_list *cl; |
| auto_bitmap used_in_copies; |
| |
| bitmap_tree_view (used_in_copies); |
| cl = create_coalesce_list_for_region (map, used_in_copies); |
| if (map->outofssa_p) |
| populate_coalesce_list_for_outofssa (cl, used_in_copies); |
| bitmap_list_view (used_in_copies); |
| |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| dump_var_map (dump_file, map); |
| |
| partition_view_bitmap (map, used_in_copies); |
| |
| compute_optimized_partition_bases (map, used_in_copies, cl); |
| |
| if (num_var_partitions (map) < 1) |
| { |
| delete_coalesce_list (cl); |
| return; |
| } |
| |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| dump_var_map (dump_file, map); |
| |
| liveinfo = calculate_live_ranges (map, false); |
| |
| 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); |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| ssa_conflicts_dump (dump_file, graph); |
| |
| sort_coalesce_list (cl, graph, map); |
| |
| 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. */ |
| |
| 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); |
| } |
| |