logo       

[perl #32208] [PATCH] Register allocation patch - scales better to more sym: msg#00618

lang.perl.perl6.internals

Subject: [perl #32208] [PATCH] Register allocation patch - scales better to more symbols

# New Ticket Created by Bill Coffman
# Please include the string: [perl #32208]
# in the subject line of all future correspondence about this issue.
# <URL: http://rt.perl.org:80/rt3/Ticket/Display.html?id=32208 >


Patch does the following:

- Applied Matula/Chaitin/Briggs algorithm for register allocation.
- Color the graph all at once, and spill all symbols with high colors.
Spill all at once to speed things up.
- Remove several of the functions, which are incorporated into the new
algorithm.

- Shortcomming: doesn't use score anymore, but the algorithm is smart
enough that I hope it's okay to do that.
- Failed 2 tests for latest CVS. (See earlier posting.)

WANT TO DO:
- Apparently, there's a memory leak which prevents from coloring
graphs with more than a few hundred registers. I suspect this is in
the spill, or update_life routine. Not sure if it's mine or
pre-existing.
- Interference graph is using 8 times the memory it needs to use.
This is still trivial compared to lost data in above bug.
- Smarten up algorithm to use score again. A good way to do so is
commented in the code.
- Create spilling score, that prints out with a debug option. This
can be a metric to compare various algorithms.
- Improve spill to spill all registers at once, adding speed.
- Introduce proper analysis of flow graph, to create less conservative
interference graph.
- Color each of the four register types separately. Be sure to
compare gains with losses for this, as it is not entirely cear.
- Introduce register renaming. When variable is reassigned, it might
as well be considered a new symbol... well, much of the time, anyway.
- Introduce variable register size, in coordination with subroutine
calls, to reduce copy cost. Coordinate with Dan and Leo on this.
- Improve flow-graph, basic block calculation, etc. Make it all a
little easier to understand, and more efficient. Knuth style,
literate programming. Well, just good comments, and a couple of
decent pods.
Index: imcc/reg_alloc.c
===================================================================
RCS file: /cvs/public/parrot/imcc/reg_alloc.c,v
retrieving revision 1.22
diff -u -r1.22 reg_alloc.c
--- imcc/reg_alloc.c 30 Sep 2004 16:00:37 -0000 1.22
+++ imcc/reg_alloc.c 29 Oct 2004 04:39:07 -0000
@@ -41,15 +41,63 @@
static void compute_du_chain(IMC_Unit * unit);
static void compute_one_du_chain(SymReg * r, IMC_Unit * unit);
static int interferes(IMC_Unit *, SymReg * r0, SymReg * r1);
-static int map_colors(IMC_Unit *, int x, unsigned int * graph, int colors[],
int typ);
-#ifdef DO_SIMPLIFY
-static int simplify (IMC_Unit *);
-#endif
static void compute_spilling_costs (Parrot_Interp, IMC_Unit *);
-static void order_spilling (IMC_Unit *);
static void spill (Interp *, IMC_Unit * unit, int);
-static int try_allocate(Parrot_Interp, IMC_Unit *);
-static void restore_interference_graph(IMC_Unit *);
+
+/***************** New graph algorithm stuff *********************/
+static void ig_color_graph(void);
+static void apply_coloring(IMC_Unit *);
+static void ig_precolor(IMC_Unit *);
+static int ig_init_graph(int num_nodes, unsigned* edge_bits);
+static void ig_clear_graph(void);
+static int spill_registers(Parrot_Interp interpreter, IMC_Unit * unit);
+
+typedef struct {
+ int deg; /* degree of node (# neighbors) */
+ int col; /* color assigned to this node */
+ int rank; /* position within the below D array */
+ char in; /* boolean, indicating if removed yet */
+} node;
+
+typedef struct {
+ int n; /* number of nodes */
+ node* V; /* array of nodes */
+ int* D; /* sorted nodes by degree */
+ unsigned* E; /* edge data, adjacency matrix */
+ int k; /* maximum color used in graph (0 means uncolored) */
+} graph;
+
+graph G; /* must have as global to use qsort,
+ but there's only one at a time -- FIXME minimize this global */
+
+#define Dbg_level 0 /* FIXME -- must be a better way to implement this */
+#include <stdarg.h>
+static void my_message(const char *pat, ...)
+{
+ va_list args;
+ va_start(args, pat);
+#if Dbg_level >= 1
+ vfprintf(stderr,pat,args);
+#endif
+ va_end(args);
+}
+static void my_message2(const char *pat, ...)
+{
+ va_list args;
+ va_start(args, pat);
+#if Dbg_level >= 2
+ vfprintf(stderr,pat,args);
+#endif
+ va_end(args);
+}
+/*#define Dbg printf*/
+#define Dbg my_message
+#define Dbg2 my_message2
+
+
+/******************************************************************/
+
+
#if 0
static int neighbours(int node);
#endif
@@ -57,7 +105,7 @@
extern int pasm_file;
/* XXX FIXME: Globals: */

-static IMCStack nodeStack;
+static IMCStack nodeStack; /* FIXME -- this is used in a silly way */

static unsigned int* ig_get_word(int i, int j, int N, unsigned int* graph,
int* bit_ofs)
@@ -74,12 +122,14 @@
*word |= (1 << bit_ofs);
}

+/* currently unused.
static void ig_clear(int i, int j, int N, unsigned int* graph)
{
int bit_ofs;
unsigned int* word = ig_get_word(i, j, N, graph, &bit_ofs);
*word &= ~(1 << bit_ofs);
}
+*/

int ig_test(int i, int j, int N, unsigned int* graph);
int ig_test(int i, int j, int N, unsigned int* graph)
@@ -105,9 +155,7 @@
void
imc_reg_alloc(Interp *interpreter, IMC_Unit * unit)
{
- int to_spill;
- int todo, first;
- int loop_counter;
+ int todo, first, loop_counter;

if (!unit)
return;
@@ -181,45 +229,36 @@
}
todo = 1;
loop_counter = 0;
-#if !DOIT_AGAIN_SAM
+
build_interference_graph(interpreter, unit);
-#endif
- while (todo) {
- loop_counter++;
-#if DOIT_AGAIN_SAM
+
+ if (optimizer_level & OPT_SUB)
+ allocate_wanted_regs(unit);
+
+ if (!ig_init_graph(unit->n_symbols, unit->interference_graph))
+ fatal(1,"imc_reg_alloc", "cannot allocate memory for coloring graph");
+ ig_precolor(unit);
+ ig_color_graph(); /* in G */
+
+ /*
+ * Use colors from G to allocate registers and spill the high colors.
+ */
+ while (G.k > MAX_COLOR) { /* inside this loop, we must spill */
+ compute_spilling_costs(interpreter, unit); /* will eventually use */
+ spill_registers(interpreter, unit); /* this changes things */
+
+ ig_clear_graph(); /* reclaim used memory for G */
+ free(unit->interference_graph);
build_interference_graph(interpreter, unit);
-#endif
- if (optimizer_level & OPT_SUB)
- allocate_wanted_regs(unit);
- compute_spilling_costs(interpreter, unit);
-#ifdef DO_SIMPLIFY
- /* simplify until no changes can be made */
- while (simplify(unit)) {}
-#endif
- order_spilling(unit); /* put the remaining items on stack */
+ if (!ig_init_graph(unit->n_symbols, unit->interference_graph))
+ fatal(1,"imc_reg_alloc", "cannot allocate memory for coloring
graph");
+ ig_precolor(unit);
+ ig_color_graph(); /* in G */
+ }
+ apply_coloring(unit);

- to_spill = try_allocate(interpreter, unit);
- allocated = 1;
+ ig_clear_graph(); /* reclaim used memory */

- if ( to_spill >= 0 ) {
- allocated = 0;
- spill(interpreter, unit, to_spill);
- /*
- * build the new cfg/reglist on the fly in spill() and
- * do life analysis there for only the involved regs
- */
-#if DOIT_AGAIN_SAM
- find_basic_blocks(interpreter, unit, 0);
- build_cfg(interpreter, unit);
- build_reglist(interpreter, unit, 1);
- life_analysis(interpreter);
-#endif
- }
- else {
- /* the process is finished */
- todo = 0;
- }
- }
if (optimizer_level & OPT_SUB)
sub_optimize(interpreter, unit);
if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
@@ -227,6 +266,7 @@
if (IMCC_INFO(interpreter)->verbose ||
(IMCC_INFO(interpreter)->debug & DEBUG_IMC))
print_stat(interpreter, unit);
+ free(unit->interference_graph);
imcstack_free(nodeStack);
}

@@ -327,99 +367,6 @@
qsort(unit->reglist, unit->n_symbols, sizeof(SymReg*), reg_sort_f);
}

