mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-24 07:31:32 +00:00
1382 lines
35 KiB
C
1382 lines
35 KiB
C
/* Coalesce SSA_NAMES together for the out-of-ssa pass.
|
|
Copyright (C) 2004-2015 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 "tm.h"
|
|
#include "hash-set.h"
|
|
#include "machmode.h"
|
|
#include "vec.h"
|
|
#include "double-int.h"
|
|
#include "input.h"
|
|
#include "alias.h"
|
|
#include "symtab.h"
|
|
#include "wide-int.h"
|
|
#include "inchash.h"
|
|
#include "tree.h"
|
|
#include "fold-const.h"
|
|
#include "flags.h"
|
|
#include "tree-pretty-print.h"
|
|
#include "bitmap.h"
|
|
#include "dumpfile.h"
|
|
#include "hash-table.h"
|
|
#include "predict.h"
|
|
#include "hard-reg-set.h"
|
|
#include "input.h"
|
|
#include "function.h"
|
|
#include "dominance.h"
|
|
#include "cfg.h"
|
|
#include "basic-block.h"
|
|
#include "tree-ssa-alias.h"
|
|
#include "internal-fn.h"
|
|
#include "gimple-expr.h"
|
|
#include "is-a.h"
|
|
#include "gimple.h"
|
|
#include "gimple-iterator.h"
|
|
#include "gimple-ssa.h"
|
|
#include "tree-phinodes.h"
|
|
#include "ssa-iterators.h"
|
|
#include "stringpool.h"
|
|
#include "tree-ssanames.h"
|
|
#include "tree-ssa-live.h"
|
|
#include "tree-ssa-coalesce.h"
|
|
#include "diagnostic-core.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 const struct coalesce_pair *const_coalesce_pair_p;
|
|
|
|
/* Coalesce pair hashtable helpers. */
|
|
|
|
struct coalesce_pair_hasher : typed_noop_remove <coalesce_pair>
|
|
{
|
|
typedef coalesce_pair value_type;
|
|
typedef coalesce_pair compare_type;
|
|
static inline hashval_t hash (const value_type *);
|
|
static inline bool equal (const value_type *, const compare_type *);
|
|
};
|
|
|
|
/* Hash function for coalesce list. Calculate hash for PAIR. */
|
|
|
|
inline hashval_t
|
|
coalesce_pair_hasher::hash (const value_type *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 value_type *p1, const compare_type *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;
|
|
|
|
|
|
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
|
|
{
|
|
coalesce_table_type *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. */
|
|
|
|
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->frequency, 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_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;
|
|
}
|
|
|
|
|
|
/* 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 = new coalesce_table_type (size);
|
|
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);
|
|
delete cl->list;
|
|
cl->list = NULL;
|
|
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;
|
|
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 = XNEW (struct coalesce_pair);
|
|
gcc_assert (cl->sorted == NULL);
|
|
pair->first_element = p.first_element;
|
|
pair->second_element = p.second_element;
|
|
pair->cost = 0;
|
|
*slot = pair;
|
|
}
|
|
|
|
return (struct coalesce_pair *) *slot;
|
|
}
|
|
|
|
static inline void
|
|
add_cost_one_coalesce (coalesce_list_p cl, int p1, int p2)
|
|
{
|
|
cost_one_pair_p pair;
|
|
|
|
pair = XNEW (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 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;
|
|
}
|
|
}
|
|
|
|
|
|
/* Comparison function to allow qsort to sort P1 and P2 in Ascending order. */
|
|
|
|
static int
|
|
compare_pairs (const void *p1, const void *p2)
|
|
{
|
|
const_coalesce_pair_p const *const pp1 = (const_coalesce_pair_p const *) p1;
|
|
const_coalesce_pair_p const *const pp2 = (const_coalesce_pair_p const *) p2;
|
|
int result;
|
|
|
|
result = (* pp1)->cost - (* pp2)->cost;
|
|
/* Since qsort does not guarantee stability we use the elements
|
|
as a secondary key. This provides us with independence from
|
|
the host's implementation of the sorting algorithm. */
|
|
if (result == 0)
|
|
{
|
|
result = (* pp2)->first_element - (* pp1)->first_element;
|
|
if (result == 0)
|
|
result = (* pp2)->second_element - (* pp1)->second_element;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Return the number of unique coalesce pairs in CL. */
|
|
|
|
static inline int
|
|
num_coalesce_pairs (coalesce_list_p cl)
|
|
{
|
|
return cl->list->elements ();
|
|
}
|
|
|
|
|
|
/* 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_p cl)
|
|
{
|
|
unsigned x, num;
|
|
coalesce_pair_p 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_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_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), ", 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 is used for conflicts rather than just upper triangular form.
|
|
this make sit much simpler and faster to perform conflict merges. */
|
|
|
|
typedef struct ssa_conflicts_d
|
|
{
|
|
bitmap_obstack obstack; /* A place to allocate our bitmaps. */
|
|
vec<bitmap> conflicts;
|
|
} * ssa_conflicts_p;
|
|
|
|
/* Return an 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);
|
|
bitmap_obstack_initialize (&ptr->obstack);
|
|
ptr->conflicts.create (size);
|
|
ptr->conflicts.safe_grow_cleared (size);
|
|
return ptr;
|
|
}
|
|
|
|
|
|
/* Free storage for conflict graph PTR. */
|
|
|
|
static inline void
|
|
ssa_conflicts_delete (ssa_conflicts_p 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_p 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_p 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_p 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_p 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)
|
|
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_p 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. */
|
|
|
|
typedef struct live_track_d
|
|
{
|
|
bitmap_obstack obstack; /* A place to allocate our bitmaps. */
|
|
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);
|
|
bitmap_obstack_initialize (&ptr->obstack);
|
|
ptr->live_base_partitions = (bitmap *) xmalloc (sizeof (bitmap *) * lim);
|
|
ptr->live_base_var = BITMAP_ALLOC (&ptr->obstack);
|
|
for (x = 0; x < lim; x++)
|
|
ptr->live_base_partitions[x] = BITMAP_ALLOC (&ptr->obstack);
|
|
return ptr;
|
|
}
|
|
|
|
|
|
/* This routine will free the memory associated with PTR. */
|
|
|
|
static void
|
|
delete_live_track (live_track_p ptr)
|
|
{
|
|
bitmap_obstack_release (&ptr->obstack);
|
|
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_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 entries in the
|
|
conflict graph. Only conflicts between ssa_name partitions with the same
|
|
base variable are 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_FN (bb, cfun)
|
|
{
|
|
/* 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_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 (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);
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
gimple_stmt_iterator gsi;
|
|
basic_block bb;
|
|
tree var;
|
|
gimple stmt;
|
|
tree first;
|
|
var_map map;
|
|
ssa_op_iter iter;
|
|
int v1, v2, cost;
|
|
unsigned i;
|
|
|
|
map = init_var_map (num_ssa_names);
|
|
|
|
FOR_EACH_BB_FN (bb, cfun)
|
|
{
|
|
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);
|
|
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 < 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;
|
|
|
|
register_ssa_partition (map, arg);
|
|
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;
|
|
|
|
/* 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 (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_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 = ssa_name (i);
|
|
if (var != NULL_TREE && !virtual_operand_p (var))
|
|
{
|
|
/* Add coalesces between all the result decls. */
|
|
if (SSA_NAME_VAR (var)
|
|
&& TREE_CODE (SSA_NAME_VAR (var)) == RESULT_DECL)
|
|
{
|
|
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);
|
|
bitmap_set_bit (used_in_copy, v1);
|
|
bitmap_set_bit (used_in_copy, v2);
|
|
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))
|
|
bitmap_set_bit (used_in_copy, SSA_NAME_VERSION (var));
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
|
|
/* 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_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;
|
|
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 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;
|
|
|
|
tree res = PHI_RESULT (phi);
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/* Hashtable support for storing SSA names hashed by their SSA_NAME_VAR. */
|
|
|
|
struct ssa_name_var_hash : typed_noop_remove <tree_node>
|
|
{
|
|
typedef union tree_node value_type;
|
|
typedef union tree_node compare_type;
|
|
static inline hashval_t hash (const value_type *);
|
|
static inline int equal (const value_type *, const compare_type *);
|
|
};
|
|
|
|
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 value_type *n1, const compare_type *n2)
|
|
{
|
|
return SSA_NAME_VAR (n1) == SSA_NAME_VAR (n2);
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
tree_live_info_p liveinfo;
|
|
ssa_conflicts_p graph;
|
|
coalesce_list_p cl;
|
|
bitmap used_in_copies = BITMAP_ALLOC (NULL);
|
|
var_map map;
|
|
unsigned int i;
|
|
|
|
cl = create_coalesce_list ();
|
|
map = create_outofssa_var_map (cl, used_in_copies);
|
|
|
|
/* If optimization is disabled, we need to coalesce all the names originating
|
|
from the same SSA_NAME_VAR so debug info remains undisturbed. */
|
|
if (!optimize)
|
|
{
|
|
hash_table<ssa_name_var_hash> ssa_name_hash (10);
|
|
|
|
for (i = 1; i < num_ssa_names; i++)
|
|
{
|
|
tree a = ssa_name (i);
|
|
|
|
if (a
|
|
&& SSA_NAME_VAR (a)
|
|
&& !DECL_IGNORED_P (SSA_NAME_VAR (a))
|
|
&& (!has_zero_uses (a) || !SSA_NAME_IS_DEFAULT_DEF (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_copies, SSA_NAME_VERSION (a));
|
|
bitmap_set_bit (used_in_copies, SSA_NAME_VERSION (*slot));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
dump_var_map (dump_file, map);
|
|
|
|
/* 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, 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);
|
|
|
|
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);
|
|
|
|
return map;
|
|
}
|