2687 lines
83 KiB
C
2687 lines
83 KiB
C
/* Graph coloring register allocator
|
|
Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
|
Contributed by Michael Matz <matz@suse.de>
|
|
and Daniel Berlin <dan@cgsoftware.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 2, or (at your option) any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with GCC; see the file COPYING. If not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "rtl.h"
|
|
#include "tm_p.h"
|
|
#include "function.h"
|
|
#include "regs.h"
|
|
#include "hard-reg-set.h"
|
|
#include "basic-block.h"
|
|
#include "df.h"
|
|
#include "output.h"
|
|
#include "ra.h"
|
|
|
|
/* This file is part of the graph coloring register allocator.
|
|
It contains the graph colorizer. Given an interference graph
|
|
as set up in ra-build.c the toplevel function in this file
|
|
(ra_colorize_graph) colorizes the graph, leaving a list
|
|
of colored, coalesced and spilled nodes.
|
|
|
|
The algorithm used is a merge of George & Appels iterative coalescing
|
|
and optimistic coalescing, switchable at runtime. The current default
|
|
is "optimistic coalescing +", which is based on the normal Briggs/Cooper
|
|
framework. We can also use biased coloring. Most of the structure
|
|
here follows the different papers.
|
|
|
|
Additionally there is a custom step to locally improve the overall
|
|
spill cost of the colored graph (recolor_spills). */
|
|
|
|
static void push_list (struct dlist *, struct dlist **);
|
|
static void push_list_end (struct dlist *, struct dlist **);
|
|
static void free_dlist (struct dlist **);
|
|
static void put_web_at_end (struct web *, enum node_type);
|
|
static void put_move (struct move *, enum move_type);
|
|
static void build_worklists (struct df *);
|
|
static void enable_move (struct web *);
|
|
static void decrement_degree (struct web *, int);
|
|
static void simplify (void);
|
|
static void remove_move_1 (struct web *, struct move *);
|
|
static void remove_move (struct web *, struct move *);
|
|
static void add_worklist (struct web *);
|
|
static int ok (struct web *, struct web *);
|
|
static int conservative (struct web *, struct web *);
|
|
static inline unsigned int simplify_p (enum node_type);
|
|
static void combine (struct web *, struct web *);
|
|
static void coalesce (void);
|
|
static void freeze_moves (struct web *);
|
|
static void freeze (void);
|
|
static void select_spill (void);
|
|
static int color_usable_p (int, HARD_REG_SET, HARD_REG_SET,
|
|
enum machine_mode);
|
|
int get_free_reg (HARD_REG_SET, HARD_REG_SET, enum machine_mode);
|
|
static int get_biased_reg (HARD_REG_SET, HARD_REG_SET, HARD_REG_SET,
|
|
HARD_REG_SET, enum machine_mode);
|
|
static int count_long_blocks (HARD_REG_SET, int);
|
|
static char * hardregset_to_string (HARD_REG_SET);
|
|
static void calculate_dont_begin (struct web *, HARD_REG_SET *);
|
|
static void colorize_one_web (struct web *, int);
|
|
static void assign_colors (void);
|
|
static void try_recolor_web (struct web *);
|
|
static void insert_coalesced_conflicts (void);
|
|
static int comp_webs_maxcost (const void *, const void *);
|
|
static void recolor_spills (void);
|
|
static void check_colors (void);
|
|
static void restore_conflicts_from_coalesce (struct web *);
|
|
static void break_coalesced_spills (void);
|
|
static void unalias_web (struct web *);
|
|
static void break_aliases_to_web (struct web *);
|
|
static void break_precolored_alias (struct web *);
|
|
static void init_web_pairs (void);
|
|
static void add_web_pair_cost (struct web *, struct web *,
|
|
unsigned HOST_WIDE_INT, unsigned int);
|
|
static int comp_web_pairs (const void *, const void *);
|
|
static void sort_and_combine_web_pairs (int);
|
|
static void aggressive_coalesce (void);
|
|
static void extended_coalesce_2 (void);
|
|
static void check_uncoalesced_moves (void);
|
|
|
|
static struct dlist *mv_worklist, *mv_coalesced, *mv_constrained;
|
|
static struct dlist *mv_frozen, *mv_active;
|
|
|
|
/* Push a node onto the front of the list. */
|
|
|
|
static void
|
|
push_list (struct dlist *x, struct dlist **list)
|
|
{
|
|
if (x->next || x->prev)
|
|
abort ();
|
|
x->next = *list;
|
|
if (*list)
|
|
(*list)->prev = x;
|
|
*list = x;
|
|
}
|
|
|
|
static void
|
|
push_list_end (struct dlist *x, struct dlist **list)
|
|
{
|
|
if (x->prev || x->next)
|
|
abort ();
|
|
if (!*list)
|
|
{
|
|
*list = x;
|
|
return;
|
|
}
|
|
while ((*list)->next)
|
|
list = &((*list)->next);
|
|
x->prev = *list;
|
|
(*list)->next = x;
|
|
}
|
|
|
|
/* Remove a node from the list. */
|
|
|
|
void
|
|
remove_list (struct dlist *x, struct dlist **list)
|
|
{
|
|
struct dlist *y = x->prev;
|
|
if (y)
|
|
y->next = x->next;
|
|
else
|
|
*list = x->next;
|
|
y = x->next;
|
|
if (y)
|
|
y->prev = x->prev;
|
|
x->next = x->prev = NULL;
|
|
}
|
|
|
|
/* Pop the front of the list. */
|
|
|
|
struct dlist *
|
|
pop_list (struct dlist **list)
|
|
{
|
|
struct dlist *r = *list;
|
|
if (r)
|
|
remove_list (r, list);
|
|
return r;
|
|
}
|
|
|
|
/* Free the given double linked list. */
|
|
|
|
static void
|
|
free_dlist (struct dlist **list)
|
|
{
|
|
*list = NULL;
|
|
}
|
|
|
|
/* The web WEB should get the given new TYPE. Put it onto the
|
|
appropriate list.
|
|
Inline, because it's called with constant TYPE every time. */
|
|
|
|
inline void
|
|
put_web (struct web *web, enum node_type type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case INITIAL:
|
|
case FREE:
|
|
case FREEZE:
|
|
case SPILL:
|
|
case SPILLED:
|
|
case COALESCED:
|
|
case COLORED:
|
|
case SELECT:
|
|
push_list (web->dlink, &WEBS(type));
|
|
break;
|
|
case PRECOLORED:
|
|
push_list (web->dlink, &WEBS(INITIAL));
|
|
break;
|
|
case SIMPLIFY:
|
|
if (web->spill_temp)
|
|
push_list (web->dlink, &WEBS(type = SIMPLIFY_SPILL));
|
|
else if (web->add_hardregs)
|
|
push_list (web->dlink, &WEBS(type = SIMPLIFY_FAT));
|
|
else
|
|
push_list (web->dlink, &WEBS(SIMPLIFY));
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
web->type = type;
|
|
}
|
|
|
|
/* After we are done with the whole pass of coloring/spilling,
|
|
we reset the lists of webs, in preparation of the next pass.
|
|
The spilled webs become free, colored webs go to the initial list,
|
|
coalesced webs become free or initial, according to what type of web
|
|
they are coalesced to. */
|
|
|
|
void
|
|
reset_lists (void)
|
|
{
|
|
struct dlist *d;
|
|
unsigned int i;
|
|
if (WEBS(SIMPLIFY) || WEBS(SIMPLIFY_SPILL) || WEBS(SIMPLIFY_FAT)
|
|
|| WEBS(FREEZE) || WEBS(SPILL) || WEBS(SELECT))
|
|
abort ();
|
|
|
|
while ((d = pop_list (&WEBS(COALESCED))) != NULL)
|
|
{
|
|
struct web *web = DLIST_WEB (d);
|
|
struct web *aweb = alias (web);
|
|
/* Note, how alias() becomes invalid through the two put_web()'s
|
|
below. It might set the type of a web to FREE (from COALESCED),
|
|
which itself is a target of aliasing (i.e. in the middle of
|
|
an alias chain). We can handle this by checking also for
|
|
type == FREE. Note nevertheless, that alias() is invalid
|
|
henceforth. */
|
|
if (aweb->type == SPILLED || aweb->type == FREE)
|
|
put_web (web, FREE);
|
|
else
|
|
put_web (web, INITIAL);
|
|
}
|
|
while ((d = pop_list (&WEBS(SPILLED))) != NULL)
|
|
put_web (DLIST_WEB (d), FREE);
|
|
while ((d = pop_list (&WEBS(COLORED))) != NULL)
|
|
put_web (DLIST_WEB (d), INITIAL);
|
|
|
|
/* All free webs have no conflicts anymore. */
|
|
for (d = WEBS(FREE); d; d = d->next)
|
|
{
|
|
struct web *web = DLIST_WEB (d);
|
|
BITMAP_XFREE (web->useless_conflicts);
|
|
web->useless_conflicts = NULL;
|
|
}
|
|
|
|
/* Sanity check, that we only have free, initial or precolored webs. */
|
|
for (i = 0; i < num_webs; i++)
|
|
{
|
|
struct web *web = ID2WEB (i);
|
|
if (web->type != INITIAL && web->type != FREE && web->type != PRECOLORED)
|
|
abort ();
|
|
}
|
|
free_dlist (&mv_worklist);
|
|
free_dlist (&mv_coalesced);
|
|
free_dlist (&mv_constrained);
|
|
free_dlist (&mv_frozen);
|
|
free_dlist (&mv_active);
|
|
}
|
|
|
|
/* Similar to put_web(), but add the web to the end of the appropriate
|
|
list. Additionally TYPE may not be SIMPLIFY. */
|
|
|
|
static void
|
|
put_web_at_end (struct web *web, enum node_type type)
|
|
{
|
|
if (type == PRECOLORED)
|
|
type = INITIAL;
|
|
else if (type == SIMPLIFY)
|
|
abort ();
|
|
push_list_end (web->dlink, &WEBS(type));
|
|
web->type = type;
|
|
}
|
|
|
|
/* Unlink WEB from the list it's currently on (which corresponds to
|
|
its current type). */
|
|
|
|
void
|
|
remove_web_from_list (struct web *web)
|
|
{
|
|
if (web->type == PRECOLORED)
|
|
remove_list (web->dlink, &WEBS(INITIAL));
|
|
else
|
|
remove_list (web->dlink, &WEBS(web->type));
|
|
}
|
|
|
|
/* Give MOVE the TYPE, and link it into the correct list. */
|
|
|
|
static inline void
|
|
put_move (struct move *move, enum move_type type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case WORKLIST:
|
|
push_list (move->dlink, &mv_worklist);
|
|
break;
|
|
case MV_COALESCED:
|
|
push_list (move->dlink, &mv_coalesced);
|
|
break;
|
|
case CONSTRAINED:
|
|
push_list (move->dlink, &mv_constrained);
|
|
break;
|
|
case FROZEN:
|
|
push_list (move->dlink, &mv_frozen);
|
|
break;
|
|
case ACTIVE:
|
|
push_list (move->dlink, &mv_active);
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
move->type = type;
|
|
}
|
|
|
|
/* Build the worklists we are going to process. */
|
|
|
|
static void
|
|
build_worklists (struct df *df ATTRIBUTE_UNUSED)
|
|
{
|
|
struct dlist *d, *d_next;
|
|
struct move_list *ml;
|
|
|
|
/* If we are not the first pass, put all stackwebs (which are still
|
|
backed by a new pseudo, but conceptually can stand for a stackslot,
|
|
i.e. it doesn't really matter if they get a color or not), on
|
|
the SELECT stack first, those with lowest cost first. This way
|
|
they will be colored last, so do not constrain the coloring of the
|
|
normal webs. But still those with the highest count are colored
|
|
before, i.e. get a color more probable. The use of stackregs is
|
|
a pure optimization, and all would work, if we used real stackslots
|
|
from the begin. */
|
|
if (ra_pass > 1)
|
|
{
|
|
unsigned int i, num, max_num;
|
|
struct web **order2web;
|
|
max_num = num_webs - num_subwebs;
|
|
order2web = xmalloc (max_num * sizeof (order2web[0]));
|
|
for (i = 0, num = 0; i < max_num; i++)
|
|
if (id2web[i]->regno >= max_normal_pseudo)
|
|
order2web[num++] = id2web[i];
|
|
if (num)
|
|
{
|
|
qsort (order2web, num, sizeof (order2web[0]), comp_webs_maxcost);
|
|
for (i = num - 1;; i--)
|
|
{
|
|
struct web *web = order2web[i];
|
|
struct conflict_link *wl;
|
|
remove_list (web->dlink, &WEBS(INITIAL));
|
|
put_web (web, SELECT);
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *pweb = wl->t;
|
|
pweb->num_conflicts -= 1 + web->add_hardregs;
|
|
}
|
|
if (i == 0)
|
|
break;
|
|
}
|
|
}
|
|
free (order2web);
|
|
}
|
|
|
|
/* For all remaining initial webs, classify them. */
|
|
for (d = WEBS(INITIAL); d; d = d_next)
|
|
{
|
|
struct web *web = DLIST_WEB (d);
|
|
d_next = d->next;
|
|
if (web->type == PRECOLORED)
|
|
continue;
|
|
|
|
remove_list (d, &WEBS(INITIAL));
|
|
if (web->num_conflicts >= NUM_REGS (web))
|
|
put_web (web, SPILL);
|
|
else if (web->moves)
|
|
put_web (web, FREEZE);
|
|
else
|
|
put_web (web, SIMPLIFY);
|
|
}
|
|
|
|
/* And put all moves on the worklist for iterated coalescing.
|
|
Note, that if iterated coalescing is off, then wl_moves doesn't
|
|
contain any moves. */
|
|
for (ml = wl_moves; ml; ml = ml->next)
|
|
if (ml->move)
|
|
{
|
|
struct move *m = ml->move;
|
|
d = ra_calloc (sizeof (struct dlist));
|
|
DLIST_MOVE (d) = m;
|
|
m->dlink = d;
|
|
put_move (m, WORKLIST);
|
|
}
|
|
}
|
|
|
|
/* Enable the active moves, in which WEB takes part, to be processed. */
|
|
|
|
static void
|
|
enable_move (struct web *web)
|
|
{
|
|
struct move_list *ml;
|
|
for (ml = web->moves; ml; ml = ml->next)
|
|
if (ml->move->type == ACTIVE)
|
|
{
|
|
remove_list (ml->move->dlink, &mv_active);
|
|
put_move (ml->move, WORKLIST);
|
|
}
|
|
}
|
|
|
|
/* Decrement the degree of node WEB by the amount DEC.
|
|
Possibly change the type of WEB, if the number of conflicts is
|
|
now smaller than its freedom. */
|
|
|
|
static void
|
|
decrement_degree (struct web *web, int dec)
|
|
{
|
|
int before = web->num_conflicts;
|
|
web->num_conflicts -= dec;
|
|
if (web->num_conflicts < NUM_REGS (web) && before >= NUM_REGS (web))
|
|
{
|
|
struct conflict_link *a;
|
|
enable_move (web);
|
|
for (a = web->conflict_list; a; a = a->next)
|
|
{
|
|
struct web *aweb = a->t;
|
|
if (aweb->type != SELECT && aweb->type != COALESCED)
|
|
enable_move (aweb);
|
|
}
|
|
if (web->type != FREEZE)
|
|
{
|
|
remove_web_from_list (web);
|
|
if (web->moves)
|
|
put_web (web, FREEZE);
|
|
else
|
|
put_web (web, SIMPLIFY);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Repeatedly simplify the nodes on the simplify worklists. */
|
|
|
|
static void
|
|
simplify (void)
|
|
{
|
|
struct dlist *d;
|
|
struct web *web;
|
|
struct conflict_link *wl;
|
|
while (1)
|
|
{
|
|
/* We try hard to color all the webs resulting from spills first.
|
|
Without that on register starved machines (x86 e.g) with some live
|
|
DImode pseudos, -fPIC, and an asm requiring %edx, it might be, that
|
|
we do rounds over rounds, because the conflict graph says, we can
|
|
simplify those short webs, but later due to irregularities we can't
|
|
color those pseudos. So we have to spill them, which in later rounds
|
|
leads to other spills. */
|
|
d = pop_list (&WEBS(SIMPLIFY));
|
|
if (!d)
|
|
d = pop_list (&WEBS(SIMPLIFY_FAT));
|
|
if (!d)
|
|
d = pop_list (&WEBS(SIMPLIFY_SPILL));
|
|
if (!d)
|
|
break;
|
|
web = DLIST_WEB (d);
|
|
ra_debug_msg (DUMP_PROCESS, " simplifying web %3d, conflicts = %d\n",
|
|
web->id, web->num_conflicts);
|
|
put_web (web, SELECT);
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *pweb = wl->t;
|
|
if (pweb->type != SELECT && pweb->type != COALESCED)
|
|
{
|
|
decrement_degree (pweb, 1 + web->add_hardregs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Helper function to remove a move from the movelist of the web. */
|
|
|
|
static void
|
|
remove_move_1 (struct web *web, struct move *move)
|
|
{
|
|
struct move_list *ml = web->moves;
|
|
if (!ml)
|
|
return;
|
|
if (ml->move == move)
|
|
{
|
|
web->moves = ml->next;
|
|
return;
|
|
}
|
|
for (; ml->next && ml->next->move != move; ml = ml->next) ;
|
|
if (!ml->next)
|
|
return;
|
|
ml->next = ml->next->next;
|
|
}
|
|
|
|
/* Remove a move from the movelist of the web. Actually this is just a
|
|
wrapper around remove_move_1(), making sure, the removed move really is
|
|
not in the list anymore. */
|
|
|
|
static void
|
|
remove_move (struct web *web, struct move *move)
|
|
{
|
|
struct move_list *ml;
|
|
remove_move_1 (web, move);
|
|
for (ml = web->moves; ml; ml = ml->next)
|
|
if (ml->move == move)
|
|
abort ();
|
|
}
|
|
|
|
/* Merge the moves for the two webs into the first web's movelist. */
|
|
|
|
void
|
|
merge_moves (struct web *u, struct web *v)
|
|
{
|
|
regset seen;
|
|
struct move_list *ml, *ml_next;
|
|
|
|
seen = BITMAP_XMALLOC ();
|
|
for (ml = u->moves; ml; ml = ml->next)
|
|
bitmap_set_bit (seen, INSN_UID (ml->move->insn));
|
|
for (ml = v->moves; ml; ml = ml_next)
|
|
{
|
|
ml_next = ml->next;
|
|
if (! bitmap_bit_p (seen, INSN_UID (ml->move->insn)))
|
|
{
|
|
ml->next = u->moves;
|
|
u->moves = ml;
|
|
}
|
|
}
|
|
BITMAP_XFREE (seen);
|
|
v->moves = NULL;
|
|
}
|
|
|
|
/* Add a web to the simplify worklist, from the freeze worklist. */
|
|
|
|
static void
|
|
add_worklist (struct web *web)
|
|
{
|
|
if (web->type != PRECOLORED && !web->moves
|
|
&& web->num_conflicts < NUM_REGS (web))
|
|
{
|
|
remove_list (web->dlink, &WEBS(FREEZE));
|
|
put_web (web, SIMPLIFY);
|
|
}
|
|
}
|
|
|
|
/* Precolored node coalescing heuristic. */
|
|
|
|
static int
|
|
ok (struct web *target, struct web *source)
|
|
{
|
|
struct conflict_link *wl;
|
|
int i;
|
|
int color = source->color;
|
|
int size;
|
|
|
|
/* Normally one would think, the next test wouldn't be needed.
|
|
We try to coalesce S and T, and S has already a color, and we checked
|
|
when processing the insns, that both have the same mode. So naively
|
|
we could conclude, that of course that mode was valid for this color.
|
|
Hah. But there is sparc. Before reload there are copy insns
|
|
(e.g. the ones copying arguments to locals) which happily refer to
|
|
colors in invalid modes. We can't coalesce those things. */
|
|
if (! HARD_REGNO_MODE_OK (source->color, GET_MODE (target->orig_x)))
|
|
return 0;
|
|
|
|
/* Sanity for funny modes. */
|
|
size = HARD_REGNO_NREGS (color, GET_MODE (target->orig_x));
|
|
if (!size)
|
|
return 0;
|
|
|
|
/* We can't coalesce target with a precolored register which isn't in
|
|
usable_regs. */
|
|
for (i = size; i--;)
|
|
if (TEST_HARD_REG_BIT (never_use_colors, color + i)
|
|
|| !TEST_HARD_REG_BIT (target->usable_regs, color + i)
|
|
/* Before usually calling ok() at all, we already test, if the
|
|
candidates conflict in sup_igraph. But when wide webs are
|
|
coalesced to hardregs, we only test the hardweb coalesced into.
|
|
This is only the begin color. When actually coalescing both,
|
|
it will also take the following size colors, i.e. their webs.
|
|
We nowhere checked if the candidate possibly conflicts with
|
|
one of _those_, which is possible with partial conflicts,
|
|
so we simply do it here (this does one bit-test more than
|
|
necessary, the first color). Note, that if X is precolored
|
|
bit [X*num_webs + Y] can't be set (see add_conflict_edge()). */
|
|
|| TEST_BIT (sup_igraph,
|
|
target->id * num_webs + hardreg2web[color + i]->id))
|
|
return 0;
|
|
|
|
for (wl = target->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *pweb = wl->t;
|
|
if (pweb->type == SELECT || pweb->type == COALESCED)
|
|
continue;
|
|
|
|
/* Coalescing target (T) and source (S) is o.k, if for
|
|
all conflicts C of T it is true, that:
|
|
1) C will be colored, or
|
|
2) C is a hardreg (precolored), or
|
|
3) C already conflicts with S too, or
|
|
4) a web which contains C conflicts already with S.
|
|
XXX: we handle here only the special case of 4), that C is
|
|
a subreg, and the containing thing is the reg itself, i.e.
|
|
we dont handle the situation, were T conflicts with
|
|
(subreg:SI x 1), and S conflicts with (subreg:DI x 0), which
|
|
would be allowed also, as the S-conflict overlaps
|
|
the T-conflict.
|
|
So, we first test the whole web for any of these conditions, and
|
|
continue with the next C, if 1, 2 or 3 is true. */
|
|
if (pweb->num_conflicts < NUM_REGS (pweb)
|
|
|| pweb->type == PRECOLORED
|
|
|| TEST_BIT (igraph, igraph_index (source->id, pweb->id)) )
|
|
continue;
|
|
|
|
/* This is reached, if not one of 1, 2 or 3 was true. In the case C has
|
|
no subwebs, 4 can't be true either, so we can't coalesce S and T. */
|
|
if (wl->sub == NULL)
|
|
return 0;
|
|
else
|
|
{
|
|
/* The main webs do _not_ conflict, only some parts of both. This
|
|
means, that 4 is possibly true, so we need to check this too.
|
|
For this we go through all sub conflicts between T and C, and see if
|
|
the target part of C already conflicts with S. When this is not
|
|
the case we disallow coalescing. */
|
|
struct sub_conflict *sl;
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
{
|
|
if (!TEST_BIT (igraph, igraph_index (source->id, sl->t->id)))
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Non-precolored node coalescing heuristic. */
|
|
|
|
static int
|
|
conservative (struct web *target, struct web *source)
|
|
{
|
|
unsigned int k;
|
|
unsigned int loop;
|
|
regset seen;
|
|
struct conflict_link *wl;
|
|
unsigned int num_regs = NUM_REGS (target); /* XXX */
|
|
|
|
/* k counts the resulting conflict weight, if target and source
|
|
would be merged, and all low-degree neighbors would be
|
|
removed. */
|
|
k = 0 * MAX (target->add_hardregs, source->add_hardregs);
|
|
seen = BITMAP_XMALLOC ();
|
|
for (loop = 0; loop < 2; loop++)
|
|
for (wl = ((loop == 0) ? target : source)->conflict_list;
|
|
wl; wl = wl->next)
|
|
{
|
|
struct web *pweb = wl->t;
|
|
if (pweb->type != SELECT && pweb->type != COALESCED
|
|
&& pweb->num_conflicts >= NUM_REGS (pweb)
|
|
&& ! REGNO_REG_SET_P (seen, pweb->id))
|
|
{
|
|
SET_REGNO_REG_SET (seen, pweb->id);
|
|
k += 1 + pweb->add_hardregs;
|
|
}
|
|
}
|
|
BITMAP_XFREE (seen);
|
|
|
|
if (k >= num_regs)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* If the web is coalesced, return it's alias. Otherwise, return what
|
|
was passed in. */
|
|
|
|
struct web *
|
|
alias (struct web *web)
|
|
{
|
|
while (web->type == COALESCED)
|
|
web = web->alias;
|
|
return web;
|
|
}
|
|
|
|
/* Returns nonzero, if the TYPE belongs to one of those representing
|
|
SIMPLIFY types. */
|
|
|
|
static inline unsigned int
|
|
simplify_p (enum node_type type)
|
|
{
|
|
return type == SIMPLIFY || type == SIMPLIFY_SPILL || type == SIMPLIFY_FAT;
|
|
}
|
|
|
|
/* Actually combine two webs, that can be coalesced. */
|
|
|
|
static void
|
|
combine (struct web *u, struct web *v)
|
|
{
|
|
int i;
|
|
struct conflict_link *wl;
|
|
if (u == v || v->type == COALESCED)
|
|
abort ();
|
|
if ((u->regno >= max_normal_pseudo) != (v->regno >= max_normal_pseudo))
|
|
abort ();
|
|
remove_web_from_list (v);
|
|
put_web (v, COALESCED);
|
|
v->alias = u;
|
|
u->is_coalesced = 1;
|
|
v->is_coalesced = 1;
|
|
u->num_aliased += 1 + v->num_aliased;
|
|
if (flag_ra_merge_spill_costs && u->type != PRECOLORED)
|
|
u->spill_cost += v->spill_cost;
|
|
/*u->spill_cost = MAX (u->spill_cost, v->spill_cost);*/
|
|
merge_moves (u, v);
|
|
/* combine add_hardregs's of U and V. */
|
|
|
|
for (wl = v->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *pweb = wl->t;
|
|
/* We don't strictly need to move conflicts between webs which are
|
|
already coalesced or selected, if we do iterated coalescing, or
|
|
better if we need not to be able to break aliases again.
|
|
I.e. normally we would use the condition
|
|
(pweb->type != SELECT && pweb->type != COALESCED).
|
|
But for now we simply merge all conflicts. It doesn't take that
|
|
much time. */
|
|
if (1)
|
|
{
|
|
struct web *web = u;
|
|
int nregs = 1 + v->add_hardregs;
|
|
if (u->type == PRECOLORED)
|
|
nregs = HARD_REGNO_NREGS (u->color, GET_MODE (v->orig_x));
|
|
|
|
/* For precolored U's we need to make conflicts between V's
|
|
neighbors and as many hardregs from U as V needed if it gets
|
|
color U. For now we approximate this by V->add_hardregs, which
|
|
could be too much in multi-length classes. We should really
|
|
count how many hardregs are needed for V with color U. When U
|
|
isn't precolored this loop breaks out after one iteration. */
|
|
for (i = 0; i < nregs; i++)
|
|
{
|
|
if (u->type == PRECOLORED)
|
|
web = hardreg2web[i + u->color];
|
|
if (wl->sub == NULL)
|
|
record_conflict (web, pweb);
|
|
else
|
|
{
|
|
struct sub_conflict *sl;
|
|
/* So, between V and PWEB there are sub_conflicts. We
|
|
need to relocate those conflicts to be between WEB (==
|
|
U when it wasn't precolored) and PWEB. In the case
|
|
only a part of V conflicted with (part of) PWEB we
|
|
nevertheless make the new conflict between the whole U
|
|
and the (part of) PWEB. Later we might try to find in
|
|
U the correct subpart corresponding (by size and
|
|
offset) to the part of V (sl->s) which was the source
|
|
of the conflict. */
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
{
|
|
/* Beware: sl->s is no subweb of web (== U) but of V.
|
|
We try to search a corresponding subpart of U.
|
|
If we found none we let it conflict with the whole U.
|
|
Note that find_subweb() only looks for mode and
|
|
subreg_byte of the REG rtx but not for the pseudo
|
|
reg number (otherwise it would be guaranteed to
|
|
_not_ find any subpart). */
|
|
struct web *sweb = NULL;
|
|
if (SUBWEB_P (sl->s))
|
|
sweb = find_subweb (web, sl->s->orig_x);
|
|
if (!sweb)
|
|
sweb = web;
|
|
record_conflict (sweb, sl->t);
|
|
}
|
|
}
|
|
if (u->type != PRECOLORED)
|
|
break;
|
|
}
|
|
if (pweb->type != SELECT && pweb->type != COALESCED)
|
|
decrement_degree (pweb, 1 + v->add_hardregs);
|
|
}
|
|
}
|
|
|
|
/* Now merge the usable_regs together. */
|
|
/* XXX That merging might normally make it necessary to
|
|
adjust add_hardregs, which also means to adjust neighbors. This can
|
|
result in making some more webs trivially colorable, (or the opposite,
|
|
if this increases our add_hardregs). Because we intersect the
|
|
usable_regs it should only be possible to decrease add_hardregs. So a
|
|
conservative solution for now is to simply don't change it. */
|
|
u->use_my_regs = 1;
|
|
AND_HARD_REG_SET (u->usable_regs, v->usable_regs);
|
|
u->regclass = reg_class_subunion[u->regclass][v->regclass];
|
|
/* Count number of possible hardregs. This might make U a spillweb,
|
|
but that could also happen, if U and V together had too many
|
|
conflicts. */
|
|
u->num_freedom = hard_regs_count (u->usable_regs);
|
|
u->num_freedom -= u->add_hardregs;
|
|
/* The next would mean an invalid coalesced move (both webs have no
|
|
possible hardreg in common), so abort. */
|
|
if (!u->num_freedom)
|
|
abort();
|
|
|
|
if (u->num_conflicts >= NUM_REGS (u)
|
|
&& (u->type == FREEZE || simplify_p (u->type)))
|
|
{
|
|
remove_web_from_list (u);
|
|
put_web (u, SPILL);
|
|
}
|
|
|
|
/* We want the most relaxed combination of spill_temp state.
|
|
I.e. if any was no spilltemp or a spilltemp2, the result is so too,
|
|
otherwise if any is short, the result is too. It remains, when both
|
|
are normal spilltemps. */
|
|
if (v->spill_temp == 0)
|
|
u->spill_temp = 0;
|
|
else if (v->spill_temp == 2 && u->spill_temp != 0)
|
|
u->spill_temp = 2;
|
|
else if (v->spill_temp == 3 && u->spill_temp == 1)
|
|
u->spill_temp = 3;
|
|
}
|
|
|
|
/* Attempt to coalesce the first thing on the move worklist.
|
|
This is used only for iterated coalescing. */
|
|
|
|
static void
|
|
coalesce (void)
|
|
{
|
|
struct dlist *d = pop_list (&mv_worklist);
|
|
struct move *m = DLIST_MOVE (d);
|
|
struct web *source = alias (m->source_web);
|
|
struct web *target = alias (m->target_web);
|
|
|
|
if (target->type == PRECOLORED)
|
|
{
|
|
struct web *h = source;
|
|
source = target;
|
|
target = h;
|
|
}
|
|
if (source == target)
|
|
{
|
|
remove_move (source, m);
|
|
put_move (m, MV_COALESCED);
|
|
add_worklist (source);
|
|
}
|
|
else if (target->type == PRECOLORED
|
|
|| TEST_BIT (sup_igraph, source->id * num_webs + target->id)
|
|
|| TEST_BIT (sup_igraph, target->id * num_webs + source->id))
|
|
{
|
|
remove_move (source, m);
|
|
remove_move (target, m);
|
|
put_move (m, CONSTRAINED);
|
|
add_worklist (source);
|
|
add_worklist (target);
|
|
}
|
|
else if ((source->type == PRECOLORED && ok (target, source))
|
|
|| (source->type != PRECOLORED
|
|
&& conservative (target, source)))
|
|
{
|
|
remove_move (source, m);
|
|
remove_move (target, m);
|
|
put_move (m, MV_COALESCED);
|
|
combine (source, target);
|
|
add_worklist (source);
|
|
}
|
|
else
|
|
put_move (m, ACTIVE);
|
|
}
|
|
|
|
/* Freeze the moves associated with the web. Used for iterated coalescing. */
|
|
|
|
static void
|
|
freeze_moves (struct web *web)
|
|
{
|
|
struct move_list *ml, *ml_next;
|
|
for (ml = web->moves; ml; ml = ml_next)
|
|
{
|
|
struct move *m = ml->move;
|
|
struct web *src, *dest;
|
|
ml_next = ml->next;
|
|
if (m->type == ACTIVE)
|
|
remove_list (m->dlink, &mv_active);
|
|
else
|
|
remove_list (m->dlink, &mv_worklist);
|
|
put_move (m, FROZEN);
|
|
remove_move (web, m);
|
|
src = alias (m->source_web);
|
|
dest = alias (m->target_web);
|
|
src = (src == web) ? dest : src;
|
|
remove_move (src, m);
|
|
/* XXX GA use the original v, instead of alias(v) */
|
|
if (!src->moves && src->num_conflicts < NUM_REGS (src))
|
|
{
|
|
remove_list (src->dlink, &WEBS(FREEZE));
|
|
put_web (src, SIMPLIFY);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Freeze the first thing on the freeze worklist (only for iterated
|
|
coalescing). */
|
|
|
|
static void
|
|
freeze (void)
|
|
{
|
|
struct dlist *d = pop_list (&WEBS(FREEZE));
|
|
put_web (DLIST_WEB (d), SIMPLIFY);
|
|
freeze_moves (DLIST_WEB (d));
|
|
}
|
|
|
|
/* The current spill heuristic. Returns a number for a WEB.
|
|
Webs with higher numbers are selected later. */
|
|
|
|
static unsigned HOST_WIDE_INT (*spill_heuristic) (struct web *);
|
|
|
|
static unsigned HOST_WIDE_INT default_spill_heuristic (struct web *);
|
|
|
|
/* Our default heuristic is similar to spill_cost / num_conflicts.
|
|
Just scaled for integer arithmetic, and it favors coalesced webs,
|
|
and webs which span more insns with deaths. */
|
|
|
|
static unsigned HOST_WIDE_INT
|
|
default_spill_heuristic (struct web *web)
|
|
{
|
|
unsigned HOST_WIDE_INT ret;
|
|
unsigned int divisor = 1;
|
|
/* Make coalesce targets cheaper to spill, because they will be broken
|
|
up again into smaller parts. */
|
|
if (flag_ra_break_aliases)
|
|
divisor += web->num_aliased;
|
|
divisor += web->num_conflicts;
|
|
ret = ((web->spill_cost << 8) + divisor - 1) / divisor;
|
|
/* It is better to spill webs that span more insns (deaths in our
|
|
case) than other webs with the otherwise same spill_cost. So make
|
|
them a little bit cheaper. Remember that spill_cost is unsigned. */
|
|
if (web->span_deaths < ret)
|
|
ret -= web->span_deaths;
|
|
return ret;
|
|
}
|
|
|
|
/* Select the cheapest spill to be potentially spilled (we don't
|
|
*actually* spill until we need to). */
|
|
|
|
static void
|
|
select_spill (void)
|
|
{
|
|
unsigned HOST_WIDE_INT best = (unsigned HOST_WIDE_INT) -1;
|
|
struct dlist *bestd = NULL;
|
|
unsigned HOST_WIDE_INT best2 = (unsigned HOST_WIDE_INT) -1;
|
|
struct dlist *bestd2 = NULL;
|
|
struct dlist *d;
|
|
for (d = WEBS(SPILL); d; d = d->next)
|
|
{
|
|
struct web *w = DLIST_WEB (d);
|
|
unsigned HOST_WIDE_INT cost = spill_heuristic (w);
|
|
if ((!w->spill_temp) && cost < best)
|
|
{
|
|
best = cost;
|
|
bestd = d;
|
|
}
|
|
/* Specially marked spill temps can be spilled. Also coalesce
|
|
targets can. Eventually they will be broken up later in the
|
|
colorizing process, so if we have nothing better take that. */
|
|
else if ((w->spill_temp == 2 || w->is_coalesced) && cost < best2)
|
|
{
|
|
best2 = cost;
|
|
bestd2 = d;
|
|
}
|
|
}
|
|
if (!bestd)
|
|
{
|
|
bestd = bestd2;
|
|
best = best2;
|
|
}
|
|
if (!bestd)
|
|
abort ();
|
|
|
|
/* Note the potential spill. */
|
|
DLIST_WEB (bestd)->was_spilled = 1;
|
|
remove_list (bestd, &WEBS(SPILL));
|
|
put_web (DLIST_WEB (bestd), SIMPLIFY);
|
|
freeze_moves (DLIST_WEB (bestd));
|
|
ra_debug_msg (DUMP_PROCESS, " potential spill web %3d, conflicts = %d\n",
|
|
DLIST_WEB (bestd)->id, DLIST_WEB (bestd)->num_conflicts);
|
|
}
|
|
|
|
/* Given a set of forbidden colors to begin at, and a set of still
|
|
free colors, and MODE, returns nonzero of color C is still usable. */
|
|
|
|
static int
|
|
color_usable_p (int c, HARD_REG_SET dont_begin_colors,
|
|
HARD_REG_SET free_colors, enum machine_mode mode)
|
|
{
|
|
if (!TEST_HARD_REG_BIT (dont_begin_colors, c)
|
|
&& TEST_HARD_REG_BIT (free_colors, c)
|
|
&& HARD_REGNO_MODE_OK (c, mode))
|
|
{
|
|
int i, size;
|
|
size = HARD_REGNO_NREGS (c, mode);
|
|
for (i = 1; i < size && TEST_HARD_REG_BIT (free_colors, c + i); i++);
|
|
if (i == size)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* I don't want to clutter up the actual code with ifdef's. */
|
|
#ifdef REG_ALLOC_ORDER
|
|
#define INV_REG_ALLOC_ORDER(c) inv_reg_alloc_order[c]
|
|
#else
|
|
#define INV_REG_ALLOC_ORDER(c) c
|
|
#endif
|
|
|
|
/* Searches in FREE_COLORS for a block of hardregs of the right length
|
|
for MODE, which doesn't begin at a hardreg mentioned in DONT_BEGIN_COLORS.
|
|
If it needs more than one hardreg it prefers blocks beginning
|
|
at an even hardreg, and only gives an odd begin reg if no other
|
|
block could be found. */
|
|
|
|
int
|
|
get_free_reg (HARD_REG_SET dont_begin_colors, HARD_REG_SET free_colors,
|
|
enum machine_mode mode)
|
|
{
|
|
int c;
|
|
int last_resort_reg = -1;
|
|
int pref_reg = -1;
|
|
int pref_reg_order = INT_MAX;
|
|
int last_resort_reg_order = INT_MAX;
|
|
|
|
for (c = 0; c < FIRST_PSEUDO_REGISTER; c++)
|
|
if (!TEST_HARD_REG_BIT (dont_begin_colors, c)
|
|
&& TEST_HARD_REG_BIT (free_colors, c)
|
|
&& HARD_REGNO_MODE_OK (c, mode))
|
|
{
|
|
int i, size;
|
|
size = HARD_REGNO_NREGS (c, mode);
|
|
for (i = 1; i < size && TEST_HARD_REG_BIT (free_colors, c + i); i++);
|
|
if (i != size)
|
|
{
|
|
c += i;
|
|
continue;
|
|
}
|
|
if (i == size)
|
|
{
|
|
if (size < 2 || (c & 1) == 0)
|
|
{
|
|
if (INV_REG_ALLOC_ORDER (c) < pref_reg_order)
|
|
{
|
|
pref_reg = c;
|
|
pref_reg_order = INV_REG_ALLOC_ORDER (c);
|
|
}
|
|
}
|
|
else if (INV_REG_ALLOC_ORDER (c) < last_resort_reg_order)
|
|
{
|
|
last_resort_reg = c;
|
|
last_resort_reg_order = INV_REG_ALLOC_ORDER (c);
|
|
}
|
|
}
|
|
else
|
|
c += i;
|
|
}
|
|
return pref_reg >= 0 ? pref_reg : last_resort_reg;
|
|
}
|
|
|
|
/* Similar to get_free_reg(), but first search in colors provided
|
|
by BIAS _and_ PREFER_COLORS, then in BIAS alone, then in PREFER_COLORS
|
|
alone, and only then for any free color. If flag_ra_biased is zero
|
|
only do the last two steps. */
|
|
|
|
static int
|
|
get_biased_reg (HARD_REG_SET dont_begin_colors, HARD_REG_SET bias,
|
|
HARD_REG_SET prefer_colors, HARD_REG_SET free_colors,
|
|
enum machine_mode mode)
|
|
{
|
|
int c = -1;
|
|
HARD_REG_SET s;
|
|
if (flag_ra_biased)
|
|
{
|
|
COPY_HARD_REG_SET (s, dont_begin_colors);
|
|
IOR_COMPL_HARD_REG_SET (s, bias);
|
|
IOR_COMPL_HARD_REG_SET (s, prefer_colors);
|
|
c = get_free_reg (s, free_colors, mode);
|
|
if (c >= 0)
|
|
return c;
|
|
COPY_HARD_REG_SET (s, dont_begin_colors);
|
|
IOR_COMPL_HARD_REG_SET (s, bias);
|
|
c = get_free_reg (s, free_colors, mode);
|
|
if (c >= 0)
|
|
return c;
|
|
}
|
|
COPY_HARD_REG_SET (s, dont_begin_colors);
|
|
IOR_COMPL_HARD_REG_SET (s, prefer_colors);
|
|
c = get_free_reg (s, free_colors, mode);
|
|
if (c >= 0)
|
|
return c;
|
|
c = get_free_reg (dont_begin_colors, free_colors, mode);
|
|
return c;
|
|
}
|
|
|
|
/* Counts the number of non-overlapping bitblocks of length LEN
|
|
in FREE_COLORS. */
|
|
|
|
static int
|
|
count_long_blocks (HARD_REG_SET free_colors, int len)
|
|
{
|
|
int i, j;
|
|
int count = 0;
|
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
|
{
|
|
if (!TEST_HARD_REG_BIT (free_colors, i))
|
|
continue;
|
|
for (j = 1; j < len; j++)
|
|
if (!TEST_HARD_REG_BIT (free_colors, i + j))
|
|
break;
|
|
/* Bits [i .. i+j-1] are free. */
|
|
if (j == len)
|
|
count++;
|
|
i += j - 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* Given a hardreg set S, return a string representing it.
|
|
Either as 0/1 string, or as hex value depending on the implementation
|
|
of hardreg sets. Note that this string is statically allocated. */
|
|
|
|
static char *
|
|
hardregset_to_string (HARD_REG_SET s)
|
|
{
|
|
static char string[/*FIRST_PSEUDO_REGISTER + 30*/1024];
|
|
#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDE_INT
|
|
sprintf (string, HOST_WIDE_INT_PRINT_HEX, s);
|
|
#else
|
|
char *c = string;
|
|
int i,j;
|
|
c += sprintf (c, "{ ");
|
|
for (i = 0;i < HARD_REG_SET_LONGS; i++)
|
|
{
|
|
for (j = 0; j < HOST_BITS_PER_WIDE_INT; j++)
|
|
c += sprintf (c, "%s", ( 1 << j) & s[i] ? "1" : "0");
|
|
c += sprintf (c, "%s", i ? ", " : "");
|
|
}
|
|
c += sprintf (c, " }");
|
|
#endif
|
|
return string;
|
|
}
|
|
|
|
/* For WEB, look at its already colored neighbors, and calculate
|
|
the set of hardregs which is not allowed as color for WEB. Place
|
|
that set int *RESULT. Note that the set of forbidden begin colors
|
|
is not the same as all colors taken up by neighbors. E.g. suppose
|
|
two DImode webs, but only the lo-part from one conflicts with the
|
|
hipart from the other, and suppose the other gets colors 2 and 3
|
|
(it needs two SImode hardregs). Now the first can take also color
|
|
1 or 2, although in those cases there's a partial overlap. Only
|
|
3 can't be used as begin color. */
|
|
|
|
static void
|
|
calculate_dont_begin (struct web *web, HARD_REG_SET *result)
|
|
{
|
|
struct conflict_link *wl;
|
|
HARD_REG_SET dont_begin;
|
|
/* The bits set in dont_begin correspond to the hardregs, at which
|
|
WEB may not begin. This differs from the set of _all_ hardregs which
|
|
are taken by WEB's conflicts in the presence of wide webs, where only
|
|
some parts conflict with others. */
|
|
CLEAR_HARD_REG_SET (dont_begin);
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *w;
|
|
struct web *ptarget = alias (wl->t);
|
|
struct sub_conflict *sl = wl->sub;
|
|
w = sl ? sl->t : wl->t;
|
|
while (w)
|
|
{
|
|
if (ptarget->type == COLORED || ptarget->type == PRECOLORED)
|
|
{
|
|
struct web *source = (sl) ? sl->s : web;
|
|
unsigned int tsize = HARD_REGNO_NREGS (ptarget->color,
|
|
GET_MODE (w->orig_x));
|
|
/* ssize is only a first guess for the size. */
|
|
unsigned int ssize = HARD_REGNO_NREGS (ptarget->color, GET_MODE
|
|
(source->orig_x));
|
|
unsigned int tofs = 0;
|
|
unsigned int sofs = 0;
|
|
/* C1 and C2 can become negative, so unsigned
|
|
would be wrong. */
|
|
int c1, c2;
|
|
|
|
if (SUBWEB_P (w)
|
|
&& GET_MODE_SIZE (GET_MODE (w->orig_x)) >= UNITS_PER_WORD)
|
|
tofs = (SUBREG_BYTE (w->orig_x) / UNITS_PER_WORD);
|
|
if (SUBWEB_P (source)
|
|
&& GET_MODE_SIZE (GET_MODE (source->orig_x))
|
|
>= UNITS_PER_WORD)
|
|
sofs = (SUBREG_BYTE (source->orig_x) / UNITS_PER_WORD);
|
|
c1 = ptarget->color + tofs - sofs - ssize + 1;
|
|
c2 = ptarget->color + tofs + tsize - 1 - sofs;
|
|
if (c2 >= 0)
|
|
{
|
|
if (c1 < 0)
|
|
c1 = 0;
|
|
/* Because ssize was only guessed above, which influenced our
|
|
begin color (c1), we need adjustment, if for that color
|
|
another size would be needed. This is done by moving
|
|
c1 to a place, where the last of sources hardregs does not
|
|
overlap the first of targets colors. */
|
|
while (c1 + sofs
|
|
+ HARD_REGNO_NREGS (c1, GET_MODE (source->orig_x)) - 1
|
|
< ptarget->color + tofs)
|
|
c1++;
|
|
while (c1 > 0 && c1 + sofs
|
|
+ HARD_REGNO_NREGS (c1, GET_MODE (source->orig_x)) - 1
|
|
> ptarget->color + tofs)
|
|
c1--;
|
|
for (; c1 <= c2; c1++)
|
|
SET_HARD_REG_BIT (dont_begin, c1);
|
|
}
|
|
}
|
|
/* The next if() only gets true, if there was no wl->sub at all, in
|
|
which case we are only making one go through this loop with W being
|
|
a whole web. */
|
|
if (!sl)
|
|
break;
|
|
sl = sl->next;
|
|
w = sl ? sl->t : NULL;
|
|
}
|
|
}
|
|
COPY_HARD_REG_SET (*result, dont_begin);
|
|
}
|
|
|
|
/* Try to assign a color to WEB. If HARD if nonzero, we try many
|
|
tricks to get it one color, including respilling already colored
|
|
neighbors.
|
|
|
|
We also trie very hard, to not constrain the uncolored non-spill
|
|
neighbors, which need more hardregs than we. Consider a situation, 2
|
|
hardregs free for us (0 and 1), and one of our neighbors needs 2
|
|
hardregs, and only conflicts with us. There are 3 hardregs at all. Now
|
|
a simple minded method might choose 1 as color for us. Then our neighbor
|
|
has two free colors (0 and 2) as it should, but they are not consecutive,
|
|
so coloring it later would fail. This leads to nasty problems on
|
|
register starved machines, so we try to avoid this. */
|
|
|
|
static void
|
|
colorize_one_web (struct web *web, int hard)
|
|
{
|
|
struct conflict_link *wl;
|
|
HARD_REG_SET colors, dont_begin;
|
|
int c = -1;
|
|
int bestc = -1;
|
|
int neighbor_needs= 0;
|
|
struct web *fats_parent = NULL;
|
|
int num_fat = 0;
|
|
int long_blocks = 0;
|
|
int best_long_blocks = -1;
|
|
HARD_REG_SET fat_colors;
|
|
HARD_REG_SET bias;
|
|
|
|
CLEAR_HARD_REG_SET (fat_colors);
|
|
|
|
if (web->regno >= max_normal_pseudo)
|
|
hard = 0;
|
|
|
|
/* First we want to know the colors at which we can't begin. */
|
|
calculate_dont_begin (web, &dont_begin);
|
|
CLEAR_HARD_REG_SET (bias);
|
|
|
|
/* Now setup the set of colors used by our neighbors neighbors,
|
|
and search the biggest noncolored neighbor. */
|
|
neighbor_needs = web->add_hardregs + 1;
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *w;
|
|
struct web *ptarget = alias (wl->t);
|
|
struct sub_conflict *sl = wl->sub;
|
|
IOR_HARD_REG_SET (bias, ptarget->bias_colors);
|
|
w = sl ? sl->t : wl->t;
|
|
if (ptarget->type != COLORED && ptarget->type != PRECOLORED
|
|
&& !ptarget->was_spilled)
|
|
while (w)
|
|
{
|
|
if (find_web_for_subweb (w)->type != COALESCED
|
|
&& w->add_hardregs >= neighbor_needs)
|
|
{
|
|
neighbor_needs = w->add_hardregs;
|
|
fats_parent = ptarget;
|
|
num_fat++;
|
|
}
|
|
if (!sl)
|
|
break;
|
|
sl = sl->next;
|
|
w = sl ? sl->t : NULL;
|
|
}
|
|
}
|
|
|
|
ra_debug_msg (DUMP_COLORIZE, "colorize web %d [don't begin at %s]", web->id,
|
|
hardregset_to_string (dont_begin));
|
|
|
|
/* If there are some fat neighbors, remember their usable regs,
|
|
and how many blocks are free in it for that neighbor. */
|
|
if (num_fat)
|
|
{
|
|
COPY_HARD_REG_SET (fat_colors, fats_parent->usable_regs);
|
|
long_blocks = count_long_blocks (fat_colors, neighbor_needs + 1);
|
|
}
|
|
|
|
/* We break out, if we found a color which doesn't constrain
|
|
neighbors, or if we can't find any colors. */
|
|
while (1)
|
|
{
|
|
HARD_REG_SET call_clobbered;
|
|
|
|
/* Here we choose a hard-reg for the current web. For non spill
|
|
temporaries we first search in the hardregs for it's preferred
|
|
class, then, if we found nothing appropriate, in those of the
|
|
alternate class. For spill temporaries we only search in
|
|
usable_regs of this web (which is probably larger than that of
|
|
the preferred or alternate class). All searches first try to
|
|
find a non-call-clobbered hard-reg.
|
|
XXX this should be more finegraned... First look into preferred
|
|
non-callclobbered hardregs, then _if_ the web crosses calls, in
|
|
alternate non-cc hardregs, and only _then_ also in preferred cc
|
|
hardregs (and alternate ones). Currently we don't track the number
|
|
of calls crossed for webs. We should. */
|
|
if (web->use_my_regs)
|
|
{
|
|
COPY_HARD_REG_SET (colors, web->usable_regs);
|
|
AND_HARD_REG_SET (colors,
|
|
usable_regs[reg_preferred_class (web->regno)]);
|
|
}
|
|
else
|
|
COPY_HARD_REG_SET (colors,
|
|
usable_regs[reg_preferred_class (web->regno)]);
|
|
#ifdef CANNOT_CHANGE_MODE_CLASS
|
|
if (web->mode_changed)
|
|
AND_COMPL_HARD_REG_SET (colors, invalid_mode_change_regs);
|
|
#endif
|
|
COPY_HARD_REG_SET (call_clobbered, colors);
|
|
AND_HARD_REG_SET (call_clobbered, call_used_reg_set);
|
|
|
|
/* If this web got a color in the last pass, try to give it the
|
|
same color again. This will to much better colorization
|
|
down the line, as we spilled for a certain coloring last time. */
|
|
if (web->old_color)
|
|
{
|
|
c = web->old_color - 1;
|
|
if (!color_usable_p (c, dont_begin, colors,
|
|
PSEUDO_REGNO_MODE (web->regno)))
|
|
c = -1;
|
|
}
|
|
else
|
|
c = -1;
|
|
if (c < 0)
|
|
c = get_biased_reg (dont_begin, bias, web->prefer_colors,
|
|
call_clobbered, PSEUDO_REGNO_MODE (web->regno));
|
|
if (c < 0)
|
|
c = get_biased_reg (dont_begin, bias, web->prefer_colors,
|
|
colors, PSEUDO_REGNO_MODE (web->regno));
|
|
|
|
if (c < 0)
|
|
{
|
|
if (web->use_my_regs)
|
|
IOR_HARD_REG_SET (colors, web->usable_regs);
|
|
else
|
|
IOR_HARD_REG_SET (colors, usable_regs
|
|
[reg_alternate_class (web->regno)]);
|
|
#ifdef CANNOT_CHANGE_MODE_CLASS
|
|
if (web->mode_changed)
|
|
AND_COMPL_HARD_REG_SET (colors, invalid_mode_change_regs);
|
|
#endif
|
|
COPY_HARD_REG_SET (call_clobbered, colors);
|
|
AND_HARD_REG_SET (call_clobbered, call_used_reg_set);
|
|
|
|
c = get_biased_reg (dont_begin, bias, web->prefer_colors,
|
|
call_clobbered, PSEUDO_REGNO_MODE (web->regno));
|
|
if (c < 0)
|
|
c = get_biased_reg (dont_begin, bias, web->prefer_colors,
|
|
colors, PSEUDO_REGNO_MODE (web->regno));
|
|
}
|
|
if (c < 0)
|
|
break;
|
|
if (bestc < 0)
|
|
bestc = c;
|
|
/* If one of the yet uncolored neighbors, which is not a potential
|
|
spill needs a block of hardregs be sure, not to destroy such a block
|
|
by coloring one reg in the middle. */
|
|
if (num_fat)
|
|
{
|
|
int i;
|
|
int new_long;
|
|
HARD_REG_SET colors1;
|
|
COPY_HARD_REG_SET (colors1, fat_colors);
|
|
for (i = 0; i < 1 + web->add_hardregs; i++)
|
|
CLEAR_HARD_REG_BIT (colors1, c + i);
|
|
new_long = count_long_blocks (colors1, neighbor_needs + 1);
|
|
/* If we changed the number of long blocks, and it's now smaller
|
|
than needed, we try to avoid this color. */
|
|
if (long_blocks != new_long && new_long < num_fat)
|
|
{
|
|
if (new_long > best_long_blocks)
|
|
{
|
|
best_long_blocks = new_long;
|
|
bestc = c;
|
|
}
|
|
SET_HARD_REG_BIT (dont_begin, c);
|
|
ra_debug_msg (DUMP_COLORIZE, " avoid %d", c);
|
|
}
|
|
else
|
|
/* We found a color which doesn't destroy a block. */
|
|
break;
|
|
}
|
|
/* If we havee no fat neighbors, the current color won't become
|
|
"better", so we've found it. */
|
|
else
|
|
break;
|
|
}
|
|
ra_debug_msg (DUMP_COLORIZE, " --> got %d", c < 0 ? bestc : c);
|
|
if (bestc >= 0 && c < 0 && !web->was_spilled)
|
|
{
|
|
/* This is a non-potential-spill web, which got a color, which did
|
|
destroy a hardreg block for one of it's neighbors. We color
|
|
this web anyway and hope for the best for the neighbor, if we are
|
|
a spill temp. */
|
|
if (1 || web->spill_temp)
|
|
c = bestc;
|
|
ra_debug_msg (DUMP_COLORIZE, " [constrains neighbors]");
|
|
}
|
|
ra_debug_msg (DUMP_COLORIZE, "\n");
|
|
|
|
if (c < 0)
|
|
{
|
|
/* Guard against a simplified node being spilled. */
|
|
/* Don't abort. This can happen, when e.g. enough registers
|
|
are available in colors, but they are not consecutive. This is a
|
|
very serious issue if this web is a short live one, because
|
|
even if we spill this one here, the situation won't become better
|
|
in the next iteration. It probably will have the same conflicts,
|
|
those will have the same colors, and we would come here again, for
|
|
all parts, in which this one gets split by the spill. This
|
|
can result in endless iteration spilling the same register again and
|
|
again. That's why we try to find a neighbor, which spans more
|
|
instructions that ourself, and got a color, and try to spill _that_.
|
|
|
|
if (DLIST_WEB (d)->was_spilled < 0)
|
|
abort (); */
|
|
if (hard && (!web->was_spilled || web->spill_temp))
|
|
{
|
|
unsigned int loop;
|
|
struct web *try = NULL;
|
|
struct web *candidates[8];
|
|
|
|
ra_debug_msg (DUMP_COLORIZE, " *** %d spilled, although %s ***\n",
|
|
web->id, web->spill_temp ? "spilltemp" : "non-spill");
|
|
/* We make multiple passes over our conflicts, first trying to
|
|
spill those webs, which only got a color by chance, but
|
|
were potential spill ones, and if that isn't enough, in a second
|
|
pass also to spill normal colored webs. If we still didn't find
|
|
a candidate, but we are a spill-temp, we make a third pass
|
|
and include also webs, which were targets for coalescing, and
|
|
spill those. */
|
|
memset (candidates, 0, sizeof candidates);
|
|
#define set_cand(i, w) \
|
|
do { \
|
|
if (!candidates[(i)] \
|
|
|| (candidates[(i)]->spill_cost < (w)->spill_cost)) \
|
|
candidates[(i)] = (w); \
|
|
} while (0)
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *w = wl->t;
|
|
struct web *aw = alias (w);
|
|
/* If we are a spill-temp, we also look at webs coalesced
|
|
to precolored ones. Otherwise we only look at webs which
|
|
themselves were colored, or coalesced to one. */
|
|
if (aw->type == PRECOLORED && w != aw && web->spill_temp
|
|
&& flag_ra_optimistic_coalescing)
|
|
{
|
|
if (!w->spill_temp)
|
|
set_cand (4, w);
|
|
else if (web->spill_temp == 2
|
|
&& w->spill_temp == 2
|
|
&& w->spill_cost < web->spill_cost)
|
|
set_cand (5, w);
|
|
else if (web->spill_temp != 2
|
|
&& (w->spill_temp == 2
|
|
|| w->spill_cost < web->spill_cost))
|
|
set_cand (6, w);
|
|
continue;
|
|
}
|
|
if (aw->type != COLORED)
|
|
continue;
|
|
if (w->type == COLORED && !w->spill_temp && !w->is_coalesced
|
|
&& w->was_spilled)
|
|
{
|
|
if (w->spill_cost < web->spill_cost)
|
|
set_cand (0, w);
|
|
else if (web->spill_temp)
|
|
set_cand (1, w);
|
|
}
|
|
if (w->type == COLORED && !w->spill_temp && !w->is_coalesced
|
|
&& !w->was_spilled)
|
|
{
|
|
if (w->spill_cost < web->spill_cost)
|
|
set_cand (2, w);
|
|
else if (web->spill_temp && web->spill_temp != 2)
|
|
set_cand (3, w);
|
|
}
|
|
if (web->spill_temp)
|
|
{
|
|
if (w->type == COLORED && w->spill_temp == 2
|
|
&& !w->is_coalesced
|
|
&& (w->spill_cost < web->spill_cost
|
|
|| web->spill_temp != 2))
|
|
set_cand (4, w);
|
|
if (!aw->spill_temp)
|
|
set_cand (5, aw);
|
|
if (aw->spill_temp == 2
|
|
&& (aw->spill_cost < web->spill_cost
|
|
|| web->spill_temp != 2))
|
|
set_cand (6, aw);
|
|
/* For boehm-gc/misc.c. If we are a difficult spilltemp,
|
|
also coalesced neighbors are a chance, _even_ if they
|
|
too are spilltemps. At least their coalescing can be
|
|
broken up, which may be reset usable_regs, and makes
|
|
it easier colorable. */
|
|
if (web->spill_temp != 2 && aw->is_coalesced
|
|
&& flag_ra_optimistic_coalescing)
|
|
set_cand (7, aw);
|
|
}
|
|
}
|
|
for (loop = 0; try == NULL && loop < 8; loop++)
|
|
if (candidates[loop])
|
|
try = candidates[loop];
|
|
#undef set_cand
|
|
if (try)
|
|
{
|
|
int old_c = try->color;
|
|
if (try->type == COALESCED)
|
|
{
|
|
if (alias (try)->type != PRECOLORED)
|
|
abort ();
|
|
ra_debug_msg (DUMP_COLORIZE, " breaking alias %d -> %d\n",
|
|
try->id, alias (try)->id);
|
|
break_precolored_alias (try);
|
|
colorize_one_web (web, hard);
|
|
}
|
|
else
|
|
{
|
|
remove_list (try->dlink, &WEBS(COLORED));
|
|
put_web (try, SPILLED);
|
|
/* Now try to colorize us again. Can recursively make other
|
|
webs also spill, until there are no more unspilled
|
|
neighbors. */
|
|
ra_debug_msg (DUMP_COLORIZE, " trying to spill %d\n", try->id);
|
|
colorize_one_web (web, hard);
|
|
if (web->type != COLORED)
|
|
{
|
|
/* We tried recursively to spill all already colored
|
|
neighbors, but we are still uncolorable. So it made
|
|
no sense to spill those neighbors. Recolor them. */
|
|
remove_list (try->dlink, &WEBS(SPILLED));
|
|
put_web (try, COLORED);
|
|
try->color = old_c;
|
|
ra_debug_msg (DUMP_COLORIZE,
|
|
" spilling %d was useless\n", try->id);
|
|
}
|
|
else
|
|
{
|
|
ra_debug_msg (DUMP_COLORIZE,
|
|
" to spill %d was a good idea\n",
|
|
try->id);
|
|
remove_list (try->dlink, &WEBS(SPILLED));
|
|
if (try->was_spilled)
|
|
colorize_one_web (try, 0);
|
|
else
|
|
colorize_one_web (try, hard - 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
/* No more chances to get a color, so give up hope and
|
|
spill us. */
|
|
put_web (web, SPILLED);
|
|
}
|
|
else
|
|
put_web (web, SPILLED);
|
|
}
|
|
else
|
|
{
|
|
put_web (web, COLORED);
|
|
web->color = c;
|
|
if (flag_ra_biased)
|
|
{
|
|
int nregs = HARD_REGNO_NREGS (c, GET_MODE (web->orig_x));
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *ptarget = alias (wl->t);
|
|
int i;
|
|
for (i = 0; i < nregs; i++)
|
|
SET_HARD_REG_BIT (ptarget->bias_colors, c + i);
|
|
}
|
|
}
|
|
}
|
|
if (web->regno >= max_normal_pseudo && web->type == SPILLED)
|
|
{
|
|
web->color = an_unusable_color;
|
|
remove_list (web->dlink, &WEBS(SPILLED));
|
|
put_web (web, COLORED);
|
|
}
|
|
if (web->type == SPILLED && flag_ra_optimistic_coalescing
|
|
&& web->is_coalesced)
|
|
{
|
|
ra_debug_msg (DUMP_COLORIZE, "breaking aliases to web %d:", web->id);
|
|
restore_conflicts_from_coalesce (web);
|
|
break_aliases_to_web (web);
|
|
insert_coalesced_conflicts ();
|
|
ra_debug_msg (DUMP_COLORIZE, "\n");
|
|
remove_list (web->dlink, &WEBS(SPILLED));
|
|
put_web (web, SELECT);
|
|
web->color = -1;
|
|
}
|
|
}
|
|
|
|
/* Assign the colors to all nodes on the select stack. And update the
|
|
colors of coalesced webs. */
|
|
|
|
static void
|
|
assign_colors (void)
|
|
{
|
|
struct dlist *d;
|
|
|
|
while (WEBS(SELECT))
|
|
{
|
|
d = pop_list (&WEBS(SELECT));
|
|
colorize_one_web (DLIST_WEB (d), 1);
|
|
}
|
|
|
|
for (d = WEBS(COALESCED); d; d = d->next)
|
|
{
|
|
struct web *a = alias (DLIST_WEB (d));
|
|
DLIST_WEB (d)->color = a->color;
|
|
}
|
|
}
|
|
|
|
/* WEB is a spilled web. Look if we can improve the cost of the graph,
|
|
by coloring WEB, even if we then need to spill some of it's neighbors.
|
|
For this we calculate the cost for each color C, that results when we
|
|
_would_ give WEB color C (i.e. the cost of the then spilled neighbors).
|
|
If the lowest cost among them is smaller than the spillcost of WEB, we
|
|
do that recoloring, and instead spill the neighbors.
|
|
|
|
This can sometime help, when due to irregularities in register file,
|
|
and due to multi word pseudos, the colorization is suboptimal. But
|
|
be aware, that currently this pass is quite slow. */
|
|
|
|
static void
|
|
try_recolor_web (struct web *web)
|
|
{
|
|
struct conflict_link *wl;
|
|
unsigned HOST_WIDE_INT *cost_neighbors;
|
|
unsigned int *min_color;
|
|
int newcol, c;
|
|
HARD_REG_SET precolored_neighbors, spill_temps;
|
|
HARD_REG_SET possible_begin, wide_seen;
|
|
cost_neighbors = xcalloc (FIRST_PSEUDO_REGISTER, sizeof (cost_neighbors[0]));
|
|
/* For each hard-regs count the number of preceding hardregs, which
|
|
would overlap this color, if used in WEB's mode. */
|
|
min_color = xcalloc (FIRST_PSEUDO_REGISTER, sizeof (int));
|
|
CLEAR_HARD_REG_SET (possible_begin);
|
|
for (c = 0; c < FIRST_PSEUDO_REGISTER; c++)
|
|
{
|
|
int i, nregs;
|
|
if (!HARD_REGNO_MODE_OK (c, GET_MODE (web->orig_x)))
|
|
continue;
|
|
nregs = HARD_REGNO_NREGS (c, GET_MODE (web->orig_x));
|
|
for (i = 0; i < nregs; i++)
|
|
if (!TEST_HARD_REG_BIT (web->usable_regs, c + i))
|
|
break;
|
|
if (i < nregs || nregs == 0)
|
|
continue;
|
|
SET_HARD_REG_BIT (possible_begin, c);
|
|
for (; nregs--;)
|
|
if (!min_color[c + nregs])
|
|
min_color[c + nregs] = 1 + c;
|
|
}
|
|
CLEAR_HARD_REG_SET (precolored_neighbors);
|
|
CLEAR_HARD_REG_SET (spill_temps);
|
|
CLEAR_HARD_REG_SET (wide_seen);
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
HARD_REG_SET dont_begin;
|
|
struct web *web2 = alias (wl->t);
|
|
struct conflict_link *nn;
|
|
int c1, c2;
|
|
int wide_p = 0;
|
|
if (wl->t->type == COALESCED || web2->type != COLORED)
|
|
{
|
|
if (web2->type == PRECOLORED)
|
|
{
|
|
c1 = min_color[web2->color];
|
|
c1 = (c1 == 0) ? web2->color : (c1 - 1);
|
|
c2 = web2->color;
|
|
for (; c1 <= c2; c1++)
|
|
SET_HARD_REG_BIT (precolored_neighbors, c1);
|
|
}
|
|
continue;
|
|
}
|
|
/* Mark colors for which some wide webs are involved. For
|
|
those the independent sets are not simply one-node graphs, so
|
|
they can't be recolored independent from their neighborhood. This
|
|
means, that our cost calculation can be incorrect (assuming it
|
|
can avoid spilling a web because it thinks some colors are available,
|
|
although it's neighbors which itself need recoloring might take
|
|
away exactly those colors). */
|
|
if (web2->add_hardregs)
|
|
wide_p = 1;
|
|
for (nn = web2->conflict_list; nn && !wide_p; nn = nn->next)
|
|
if (alias (nn->t)->add_hardregs)
|
|
wide_p = 1;
|
|
calculate_dont_begin (web2, &dont_begin);
|
|
c1 = min_color[web2->color];
|
|
/* Note that min_color[] contains 1-based values (zero means
|
|
undef). */
|
|
c1 = c1 == 0 ? web2->color : (c1 - 1);
|
|
c2 = web2->color + HARD_REGNO_NREGS (web2->color, GET_MODE
|
|
(web2->orig_x)) - 1;
|
|
for (; c1 <= c2; c1++)
|
|
if (TEST_HARD_REG_BIT (possible_begin, c1))
|
|
{
|
|
int nregs;
|
|
HARD_REG_SET colors;
|
|
nregs = HARD_REGNO_NREGS (c1, GET_MODE (web->orig_x));
|
|
COPY_HARD_REG_SET (colors, web2->usable_regs);
|
|
for (; nregs--;)
|
|
CLEAR_HARD_REG_BIT (colors, c1 + nregs);
|
|
if (wide_p)
|
|
SET_HARD_REG_BIT (wide_seen, c1);
|
|
if (get_free_reg (dont_begin, colors,
|
|
GET_MODE (web2->orig_x)) < 0)
|
|
{
|
|
if (web2->spill_temp)
|
|
SET_HARD_REG_BIT (spill_temps, c1);
|
|
else
|
|
cost_neighbors[c1] += web2->spill_cost;
|
|
}
|
|
}
|
|
}
|
|
newcol = -1;
|
|
for (c = 0; c < FIRST_PSEUDO_REGISTER; c++)
|
|
if (TEST_HARD_REG_BIT (possible_begin, c)
|
|
&& !TEST_HARD_REG_BIT (precolored_neighbors, c)
|
|
&& !TEST_HARD_REG_BIT (spill_temps, c)
|
|
&& (newcol == -1
|
|
|| cost_neighbors[c] < cost_neighbors[newcol]))
|
|
newcol = c;
|
|
if (newcol >= 0 && cost_neighbors[newcol] < web->spill_cost)
|
|
{
|
|
int nregs = HARD_REGNO_NREGS (newcol, GET_MODE (web->orig_x));
|
|
unsigned HOST_WIDE_INT cost = 0;
|
|
int *old_colors;
|
|
struct conflict_link *wl_next;
|
|
ra_debug_msg (DUMP_COLORIZE, "try to set web %d to color %d\n", web->id,
|
|
newcol);
|
|
remove_list (web->dlink, &WEBS(SPILLED));
|
|
put_web (web, COLORED);
|
|
web->color = newcol;
|
|
old_colors = xcalloc (num_webs, sizeof (int));
|
|
for (wl = web->conflict_list; wl; wl = wl_next)
|
|
{
|
|
struct web *web2 = alias (wl->t);
|
|
/* If web2 is a coalesce-target, and will become spilled
|
|
below in colorize_one_web(), and the current conflict wl
|
|
between web and web2 was only the result of that coalescing
|
|
this conflict will be deleted, making wl invalid. So save
|
|
the next conflict right now. Note that if web2 has indeed
|
|
such state, then wl->next can not be deleted in this
|
|
iteration. */
|
|
wl_next = wl->next;
|
|
if (web2->type == COLORED)
|
|
{
|
|
int nregs2 = HARD_REGNO_NREGS (web2->color, GET_MODE
|
|
(web2->orig_x));
|
|
if (web->color >= web2->color + nregs2
|
|
|| web2->color >= web->color + nregs)
|
|
continue;
|
|
old_colors[web2->id] = web2->color + 1;
|
|
web2->color = -1;
|
|
remove_list (web2->dlink, &WEBS(COLORED));
|
|
web2->type = SELECT;
|
|
/* Allow webs to be spilled. */
|
|
if (web2->spill_temp == 0 || web2->spill_temp == 2)
|
|
web2->was_spilled = 1;
|
|
colorize_one_web (web2, 1);
|
|
if (web2->type == SPILLED)
|
|
cost += web2->spill_cost;
|
|
}
|
|
}
|
|
/* The actual cost may be smaller than the guessed one, because
|
|
partial conflicts could result in some conflicting webs getting
|
|
a color, where we assumed it must be spilled. See the comment
|
|
above what happens, when wide webs are involved, and why in that
|
|
case there might actually be some webs spilled although thought to
|
|
be colorable. */
|
|
if (cost > cost_neighbors[newcol]
|
|
&& nregs == 1 && !TEST_HARD_REG_BIT (wide_seen, newcol))
|
|
abort ();
|
|
/* But if the new spill-cost is higher than our own, then really loose.
|
|
Respill us and recolor neighbors as before. */
|
|
if (cost > web->spill_cost)
|
|
{
|
|
ra_debug_msg (DUMP_COLORIZE,
|
|
"reset coloring of web %d, too expensive\n", web->id);
|
|
remove_list (web->dlink, &WEBS(COLORED));
|
|
web->color = -1;
|
|
put_web (web, SPILLED);
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *web2 = alias (wl->t);
|
|
if (old_colors[web2->id])
|
|
{
|
|
if (web2->type == SPILLED)
|
|
{
|
|
remove_list (web2->dlink, &WEBS(SPILLED));
|
|
web2->color = old_colors[web2->id] - 1;
|
|
put_web (web2, COLORED);
|
|
}
|
|
else if (web2->type == COLORED)
|
|
web2->color = old_colors[web2->id] - 1;
|
|
else if (web2->type == SELECT)
|
|
/* This means, that WEB2 once was a part of a coalesced
|
|
web, which got spilled in the above colorize_one_web()
|
|
call, and whose parts then got split and put back
|
|
onto the SELECT stack. As the cause for that splitting
|
|
(the coloring of WEB) was worthless, we should again
|
|
coalesce the parts, as they were before. For now we
|
|
simply leave them SELECTed, for our caller to take
|
|
care. */
|
|
;
|
|
else
|
|
abort ();
|
|
}
|
|
}
|
|
}
|
|
free (old_colors);
|
|
}
|
|
free (min_color);
|
|
free (cost_neighbors);
|
|
}
|
|
|
|
/* This ensures that all conflicts of coalesced webs are seen from
|
|
the webs coalesced into. combine() only adds the conflicts which
|
|
at the time of combining were not already SELECTed or COALESCED
|
|
to not destroy num_conflicts. Here we add all remaining conflicts
|
|
and thereby destroy num_conflicts. This should be used when num_conflicts
|
|
isn't used anymore, e.g. on a completely colored graph. */
|
|
|
|
static void
|
|
insert_coalesced_conflicts (void)
|
|
{
|
|
struct dlist *d;
|
|
for (d = WEBS(COALESCED); 0 && d; d = d->next)
|
|
{
|
|
struct web *web = DLIST_WEB (d);
|
|
struct web *aweb = alias (web);
|
|
struct conflict_link *wl;
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *tweb = aweb;
|
|
int i;
|
|
int nregs = 1 + web->add_hardregs;
|
|
if (aweb->type == PRECOLORED)
|
|
nregs = HARD_REGNO_NREGS (aweb->color, GET_MODE (web->orig_x));
|
|
for (i = 0; i < nregs; i++)
|
|
{
|
|
if (aweb->type == PRECOLORED)
|
|
tweb = hardreg2web[i + aweb->color];
|
|
/* There might be some conflict edges laying around
|
|
where the usable_regs don't intersect. This can happen
|
|
when first some webs were coalesced and conflicts
|
|
propagated, then some combining narrowed usable_regs and
|
|
further coalescing ignored those conflicts. Now there are
|
|
some edges to COALESCED webs but not to it's alias.
|
|
So abort only when they really should conflict. */
|
|
if ((!(tweb->type == PRECOLORED
|
|
|| TEST_BIT (sup_igraph, tweb->id * num_webs + wl->t->id))
|
|
|| !(wl->t->type == PRECOLORED
|
|
|| TEST_BIT (sup_igraph,
|
|
wl->t->id * num_webs + tweb->id)))
|
|
&& hard_regs_intersect_p (&tweb->usable_regs,
|
|
&wl->t->usable_regs))
|
|
abort ();
|
|
/*if (wl->sub == NULL)
|
|
record_conflict (tweb, wl->t);
|
|
else
|
|
{
|
|
struct sub_conflict *sl;
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
record_conflict (tweb, sl->t);
|
|
}*/
|
|
if (aweb->type != PRECOLORED)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* A function suitable to pass to qsort(). Compare the spill costs
|
|
of webs W1 and W2. When used by qsort, this would order webs with
|
|
largest cost first. */
|
|
|
|
static int
|
|
comp_webs_maxcost (const void *w1, const void *w2)
|
|
{
|
|
struct web *web1 = *(struct web **)w1;
|
|
struct web *web2 = *(struct web **)w2;
|
|
if (web1->spill_cost > web2->spill_cost)
|
|
return -1;
|
|
else if (web1->spill_cost < web2->spill_cost)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* This tries to recolor all spilled webs. See try_recolor_web()
|
|
how this is done. This just calls it for each spilled web. */
|
|
|
|
static void
|
|
recolor_spills (void)
|
|
{
|
|
unsigned int i, num;
|
|
struct web **order2web;
|
|
num = num_webs - num_subwebs;
|
|
order2web = xmalloc (num * sizeof (order2web[0]));
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
order2web[i] = id2web[i];
|
|
/* If we aren't breaking aliases, combine() wasn't merging the
|
|
spill_costs. So do that here to have sane measures. */
|
|
if (!flag_ra_merge_spill_costs && id2web[i]->type == COALESCED)
|
|
alias (id2web[i])->spill_cost += id2web[i]->spill_cost;
|
|
}
|
|
qsort (order2web, num, sizeof (order2web[0]), comp_webs_maxcost);
|
|
insert_coalesced_conflicts ();
|
|
dump_graph_cost (DUMP_COSTS, "before spill-recolor");
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
struct web *web = order2web[i];
|
|
if (web->type == SPILLED)
|
|
try_recolor_web (web);
|
|
}
|
|
/* It might have been decided in try_recolor_web() (in colorize_one_web())
|
|
that a coalesced web should be spilled, so it was put on the
|
|
select stack. Those webs need recoloring again, and all remaining
|
|
coalesced webs might need their color updated, so simply call
|
|
assign_colors() again. */
|
|
assign_colors ();
|
|
free (order2web);
|
|
}
|
|
|
|
/* This checks the current color assignment for obvious errors,
|
|
like two conflicting webs overlapping in colors, or the used colors
|
|
not being in usable regs. */
|
|
|
|
static void
|
|
check_colors (void)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < num_webs - num_subwebs; i++)
|
|
{
|
|
struct web *web = id2web[i];
|
|
struct web *aweb = alias (web);
|
|
struct conflict_link *wl;
|
|
int nregs, c;
|
|
if (aweb->type == SPILLED || web->regno >= max_normal_pseudo)
|
|
continue;
|
|
else if (aweb->type == COLORED)
|
|
nregs = HARD_REGNO_NREGS (aweb->color, GET_MODE (web->orig_x));
|
|
else if (aweb->type == PRECOLORED)
|
|
nregs = 1;
|
|
else
|
|
abort ();
|
|
/* The color must be valid for the original usable_regs. */
|
|
for (c = 0; c < nregs; c++)
|
|
if (!TEST_HARD_REG_BIT (web->usable_regs, aweb->color + c))
|
|
abort ();
|
|
/* Search the original (pre-coalesce) conflict list. In the current
|
|
one some imprecise conflicts may be noted (due to combine() or
|
|
insert_coalesced_conflicts() relocating partial conflicts) making
|
|
it look like some wide webs are in conflict and having the same
|
|
color. */
|
|
wl = (web->have_orig_conflicts ? web->orig_conflict_list
|
|
: web->conflict_list);
|
|
for (; wl; wl = wl->next)
|
|
if (wl->t->regno >= max_normal_pseudo)
|
|
continue;
|
|
else if (!wl->sub)
|
|
{
|
|
struct web *web2 = alias (wl->t);
|
|
int nregs2;
|
|
if (web2->type == COLORED)
|
|
nregs2 = HARD_REGNO_NREGS (web2->color, GET_MODE (web2->orig_x));
|
|
else if (web2->type == PRECOLORED)
|
|
nregs2 = 1;
|
|
else
|
|
continue;
|
|
if (aweb->color >= web2->color + nregs2
|
|
|| web2->color >= aweb->color + nregs)
|
|
continue;
|
|
abort ();
|
|
}
|
|
else
|
|
{
|
|
struct sub_conflict *sl;
|
|
int scol = aweb->color;
|
|
int tcol = alias (wl->t)->color;
|
|
if (alias (wl->t)->type == SPILLED)
|
|
continue;
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
{
|
|
int ssize = HARD_REGNO_NREGS (scol, GET_MODE (sl->s->orig_x));
|
|
int tsize = HARD_REGNO_NREGS (tcol, GET_MODE (sl->t->orig_x));
|
|
int sofs = 0, tofs = 0;
|
|
if (SUBWEB_P (sl->t)
|
|
&& GET_MODE_SIZE (GET_MODE (sl->t->orig_x)) >= UNITS_PER_WORD)
|
|
tofs = (SUBREG_BYTE (sl->t->orig_x) / UNITS_PER_WORD);
|
|
if (SUBWEB_P (sl->s)
|
|
&& GET_MODE_SIZE (GET_MODE (sl->s->orig_x))
|
|
>= UNITS_PER_WORD)
|
|
sofs = (SUBREG_BYTE (sl->s->orig_x) / UNITS_PER_WORD);
|
|
if ((tcol + tofs >= scol + sofs + ssize)
|
|
|| (scol + sofs >= tcol + tofs + tsize))
|
|
continue;
|
|
abort ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* WEB was a coalesced web. Make it unaliased again, and put it
|
|
back onto SELECT stack. */
|
|
|
|
static void
|
|
unalias_web (struct web *web)
|
|
{
|
|
web->alias = NULL;
|
|
web->is_coalesced = 0;
|
|
web->color = -1;
|
|
/* Well, initially everything was spilled, so it isn't incorrect,
|
|
that also the individual parts can be spilled.
|
|
XXX this isn't entirely correct, as we also relaxed the
|
|
spill_temp flag in combine(), which might have made components
|
|
spill, although they were a short or spilltemp web. */
|
|
web->was_spilled = 1;
|
|
remove_list (web->dlink, &WEBS(COALESCED));
|
|
/* Spilltemps must be colored right now (i.e. as early as possible),
|
|
other webs can be deferred to the end (the code building the
|
|
stack assumed that in this stage only one web was colored). */
|
|
if (web->spill_temp && web->spill_temp != 2)
|
|
put_web (web, SELECT);
|
|
else
|
|
put_web_at_end (web, SELECT);
|
|
}
|
|
|
|
/* WEB is a _target_ for coalescing which got spilled.
|
|
Break all aliases to WEB, and restore some of its member to the state
|
|
they were before coalescing. Due to the suboptimal structure of
|
|
the interference graph we need to go through all coalesced webs.
|
|
Somewhen we'll change this to be more sane. */
|
|
|
|
static void
|
|
break_aliases_to_web (struct web *web)
|
|
{
|
|
struct dlist *d, *d_next;
|
|
if (web->type != SPILLED)
|
|
abort ();
|
|
for (d = WEBS(COALESCED); d; d = d_next)
|
|
{
|
|
struct web *other = DLIST_WEB (d);
|
|
d_next = d->next;
|
|
/* Beware: Don't use alias() here. We really want to check only
|
|
one level of aliasing, i.e. only break up webs directly
|
|
aliased to WEB, not also those aliased through other webs. */
|
|
if (other->alias == web)
|
|
{
|
|
unalias_web (other);
|
|
ra_debug_msg (DUMP_COLORIZE, " %d", other->id);
|
|
}
|
|
}
|
|
web->spill_temp = web->orig_spill_temp;
|
|
web->spill_cost = web->orig_spill_cost;
|
|
/* Beware: The following possibly widens usable_regs again. While
|
|
it was narrower there might have been some conflicts added which got
|
|
ignored because of non-intersecting hardregsets. All those conflicts
|
|
would now matter again. Fortunately we only add conflicts when
|
|
coalescing, which is also the time of narrowing. And we remove all
|
|
those added conflicts again now that we unalias this web.
|
|
Therefore this is safe to do. */
|
|
COPY_HARD_REG_SET (web->usable_regs, web->orig_usable_regs);
|
|
web->is_coalesced = 0;
|
|
web->num_aliased = 0;
|
|
web->was_spilled = 1;
|
|
/* Reset is_coalesced flag for webs which itself are target of coalescing.
|
|
It was cleared above if it was coalesced to WEB. */
|
|
for (d = WEBS(COALESCED); d; d = d->next)
|
|
DLIST_WEB (d)->alias->is_coalesced = 1;
|
|
}
|
|
|
|
/* WEB is a web coalesced into a precolored one. Break that alias,
|
|
making WEB SELECTed again. Also restores the conflicts which resulted
|
|
from initially coalescing both. */
|
|
|
|
static void
|
|
break_precolored_alias (struct web *web)
|
|
{
|
|
struct web *pre = web->alias;
|
|
struct conflict_link *wl;
|
|
unsigned int c = pre->color;
|
|
unsigned int nregs = HARD_REGNO_NREGS (c, GET_MODE (web->orig_x));
|
|
if (pre->type != PRECOLORED)
|
|
abort ();
|
|
unalias_web (web);
|
|
/* Now we need to look at each conflict X of WEB, if it conflicts
|
|
with [PRE, PRE+nregs), and remove such conflicts, of X has not other
|
|
conflicts, which are coalesced into those precolored webs. */
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
{
|
|
struct web *x = wl->t;
|
|
struct web *y;
|
|
unsigned int i;
|
|
struct conflict_link *wl2;
|
|
struct conflict_link **pcl;
|
|
HARD_REG_SET regs;
|
|
if (!x->have_orig_conflicts)
|
|
continue;
|
|
/* First look at which colors can not go away, due to other coalesces
|
|
still existing. */
|
|
CLEAR_HARD_REG_SET (regs);
|
|
for (i = 0; i < nregs; i++)
|
|
SET_HARD_REG_BIT (regs, c + i);
|
|
for (wl2 = x->conflict_list; wl2; wl2 = wl2->next)
|
|
if (wl2->t->type == COALESCED && alias (wl2->t)->type == PRECOLORED)
|
|
CLEAR_HARD_REG_BIT (regs, alias (wl2->t)->color);
|
|
/* Now also remove the colors of those conflicts which already
|
|
were there before coalescing at all. */
|
|
for (wl2 = x->orig_conflict_list; wl2; wl2 = wl2->next)
|
|
if (wl2->t->type == PRECOLORED)
|
|
CLEAR_HARD_REG_BIT (regs, wl2->t->color);
|
|
/* The colors now still set are those for which WEB was the last
|
|
cause, i.e. those which can be removed. */
|
|
y = NULL;
|
|
for (i = 0; i < nregs; i++)
|
|
if (TEST_HARD_REG_BIT (regs, c + i))
|
|
{
|
|
struct web *sub;
|
|
y = hardreg2web[c + i];
|
|
RESET_BIT (sup_igraph, x->id * num_webs + y->id);
|
|
RESET_BIT (sup_igraph, y->id * num_webs + x->id);
|
|
RESET_BIT (igraph, igraph_index (x->id, y->id));
|
|
for (sub = x->subreg_next; sub; sub = sub->subreg_next)
|
|
RESET_BIT (igraph, igraph_index (sub->id, y->id));
|
|
}
|
|
if (!y)
|
|
continue;
|
|
pcl = &(x->conflict_list);
|
|
while (*pcl)
|
|
{
|
|
struct web *y = (*pcl)->t;
|
|
if (y->type != PRECOLORED || !TEST_HARD_REG_BIT (regs, y->color))
|
|
pcl = &((*pcl)->next);
|
|
else
|
|
*pcl = (*pcl)->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* WEB is a spilled web which was target for coalescing.
|
|
Delete all interference edges which were added due to that coalescing,
|
|
and break up the coalescing. */
|
|
|
|
static void
|
|
restore_conflicts_from_coalesce (struct web *web)
|
|
{
|
|
struct conflict_link **pcl;
|
|
struct conflict_link *wl;
|
|
pcl = &(web->conflict_list);
|
|
/* No original conflict list means no conflict was added at all
|
|
after building the graph. So neither we nor any neighbors have
|
|
conflicts due to this coalescing. */
|
|
if (!web->have_orig_conflicts)
|
|
return;
|
|
while (*pcl)
|
|
{
|
|
struct web *other = (*pcl)->t;
|
|
for (wl = web->orig_conflict_list; wl; wl = wl->next)
|
|
if (wl->t == other)
|
|
break;
|
|
if (wl)
|
|
{
|
|
/* We found this conflict also in the original list, so this
|
|
was no new conflict. */
|
|
pcl = &((*pcl)->next);
|
|
}
|
|
else
|
|
{
|
|
/* This is a new conflict, so delete it from us and
|
|
the neighbor. */
|
|
struct conflict_link **opcl;
|
|
struct conflict_link *owl;
|
|
struct sub_conflict *sl;
|
|
wl = *pcl;
|
|
*pcl = wl->next;
|
|
if (!other->have_orig_conflicts && other->type != PRECOLORED)
|
|
abort ();
|
|
for (owl = other->orig_conflict_list; owl; owl = owl->next)
|
|
if (owl->t == web)
|
|
break;
|
|
if (owl)
|
|
abort ();
|
|
opcl = &(other->conflict_list);
|
|
while (*opcl)
|
|
{
|
|
if ((*opcl)->t == web)
|
|
{
|
|
owl = *opcl;
|
|
*opcl = owl->next;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
opcl = &((*opcl)->next);
|
|
}
|
|
}
|
|
if (!owl && other->type != PRECOLORED)
|
|
abort ();
|
|
/* wl and owl contain the edge data to be deleted. */
|
|
RESET_BIT (sup_igraph, web->id * num_webs + other->id);
|
|
RESET_BIT (sup_igraph, other->id * num_webs + web->id);
|
|
RESET_BIT (igraph, igraph_index (web->id, other->id));
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
RESET_BIT (igraph, igraph_index (sl->s->id, sl->t->id));
|
|
if (other->type != PRECOLORED)
|
|
{
|
|
for (sl = owl->sub; sl; sl = sl->next)
|
|
RESET_BIT (igraph, igraph_index (sl->s->id, sl->t->id));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We must restore usable_regs because record_conflict will use it. */
|
|
COPY_HARD_REG_SET (web->usable_regs, web->orig_usable_regs);
|
|
/* We might have deleted some conflicts above, which really are still
|
|
there (diamond pattern coalescing). This is because we don't reference
|
|
count interference edges but some of them were the result of different
|
|
coalesces. */
|
|
for (wl = web->conflict_list; wl; wl = wl->next)
|
|
if (wl->t->type == COALESCED)
|
|
{
|
|
struct web *tweb;
|
|
for (tweb = wl->t->alias; tweb; tweb = tweb->alias)
|
|
{
|
|
if (wl->sub == NULL)
|
|
record_conflict (web, tweb);
|
|
else
|
|
{
|
|
struct sub_conflict *sl;
|
|
for (sl = wl->sub; sl; sl = sl->next)
|
|
{
|
|
struct web *sweb = NULL;
|
|
if (SUBWEB_P (sl->t))
|
|
sweb = find_subweb (tweb, sl->t->orig_x);
|
|
if (!sweb)
|
|
sweb = tweb;
|
|
record_conflict (sl->s, sweb);
|
|
}
|
|
}
|
|
if (tweb->type != COALESCED)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Repeatedly break aliases for spilled webs, which were target for
|
|
coalescing, and recolorize the resulting parts. Do this as long as
|
|
there are any spilled coalesce targets. */
|
|
|
|
static void
|
|
break_coalesced_spills (void)
|
|
{
|
|
int changed = 0;
|
|
while (1)
|
|
{
|
|
struct dlist *d;
|
|
struct web *web;
|
|
for (d = WEBS(SPILLED); d; d = d->next)
|
|
if (DLIST_WEB (d)->is_coalesced)
|
|
break;
|
|
if (!d)
|
|
break;
|
|
changed = 1;
|
|
web = DLIST_WEB (d);
|
|
ra_debug_msg (DUMP_COLORIZE, "breaking aliases to web %d:", web->id);
|
|
restore_conflicts_from_coalesce (web);
|
|
break_aliases_to_web (web);
|
|
/* WEB was a spilled web and isn't anymore. Everything coalesced
|
|
to WEB is now SELECTed and might potentially get a color.
|
|
If those other webs were itself targets of coalescing it might be
|
|
that there are still some conflicts from aliased webs missing,
|
|
because they were added in combine() right into the now
|
|
SELECTed web. So we need to add those missing conflicts here. */
|
|
insert_coalesced_conflicts ();
|
|
ra_debug_msg (DUMP_COLORIZE, "\n");
|
|
remove_list (d, &WEBS(SPILLED));
|
|
put_web (web, SELECT);
|
|
web->color = -1;
|
|
while (WEBS(SELECT))
|
|
{
|
|
d = pop_list (&WEBS(SELECT));
|
|
colorize_one_web (DLIST_WEB (d), 1);
|
|
}
|
|
}
|
|
if (changed)
|
|
{
|
|
struct dlist *d;
|
|
for (d = WEBS(COALESCED); d; d = d->next)
|
|
{
|
|
struct web *a = alias (DLIST_WEB (d));
|
|
DLIST_WEB (d)->color = a->color;
|
|
}
|
|
}
|
|
dump_graph_cost (DUMP_COSTS, "after alias-breaking");
|
|
}
|
|
|
|
/* A structure for fast hashing of a pair of webs.
|
|
Used to cumulate savings (from removing copy insns) for coalesced webs.
|
|
All the pairs are also put into a single linked list. */
|
|
struct web_pair
|
|
{
|
|
struct web_pair *next_hash;
|
|
struct web_pair *next_list;
|
|
struct web *smaller;
|
|
struct web *larger;
|
|
unsigned int conflicts;
|
|
unsigned HOST_WIDE_INT cost;
|
|
};
|
|
|
|
/* The actual hash table. */
|
|
#define WEB_PAIR_HASH_SIZE 8192
|
|
static struct web_pair *web_pair_hash[WEB_PAIR_HASH_SIZE];
|
|
static struct web_pair *web_pair_list;
|
|
static unsigned int num_web_pairs;
|
|
|
|
/* Clear the hash table of web pairs. */
|
|
|
|
static void
|
|
init_web_pairs (void)
|
|
{
|
|
memset (web_pair_hash, 0, sizeof web_pair_hash);
|
|
num_web_pairs = 0;
|
|
web_pair_list = NULL;
|
|
}
|
|
|
|
/* Given two webs connected by a move with cost COST which together
|
|
have CONFLICTS conflicts, add that pair to the hash table, or if
|
|
already in, cumulate the costs and conflict number. */
|
|
|
|
static void
|
|
add_web_pair_cost (struct web *web1, struct web *web2,
|
|
unsigned HOST_WIDE_INT cost, unsigned int conflicts)
|
|
{
|
|
unsigned int hash;
|
|
struct web_pair *p;
|
|
if (web1->id > web2->id)
|
|
{
|
|
struct web *h = web1;
|
|
web1 = web2;
|
|
web2 = h;
|
|
}
|
|
hash = (web1->id * num_webs + web2->id) % WEB_PAIR_HASH_SIZE;
|
|
for (p = web_pair_hash[hash]; p; p = p->next_hash)
|
|
if (p->smaller == web1 && p->larger == web2)
|
|
{
|
|
p->cost += cost;
|
|
p->conflicts += conflicts;
|
|
return;
|
|
}
|
|
p = ra_alloc (sizeof *p);
|
|
p->next_hash = web_pair_hash[hash];
|
|
p->next_list = web_pair_list;
|
|
p->smaller = web1;
|
|
p->larger = web2;
|
|
p->conflicts = conflicts;
|
|
p->cost = cost;
|
|
web_pair_hash[hash] = p;
|
|
web_pair_list = p;
|
|
num_web_pairs++;
|
|
}
|
|
|
|
/* Suitable to be passed to qsort(). Sort web pairs so, that those
|
|
with more conflicts and higher cost (which actually is a saving
|
|
when the moves are removed) come first. */
|
|
|
|
static int
|
|
comp_web_pairs (const void *w1, const void *w2)
|
|
{
|
|
struct web_pair *p1 = *(struct web_pair **)w1;
|
|
struct web_pair *p2 = *(struct web_pair **)w2;
|
|
if (p1->conflicts > p2->conflicts)
|
|
return -1;
|
|
else if (p1->conflicts < p2->conflicts)
|
|
return 1;
|
|
else if (p1->cost > p2->cost)
|
|
return -1;
|
|
else if (p1->cost < p2->cost)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Given the list of web pairs, begin to combine them from the one
|
|
with the most savings. */
|
|
|
|
static void
|
|
sort_and_combine_web_pairs (int for_move)
|
|
{
|
|
unsigned int i;
|
|
struct web_pair **sorted;
|
|
struct web_pair *p;
|
|
if (!num_web_pairs)
|
|
return;
|
|
sorted = xmalloc (num_web_pairs * sizeof (sorted[0]));
|
|
for (p = web_pair_list, i = 0; p; p = p->next_list)
|
|
sorted[i++] = p;
|
|
if (i != num_web_pairs)
|
|
abort ();
|
|
qsort (sorted, num_web_pairs, sizeof (sorted[0]), comp_web_pairs);
|
|
|
|
/* After combining one pair, we actually should adjust the savings
|
|
of the other pairs, if they are connected to one of the just coalesced
|
|
pair. Later. */
|
|
for (i = 0; i < num_web_pairs; i++)
|
|
{
|
|
struct web *w1, *w2;
|
|
p = sorted[i];
|
|
w1 = alias (p->smaller);
|
|
w2 = alias (p->larger);
|
|
if (!for_move && (w1->type == PRECOLORED || w2->type == PRECOLORED))
|
|
continue;
|
|
else if (w2->type == PRECOLORED)
|
|
{
|
|
struct web *h = w1;
|
|
w1 = w2;
|
|
w2 = h;
|
|
}
|
|
if (w1 != w2
|
|
&& !TEST_BIT (sup_igraph, w1->id * num_webs + w2->id)
|
|
&& !TEST_BIT (sup_igraph, w2->id * num_webs + w1->id)
|
|
&& w2->type != PRECOLORED
|
|
&& hard_regs_intersect_p (&w1->usable_regs, &w2->usable_regs))
|
|
{
|
|
if (w1->type != PRECOLORED
|
|
|| (w1->type == PRECOLORED && ok (w2, w1)))
|
|
combine (w1, w2);
|
|
else if (w1->type == PRECOLORED)
|
|
SET_HARD_REG_BIT (w2->prefer_colors, w1->color);
|
|
}
|
|
}
|
|
free (sorted);
|
|
}
|
|
|
|
/* Greedily coalesce all moves possible. Begin with the web pair
|
|
giving the most saving if coalesced. */
|
|
|
|
static void
|
|
aggressive_coalesce (void)
|
|
{
|
|
struct dlist *d;
|
|
struct move *m;
|
|
init_web_pairs ();
|
|
while ((d = pop_list (&mv_worklist)) != NULL)
|
|
if ((m = DLIST_MOVE (d)))
|
|
{
|
|
struct web *s = alias (m->source_web);
|
|
struct web *t = alias (m->target_web);
|
|
if (t->type == PRECOLORED)
|
|
{
|
|
struct web *h = s;
|
|
s = t;
|
|
t = h;
|
|
}
|
|
if (s != t
|
|
&& t->type != PRECOLORED
|
|
&& !TEST_BIT (sup_igraph, s->id * num_webs + t->id)
|
|
&& !TEST_BIT (sup_igraph, t->id * num_webs + s->id))
|
|
{
|
|
if ((s->type == PRECOLORED && ok (t, s))
|
|
|| s->type != PRECOLORED)
|
|
{
|
|
put_move (m, MV_COALESCED);
|
|
add_web_pair_cost (s, t, BLOCK_FOR_INSN (m->insn)->frequency,
|
|
0);
|
|
}
|
|
else if (s->type == PRECOLORED)
|
|
/* It is !ok(t, s). But later when coloring the graph it might
|
|
be possible to take that color. So we remember the preferred
|
|
color to try that first. */
|
|
{
|
|
put_move (m, CONSTRAINED);
|
|
SET_HARD_REG_BIT (t->prefer_colors, s->color);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
put_move (m, CONSTRAINED);
|
|
}
|
|
}
|
|
sort_and_combine_web_pairs (1);
|
|
}
|
|
|
|
/* This is the difference between optimistic coalescing and
|
|
optimistic coalescing+. Extended coalesce tries to coalesce also
|
|
non-conflicting nodes, not related by a move. The criteria here is,
|
|
the one web must be a source, the other a destination of the same insn.
|
|
This actually makes sense, as (because they are in the same insn) they
|
|
share many of their neighbors, and if they are coalesced, reduce the
|
|
number of conflicts of those neighbors by one. For this we sort the
|
|
candidate pairs again according to savings (and this time also conflict
|
|
number).
|
|
|
|
This is also a comparatively slow operation, as we need to go through
|
|
all insns, and for each insn, through all defs and uses. */
|
|
|
|
static void
|
|
extended_coalesce_2 (void)
|
|
{
|
|
rtx insn;
|
|
struct ra_insn_info info;
|
|
unsigned int n;
|
|
init_web_pairs ();
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
|
if (INSN_P (insn) && (info = insn_df[INSN_UID (insn)]).num_defs)
|
|
for (n = 0; n < info.num_defs; n++)
|
|
{
|
|
struct web *dest = def2web[DF_REF_ID (info.defs[n])];
|
|
dest = alias (find_web_for_subweb (dest));
|
|
if (dest->type != PRECOLORED && dest->regno < max_normal_pseudo)
|
|
{
|
|
unsigned int n2;
|
|
for (n2 = 0; n2 < info.num_uses; n2++)
|
|
{
|
|
struct web *source = use2web[DF_REF_ID (info.uses[n2])];
|
|
source = alias (find_web_for_subweb (source));
|
|
if (source->type != PRECOLORED
|
|
&& source != dest
|
|
&& source->regno < max_normal_pseudo
|
|
/* Coalesced webs end up using the same REG rtx in
|
|
emit_colors(). So we can only coalesce something
|
|
of equal modes. */
|
|
&& GET_MODE (source->orig_x) == GET_MODE (dest->orig_x)
|
|
&& !TEST_BIT (sup_igraph,
|
|
dest->id * num_webs + source->id)
|
|
&& !TEST_BIT (sup_igraph,
|
|
source->id * num_webs + dest->id)
|
|
&& hard_regs_intersect_p (&source->usable_regs,
|
|
&dest->usable_regs))
|
|
add_web_pair_cost (dest, source,
|
|
BLOCK_FOR_INSN (insn)->frequency,
|
|
dest->num_conflicts
|
|
+ source->num_conflicts);
|
|
}
|
|
}
|
|
}
|
|
sort_and_combine_web_pairs (0);
|
|
}
|
|
|
|
/* Check if we forgot to coalesce some moves. */
|
|
|
|
static void
|
|
check_uncoalesced_moves (void)
|
|
{
|
|
struct move_list *ml;
|
|
struct move *m;
|
|
for (ml = wl_moves; ml; ml = ml->next)
|
|
if ((m = ml->move))
|
|
{
|
|
struct web *s = alias (m->source_web);
|
|
struct web *t = alias (m->target_web);
|
|
if (t->type == PRECOLORED)
|
|
{
|
|
struct web *h = s;
|
|
s = t;
|
|
t = h;
|
|
}
|
|
if (s != t
|
|
&& m->type != CONSTRAINED
|
|
/* Following can happen when a move was coalesced, but later
|
|
broken up again. Then s!=t, but m is still MV_COALESCED. */
|
|
&& m->type != MV_COALESCED
|
|
&& t->type != PRECOLORED
|
|
&& ((s->type == PRECOLORED && ok (t, s))
|
|
|| s->type != PRECOLORED)
|
|
&& !TEST_BIT (sup_igraph, s->id * num_webs + t->id)
|
|
&& !TEST_BIT (sup_igraph, t->id * num_webs + s->id))
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
/* The toplevel function in this file. Precondition is, that
|
|
the interference graph is built completely by ra-build.c. This
|
|
produces a list of spilled, colored and coalesced nodes. */
|
|
|
|
void
|
|
ra_colorize_graph (struct df *df)
|
|
{
|
|
if (rtl_dump_file)
|
|
dump_igraph (df);
|
|
build_worklists (df);
|
|
|
|
/* With optimistic coalescing we coalesce everything we can. */
|
|
if (flag_ra_optimistic_coalescing)
|
|
{
|
|
aggressive_coalesce ();
|
|
extended_coalesce_2 ();
|
|
}
|
|
|
|
/* Now build the select stack. */
|
|
do
|
|
{
|
|
simplify ();
|
|
if (mv_worklist)
|
|
coalesce ();
|
|
else if (WEBS(FREEZE))
|
|
freeze ();
|
|
else if (WEBS(SPILL))
|
|
select_spill ();
|
|
}
|
|
while (WEBS(SIMPLIFY) || WEBS(SIMPLIFY_FAT) || WEBS(SIMPLIFY_SPILL)
|
|
|| mv_worklist || WEBS(FREEZE) || WEBS(SPILL));
|
|
if (flag_ra_optimistic_coalescing)
|
|
check_uncoalesced_moves ();
|
|
|
|
/* Actually colorize the webs from the select stack. */
|
|
assign_colors ();
|
|
check_colors ();
|
|
dump_graph_cost (DUMP_COSTS, "initially");
|
|
if (flag_ra_break_aliases)
|
|
break_coalesced_spills ();
|
|
check_colors ();
|
|
|
|
/* And try to improve the cost by recoloring spilled webs. */
|
|
recolor_spills ();
|
|
dump_graph_cost (DUMP_COSTS, "after spill-recolor");
|
|
check_colors ();
|
|
}
|
|
|
|
/* Initialize this module. */
|
|
|
|
void ra_colorize_init (void)
|
|
{
|
|
/* FIXME: Choose spill heuristic for platform if we have one */
|
|
spill_heuristic = default_spill_heuristic;
|
|
}
|
|
|
|
/* Free all memory. (Note that we don't need to free any per pass
|
|
memory). */
|
|
|
|
void
|
|
ra_colorize_free_all (void)
|
|
{
|
|
struct dlist *d;
|
|
while ((d = pop_list (&WEBS(FREE))) != NULL)
|
|
put_web (DLIST_WEB (d), INITIAL);
|
|
while ((d = pop_list (&WEBS(INITIAL))) != NULL)
|
|
{
|
|
struct web *web = DLIST_WEB (d);
|
|
struct web *wnext;
|
|
web->orig_conflict_list = NULL;
|
|
web->conflict_list = NULL;
|
|
for (web = web->subreg_next; web; web = wnext)
|
|
{
|
|
wnext = web->subreg_next;
|
|
free (web);
|
|
}
|
|
free (DLIST_WEB (d));
|
|
}
|
|
}
|
|
|
|
/*
|
|
vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
|
|
*/
|