-/*
- * regs are sorted now according to their start line of usage
- * which means they are sorted by basic block numbers too
- *
- * run through them and allocate all that don't overlap
- * in one bunch
- *
- * registers 28-30 are reserved for short range temps, which
- * get allocate immediately
- */
-
-#define ALLOCATE_HACK
-
-#ifdef ALLOCATE_HACK
-
-static void
-allocate_non_interfering(Parrot_Interp interpreter, IMC_Unit *unit, int n)
-{
- int i, t;
- int first_color, last_line, b, first_reg;
- SymReg *r = NULL; /* uninit gcc warning */
- SymReg ** reglist = unit->reglist;
-
- for (t = 0; t < 4; t++) {
- int typ = "INSP"[t];
- i = 0;
- for (b = 0; b < unit->n_basic_blocks; b++) {
- first_color = 30;
- last_line = -1;
- first_reg = -1;
- while (first_color >= 28) { /* 28..30 for 3 temps */
- for (; i < n; ++i) {
- r = reglist[i];
- /*
- * if we didn't reach this basic block, continue
- */
- if (r->first_ins->bbindex < b)
- continue;
- /* remember first register in this block */
- if (first_reg == -1)
- first_reg = i;
- /*
- * register set must match and not already allocated
- */
- if (r->set != typ || (r->type & VT_REGP) ||
- r->want_regno >= 0 || r->color >= 0)
- continue;
- if (r->color == first_color) {
- warning(interpreter, "allocate_non_interfering",
- "color %d for register type %c in use",
- first_color, typ);
- goto next_color;
- }
- /* at end of block start over, try next color */
- if (r->first_ins->bbindex > b)
- goto next_color;
- /* if this register spans more then one block
- * try next one
- */
- if (r->last_ins->bbindex > b)
- continue;
- /*
- * if it interfers with the previous allocated reg
- * try next one
- */
- if (r->first_ins->index <= last_line)
- continue;
- assert(r->first_ins->bbindex == r->last_ins->bbindex);
- break;
- }
- if (i == n)
- goto next_block;
- last_line = r->last_ins->index;
- r->color = first_color;
- r->type = VTPASM;
- debug(interpreter, DEBUG_IMC, "block %d coloring '%s' %d\n",
- b, r->name, r->color);
- continue;
-next_color:
- if (first_reg >= 0)
- i = first_reg;
- else
- i = 0;
- /* reset, color is decremented, so no interfernce */
- last_line = -1;
- --first_color;
- }
-next_block:
- ;
- } /* for b */
- } /* for t */
-}
-#endif

/* make a linear list of IDENTs and VARs, set n_symbols
* TODO
@@ -442,10 +389,6 @@
for (; r; r = r->next) {
if (r->type & VT_REGP)
count++;
-#ifdef ALLOCATE_HACK
- if (r->color >= 28)
- continue;
-#endif
if (r->type & VTREGISTER)
count++;
}
@@ -468,10 +411,7 @@
if (r->type & VT_REGP)
unit->reglist[count++] = r->reg;
else
-#ifdef ALLOCATE_HACK
- if (r->color < 28)
-#endif
- unit->reglist[count++] = r;
+ unit->reglist[count++] = r;
/* rearange I/N registers
* XXX not here, do it, when reading the source
* .nciarg, ... !!!1 */
@@ -496,15 +436,6 @@
n_symbols -= unused;
unit->n_symbols = n_symbols;
sort_reglist(unit);
- if (first) {
-#ifdef ALLOCATE_HACK
- info(interpreter, 1, "build_reglist: %d symbols\n", n_symbols);
- allocate_non_interfering(interpreter, unit, n_symbols);
- build_reglist(interpreter, unit, 0);
- info(interpreter, 1, "allocate_non_interfering, now: %d symbols\n",
- unit->n_symbols);
-#endif
- }
}

/* creates the interference graph between the variables.
@@ -755,128 +686,9 @@
return 0;
}

-/*
- * Simplify deletes all the nodes from the interference graph
- * that have arity lower than MAX_COLOR
- *
- * Returns if it has been able to delete at least one node
- * and 0 otherwise.
- *
- * XXX: this doesn't look at score, so I think this is bogus
- * - currently disabled
- *
- */
-#ifdef DO_SIMPLIFY
-static int
-simplify (IMC_Unit * unit)
-{
- int changes = 0;
- int x;
- SymReg **g;
-
- g = unit->reglist;
-
- for (x = 0; x < n_symbols; x++) {
- if (g[x]->color >= 0) /* VTPASM */
- g[x]->simplified = 1;
- }
- for (x = 0; x < n_symbols; x++) {
- if (g[x]->simplified) {
- break;
- }
-
- if ( neighbours(x) < MAX_COLOR) {
- debug(interpreter, DEBUG_IMC, "#simplifying [%s]\n", g[x]->name);
-
- imcstack_push(nodeStack, x);
-
- g[x]->simplified = 1;
- changes = 1;
- break;
- }
- }
-
- return changes;
-}
-#endif
-
-/* Puts the remaining nodes on the stack, on the correct order.
- *
- * We count how many times does a symbol appear (weighted by the probability
- * that this particular point of code will be reached) and choose the symbol
- * with the lower score until all are in the stack.
- *
- */
-
-static void
-order_spilling (IMC_Unit * unit)
-{
- int min_score = 0, total_score;
- int min_node;
- int x;
-
- while (1) {
-
- min_node = -1;
-
- for (x = 0; x < unit->n_symbols; x++) {
- /* if its spilled skip it */
- if (unit->reglist[x]->usage & U_SPILL)
- continue;
-
- /* for now, our score function only
- takes in account how many times a symbols
- has appeared + the loop_depth */
-
- /* we have to combine somehow the rank of the node
- * with the cost of spilling it
- *
- * our current function to maximize is:
- * rank - spill_cost
- *
- * I have no clue of how good it is
- */
- if (!(unit->reglist[x]->simplified)) {
- total_score = unit->reglist[x]->score /* - neighbours(x) */;
-
- if ( (min_node == -1) || (min_score > total_score) ) {
- min_node = x;
- min_score = total_score;
- }
- }
- }
-
- if (min_node == -1)
- break; /* We are finished */
-
- imcstack_push(nodeStack, min_node);
- unit->reglist[min_node]->simplified = 1;
- }
- /*
- * now put all spilled regs on top of stack so that they
- * get their register first
- */
- for (x = 0; x < unit->n_symbols; x++) {
- if (unit->reglist[x]->usage & U_SPILL)
- imcstack_push(nodeStack, x);
- }
-}
-
-
-static void
-restore_interference_graph(IMC_Unit * unit)
-{
- int i;
- for (i=0; i < unit->n_symbols; i++) {
- if ((unit->reglist[i]->type & VTPASM) && !(optimizer_level & OPT_PASM))
- continue;
- unit->reglist[i]->color = -1;
- unit->reglist[i]->simplified = 0;
- }
-}

/*
- * try to allocate as much as possible
+ * try to allocate as much as possible, an optimization ...
*/
static void
allocate_wanted_regs(IMC_Unit * unit)
@@ -905,106 +717,7 @@
r->color = r->want_regno;
}
}
-/*
- * Color the graph assigning registers to each symbol:
- *
- * We just proceed poping items from the stack, and assigning
- * a free color to the them.
- *
- * If we run out of colors, then we need to spill the top node.
- */

-static int
-try_allocate(Parrot_Interp interpreter, IMC_Unit * unit)
-{
- int x = 0;
- int color, colors[MAX_COLOR];
- int free_colors, t;
- unsigned int *graph = unit->interference_graph;
- SymReg ** reglist = unit->reglist;
-
- while ((imcstack_depth(nodeStack) > 0) ) {
- x=imcstack_pop(nodeStack);
-
- for (t = 0; t < 4; t++) {
- int typ = "INSP"[t];
- memset(colors, 0, sizeof(colors));
- if (reglist[x]->set == typ && reglist[x]->color == -1) {
- free_colors = map_colors(unit, x, graph, colors, typ);
- if (free_colors > 0) {
- for (color = 0; color < MAX_COLOR; color++) {
- int c = (color + MAX_COLOR/2) % MAX_COLOR;
- if (!colors[c]) {
- reglist[x]->color = c;
-
- debug(interpreter, DEBUG_IMC,
- "#[%s] provisionally gets color [%d]"
- "(%d free colors, score %d)\n",
- reglist[x]->name, c,
- free_colors, reglist[x]->score);
- break;
- }
- }
- }
-
- if (reglist[x]->color == -1) {
- debug(interpreter, DEBUG_IMC,
- "# no more colors free = %d\n", free_colors);
-
- /* It has been impossible to assign a color
- * to this node, return it so it gets spilled
- */
-
- restore_interference_graph(unit);
- /* clean stack */
- while ((imcstack_depth(nodeStack) > 0) )
- imcstack_pop(nodeStack);
- return x;
- }
- }
- }
- }
-
- return -1; /* we are totally finished */
-}
-
-/*
- * map_colors: calculates what colors can be assigned to the x-th symbol.
- */
-static int
-map_colors(IMC_Unit* unit, int x, unsigned int *graph, int colors[], int typ)
-{
- int y = 0, n_symbols;
- SymReg * r;
- int color, free_colors;
-
- n_symbols = unit->n_symbols;
- memset(colors, 0, sizeof(colors[0]) * MAX_COLOR);
- /* reserved for spilling */
- if (typ == 'P')
- colors[31] = 1;
-#ifdef ALLOCATE_HACK
- colors[28] = 1; /* for immediate allocation */
- colors[29] = 1; /* for immediate allocation */
- colors[30] = 1; /* for immediate allocation */
-#endif
- for (y = 0; y < n_symbols; y++) {
- if (! ig_test(x, y, n_symbols, graph))
- continue;
- r = unit->reglist[y];
- if ( r
- && r->color != -1
- && r->set == typ) {
- colors[r->color] = 1;
- }
- }
- for (color = free_colors = 0; color < MAX_COLOR; color++)
- if (!colors[color])
- free_colors++;
- return free_colors;
-}
-
-#if ! DOIT_AGAIN_SAM
/* update bb and life_info after spilling
* this saves 4 costy routines
* NOTE {lhs_,}use_count are not set again, this is save, when no
@@ -1064,61 +777,10 @@
dump_symreg(unit);
}
}
-/*
- * update the interference_graph too and don't call
- * build_interference_graph again
- */
-
-static void
-update_interference(Parrot_Interp interpreter, IMC_Unit * unit,
- SymReg *old, SymReg *new)
-{
- int x, y, n_symbols;
- SymReg ** reglist = unit->reglist;
- unsigned int *interference_graph = unit->interference_graph;
-
- n_symbols = unit->n_symbols;
- if (old != new) {
- /* n_symbols is already increased */
- unsigned int *new_graph = ig_allocate(n_symbols);
- /* old symbols count of previous graph */
- int o = n_symbols - 1;
- for (x = 0; x < o; x++) {
- for (y = x + 1; y < o; y++) {
- if (ig_test(x, y, o, interference_graph)) {
- ig_set(x, y, n_symbols, new_graph);
- ig_set(y, x, n_symbols, new_graph);
- }
- }
- }
- free(interference_graph);
- unit->interference_graph = interference_graph = new_graph;
- }
- for (x = 0; x < n_symbols; x++) {
- for (y = x + 1; y < n_symbols; y++) {
- if (reglist[x] == old || reglist[x] == new ||
- reglist[y] == old || reglist[y] == new) {
- if (interferes(unit, reglist[x], reglist[y])) {
- ig_set(x, y, n_symbols, interference_graph);
- ig_set(y, x, n_symbols, interference_graph);
- }
- else {
- ig_clear(x, y, n_symbols, interference_graph);
- ig_clear(y, x, n_symbols, interference_graph);
- }
- }
- }
- }
- if (IMCC_INFO(interpreter)->debug & DEBUG_IMC) {
- fprintf(stderr, "old_sym %s\n", old->name);
- fprintf(stderr, "new_sym %s\n", new->name);
- dump_interference_graph(unit);
- }
-}
-#endif

/* Rewrites the unit instructions, inserting spill code in every ocurrence
- * of the symbol.
+ * of the symbol. XXX this has tremendous potential for optimization.
+ * Spilling multiple variables would help tremendously.
*/

static void
@@ -1128,15 +790,10 @@
int i, n, dl;
int needs_fetch, needs_store;
SymReg * old_sym, *p31, *new_sym;
- char * buf;
+ char buf[256];
SymReg *regs[IMCC_MAX_REGS];
SymReg **reglist = unit->reglist;

- buf = malloc(256 * sizeof(char));
- if (buf == NULL) {
- fatal(1, "spill","Out of mem\n");
- }
-
debug(interpreter, DEBUG_IMC, "#Spilling [%s]:\n", reglist[spilled]->name);

new_sym = old_sym = reglist[spilled];
@@ -1217,13 +874,9 @@
dl++;
}
if (needs_fetch || needs_store) {
-#if ! DOIT_AGAIN_SAM
/* update life info of prev sym */
update_life(interpreter, unit, ins, new_sym, needs_fetch,
needs_store,
old_sym != new_sym);
- /* and interference of both */
- update_interference(interpreter, unit, old_sym, new_sym);
-#endif
/* if all symbols are in one basic_block, we need a new
* symbol, so that the life_ranges are minimal
* It would be nice, to detect, when changing the symbol
@@ -1236,18 +889,303 @@
ins = tmp;
}
}
+ if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
+ dump_instructions(unit);
+}

- free(buf);

-#if DOIT_AGAIN_SAM
- /* update index */
- for (i = 0, ins = unit; ins; ins = ins->next) {
- ins->index = i++;
+
+/*********************************************************/
+/**** The below is to test the new graph algorithms. ****/
+/*********************************************************/
+
+/*
+ * Use colors from G to allocate registers and spill the high colors.
+ */
+static int
+spill_registers(Parrot_Interp interpreter, IMC_Unit * unit)
+{
+ int spilled=0,x=0;
+ Dbg("spill_registers n=%d\n", G.n);
+
+ for (x = 0; x < G.n; x++) {
+ int maxcol = MAX_COLOR;
+ if (unit->reglist[x]->set == 'P') maxcol--; /* for spilling into P31 */
+ if (G.V[x].col > maxcol) {
+ Dbg("SPILL_REGISTERS node %d, spilling ... regcol=%d\n",
+ x, G.V[x].col);
+ spill(interpreter, unit, x);
+ /* new spill symbols are always added to end of list >n,
+ * so that's how we can get away with this little hack. */
+ Dbg("SPILL_REGISTERS spilled node %d\n", x);
+ spilled++;
+ }
}
+ return spilled;
+}
+
+/*
+ * Use colors from G to allocate registers and spill the high colors.
+ */
+static void
+apply_coloring(IMC_Unit * unit)
+{
+ int j,x=0;
+ SymReg ** reglist = unit->reglist;
+ int maxcol = MAX_COLOR;
+ Dbg("apply_coloring n=%d\n", G.n);
+
+ for (j = 0; j < G.n; j++) {
+ x = G.D[j];
+ Dbg2("pondering node %d\n", x);
+ if (unit->reglist[x]->usage & U_SPILL) /* already spilled, done below
*/
+ continue;
+ if (!(unit->reglist[x]->simplified)) /* probably not used */
+ continue;
+
+ imcstack_push(nodeStack, x); /* XXX don't really need this */
+ unit->reglist[x]->simplified = 1; /* not actually needed any more */
+ }
+ /*
+ * now put all spilled regs on top of stack so that they
+ * get their register first
+ */
+ for (x = 0; x < unit->n_symbols; x++) {
+ if (unit->reglist[x]->usage & U_SPILL)
+ imcstack_push(nodeStack, x);
+ }
+
+ while ((imcstack_depth(nodeStack) > 0) ) {
+ x=imcstack_pop(nodeStack);
+ Dbg2("APPLY popped node %d\n", x);
+
+ if (G.V[x].col <= maxcol) {
+ reglist[x]->color = G.V[x].col - 1;
+ Dbg2("APPLY node %d, reg=%ld\n", x, reglist[x]->color);
+ } else {
+ fatal(1,"apply_coloring", "wants to use too high reg num");
+ }
+ }
+#ifndef NDEBUG
+ for (x = 0; x < G.n; x++) {
+ int y;
+ assert(reglist[x]->color >= 0);
+ assert(reglist[x]->color < MAX_COLOR);
+ assert(reglist[x]->color == G.V[x].col-1);
+ Dbg("%d (reg==%ld):",x, reglist[x]->color);
+ for (y = 0; y < G.n; y++)
+ if (ig_test(x, y, G.n, G.E)) {
+ Dbg(" %d(%ld)",y, reglist[y]->color);
+ assert(reglist[x]->color != reglist[y]->color);
+ }
+ Dbg("\n");
+ }
+ Dbg("\ncoloring applied and verified, for %d node graph (Chi==%d).\n\n",
G.n, G.k);
#endif
- if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
- dump_instructions(unit);
+}
+
+static int degree_comparator(const void * x, const void * y) {
+ return G.V[*(int*)x].deg < G.V[*(int*)y].deg ? -1 :
+ G.V[*(int*)x].deg == G.V[*(int*)y].deg ? 0 : 1;
+}
+
+static int ig_init_graph(int num_nodes, unsigned* edge_bits) {
+ int j,x,y;
+
+ G.n = num_nodes;
+ G.E = edge_bits;
+ G.k = 0;
+ G.V = (node*)calloc(G.n, sizeof(node));
+ G.D = (int*)calloc(G.n, sizeof(int)); /* to keep track of nodes by degee
*/
+
+ if (G.V ? G.D ? 0 : (free(G.V),1) : (1) )
+ return 0;
+
+ for (x = 0; x < num_nodes; x++) {
+ G.D[x] = x; /* put self into degree list */
+ for (y = 0; y < num_nodes; y++)
+ if (ig_test(x, y, num_nodes, edge_bits))
+ G.V[x].deg++; /* another neighbor of x is recorded */
+ assert(G.V[x].deg>=0 && G.V[x].deg<G.n);
+ }
+ qsort(G.D, G.n, sizeof(int), degree_comparator);
+ for (j = 0; j < num_nodes; j++) {
+ x = G.D[j];
+ G.V[x].col = 0; /* start uncolored */
+ G.V[x].rank = j;
+ G.V[x].in = 1;
+ }
+ return 1;
+}
+
+
+static void ig_clear_graph(void)
+{
+ free(G.V);
+ free(G.D);
+ G.n=0;
+ G.E=NULL;
+ G.k=0;
+ G.V=NULL;
+ G.D=NULL;
+}
+
+static void ig_remove_node(int x) { /* operates on global graph, G */
+ int j,y,v;
+ assert(G.V[x].deg>=0 && G.V[x].deg<G.n);
+ /*G.V[x].deg = 0; save for last assertion */
+ G.V[x].in = 0; /* set removal flag */
+ assert(G.V[x].rank==0 || !G.V[G.D[G.V[x].rank-1]].in);
+
+#ifdef NDEBUG
+ G.V[x].deg=0;
+#else
+ Dbg("del %d(%d)[rank=%d]:", x, G.V[x].deg, G.V[x].rank);
+ for (y = 0; y < G.n; y++) {
+ assert(G.D[G.V[y].rank]==y);
+ if (G.V[y].in && ig_test(x, y, G.n, G.E)) {
+ Dbg(" %d",y);
+ G.V[x].deg--; /*countdown degrees*/
+ }
+ }
+ Dbg("\n");
+ assert(G.V[x].deg==0);
+#endif
+ for (y = 0; y < G.n; y++)
+ if (G.V[y].in && ig_test(x, y, G.n, G.E)) { /* non-deleted neighbor */
+ int dy = G.V[y].deg--;
+ j = G.V[y].rank;
+ if (j==0 || G.V[G.D[j-1]].deg<dy) continue;
+ assert(G.D[j]==y);
+ while (j>0 && G.V[G.D[j-1]].in && G.V[G.D[j-1]].deg == dy) j--;
+ v = G.D[j]; /* these three lines exchange old & new
j */
+ assert(G.V[v].rank==j);
+ G.V[v].rank = G.V[y].rank; /* old position of y saved in new */
+ G.V[y].rank = j;
+ G.D[G.V[y].rank]=y;
+ G.D[G.V[v].rank]=v;
+ assert(j>0 ? G.V[y].deg >= G.V[G.D[j-1]].deg : 1);
+ assert(j<G.n-1 ? G.V[y].deg <= G.V[G.D[j+1]].deg : 1);
+ }
+}
+
+static int ig_color_node(int x) /* select first available color */
+{
+ int c,y;
+ /*
+ * TODO for higher optimization level:
+ * Nodes that get colored too high (ie spill) should look at each
+ * neighboring colored node's score. If some neighboring color has a
+ * lower spill cost and can change their color or spill, then the more
+ * expensive node can have it's color. The cost of swapping out a color
+ * can be recorded in the below array, making it easier to find the switch
+ * color. This scheme should then reduce the cost of spilling overall.
+ *
+ * Actually, this might be a good idea anyway, since spilling is even
+ * expensive to do! Lot's of substitutions, and it looks like having
+ * to redo the whole graph?
+ */
+ char* avail = (char*)alloca(+G.k+41); /* lazily use bytes for avail
colors */
+ memset(avail,1,G.k+41);
+ avail[32]=0; /*reserve spot for spill reg, p31: FIXME blocks S31,I31,N31*/
+ if (G.V[x].col)
+ Dbg2("ig_color_node color node %d, precolor col=%d\n", x, G.V[x].col);
+ for (y = 0; y < G.n; y++)
+ if (ig_test(x, y, G.n, G.E)) {
+ Dbg2("ig_color_node k=%d, rank %d, node %d, col=%d\n",
+ G.k, G.V[y].rank, y, G.V[y].col);
+ assert(0<=G.V[y].col && G.V[y].col<=G.k); /*uncolored is okay*/
+ avail[G.V[y].col] = 0;
+ }
+ if (G.V[x].col && avail[G.V[x].col]) {
+ c = G.V[x].col;
+ assert(avail[G.V[x].col]); /* verify no conflict */
+ } else {
+ if (G.V[x].col)
+ Dbg("WARNING - ig_color_node: cannot give requested color to "
+ "node %d, (%d)\n", x, G.V[x].col-1);
+ /* this looks stupid for a number of strange reasons... XXX pls clean
*/
+ for (c = 17; c < 31; c++) if (avail[c]) break;
+ if (!avail[c]) for (c = 31; c >= 1; c--) if (avail[c]) break;
+ if (!c)
+ for (c = 1; c <= G.k+2; c++) if (avail[c]) break;
+ G.V[x].col = c;
+ }
+ if (G.k<c) G.k = c;
+ Dbg2("ig_color_node color node %d, receives col=%d\n", x, G.V[x].col);
+ return c;
+}

+/*
+ * Set colors in G to pre-allocated values, from allocate_wanted_regs
+ */
+static void
+ig_precolor(IMC_Unit * unit)
+{
+ int j,x;
+ SymReg ** reglist = unit->reglist;
+ Dbg("COLORING GRAPH n=%d *****\n", G.n);
+ for (j = 0; j < G.n; j++) {
+ x = G.D[j];
+ G.V[x].col = 1 + reglist[x]->color;
+ if (!G.V[x].col && unit->reglist[x]->usage & U_SPILL) {
+ int c=1+MAX_COLOR/2, y=0; /* precolor spilled symbols */
+ while(y<G.n) {
+ for (y = 0; y < G.n; y++)
+ if (ig_test(x, y, G.n, G.E))
+ if (c == G.V[y].col) {c++; break;}
+ if (c>=MAX_COLOR)
+ fatal(1, "ig_precolor", "cannot precolor spilled symbol");
+ }
+ G.V[x].col=c;
+ Dbg("PRECOLORing spill-node %d as color %d\n",x,c);
+ }
+ assert(0<=G.V[x].col && G.V[x].col<=MAX_COLOR); /*uncolored is okay*/
+ if (G.V[x].col>G.k)
+ G.k = G.V[x].col;
+ reglist[x]->simplified = 1; /* not actually needed any more */
+ if(G.V[x].col)
+ Dbg("PRECOL rank %d, node %d, col=%d\n", G.V[x].rank, x,
G.V[x].col);
+ }
+}
+
+/* The Matula Maximum Minimum Degree coloring algorithm (Degeneracy Coloring).
+ *
+ * Sort by degrees, remove lowest degree nodes, adjust other degrees, iterate.
+ * This algorithm for coloring, was adapted by Chaiten for register allocation.
+ * Briggs later made more modifications. The interesting part is spilling,
+ * which really has nothing to do with theoretical graph coloring. Stay tuned
+ * to this channel as the saga continues ...
+ */
+
+static void
+ig_color_graph(void) {
+ int j,x;
+
+ Dbg("ig_color_graph n=%d\n", G.n);
+ for (j=0; j<G.n; j++) {
+ x = G.D[j];
+ ig_remove_node(x);
+ }
+
+ for (j=G.n-1; j>=0; j--) {
+ x = G.D[j];
+ ig_color_node(x);
+ Dbg2("rank %d, node %d, col=%d\n", G.V[x].rank, x, G.V[x].col);
+#ifndef NDEBUG
+ {
+ int y;
+ for (y = 0; y < G.n; y++) {
+ assert(G.D[G.V[y].rank]==y);
+ if (ig_test(x, y, G.n, G.E)) {
+ assert(G.V[x].col != G.V[y].col);
+ }
+ }
+ assert(G.V[x].deg==0);
+ }
+#endif
+ }
+ Dbg("finished coloring, k=%d\n", G.k);
}

/*
<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise