#include "irprintf.h"
#include "irbitset.h"
#include "error.h"
+#include "list.h"
#include "bearch.h"
#include "beifg.h"
#include "becopyopt_t.h"
#include "bemodule.h"
-DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
#define COL_COST_INFEASIBLE DBL_MAX
#define AFF_NEIGHBOUR_FIX_BENEFIT 128.0
#define NEIGHBOUR_CONSTR_COSTS 64.0
-#define DBG_AFF_CHUNK(env, level, chunk) DEBUG_ONLY(do { if (firm_dbg_get_mask(dbg) & (level)) dbg_aff_chunk((env), (chunk)); } while(0))
-#define DBG_COL_COST(env, level, cost) DEBUG_ONLY(do { if (firm_dbg_get_mask(dbg) & (level)) dbg_col_cost((env), (cost)); } while(0))
+
+#ifdef DEBUG_libfirm
+
+#define DBG_AFF_CHUNK(env, level, chunk) do { if (firm_dbg_get_mask(dbg) & (level)) dbg_aff_chunk((env), (chunk)); } while(0)
+#define DBG_COL_COST(env, level, cost) do { if (firm_dbg_get_mask(dbg) & (level)) dbg_col_cost((env), (cost)); } while(0)
+
+static firm_dbg_module_t *dbg = NULL;
+
+#else
+
+#define DBG_AFF_CHUNK(env, level, chunk)
+#define DBG_COL_COST(env, level, cost)
+
+#endif
static int last_chunk_id = 0;
/* stores coalescing related information for a node */
typedef struct _co_mst_irn_t {
- ir_node *irn; /**< the irn this information belongs to */
- aff_chunk_t *chunk; /**< the chunk this irn belongs to */
- bitset_t *adm_colors; /**< set of admissible colors for this irn */
- ir_node **int_neighs; /**< array of all interfering neighbours (cached for speed reasons) */
- int n_neighs; /**< length of the interfering neighbours array. */
- int int_aff_neigh; /**< number of interfering affinity neighbours */
- int col; /**< color currently assigned */
- int init_col; /**< the initial color */
- int tmp_col; /**< a temporary assigned color */
- unsigned fixed : 1; /**< the color is fixed */
- unsigned tmp_fixed : 1; /**< the color is temporary fixed */
+ ir_node *irn; /**< the irn this information belongs to */
+ aff_chunk_t *chunk; /**< the chunk this irn belongs to */
+ bitset_t *adm_colors; /**< set of admissible colors for this irn */
+ ir_node **int_neighs; /**< array of all interfering neighbours (cached for speed reasons) */
+ int n_neighs; /**< length of the interfering neighbours array. */
+ int int_aff_neigh; /**< number of interfering affinity neighbours */
+ int col; /**< color currently assigned */
+ int init_col; /**< the initial color */
+ int tmp_col; /**< a temporary assigned color */
+ unsigned fixed : 1; /**< the color is fixed */
+ struct list_head list; /**< Queue for coloring undo. */
} co_mst_irn_t;
#define get_co_mst_irn(mst_env, irn) (phase_get_or_set_irn_data(&(mst_env)->ph, (irn)))
* Write a chunk to stderr for debugging.
*/
static void dbg_aff_chunk(const co_mst_env_t *env, const aff_chunk_t *c) {
- int idx;
+ bitset_pos_t idx;
if (c->weight_consistent)
ir_fprintf(stderr, " $%d ", c->weight);
ir_fprintf(stderr, "{");
* Dump all admissible colors to stderr.
*/
static void dbg_admissible_colors(const co_mst_env_t *env, const co_mst_irn_t *node) {
- int idx;
+ bitset_pos_t idx;
+ (void) env;
+
if (bitset_popcnt(node->adm_colors) < 1)
fprintf(stderr, "no admissible colors?!?");
else {
#endif /* DEBUG_libfirm */
static INLINE int get_mst_irn_col(const co_mst_irn_t *node) {
- return node->tmp_fixed ? node->tmp_col : node->col;
+ return node->tmp_col >= 0 ? node->tmp_col : node->col;
}
/**
* Always returns true.
*/
static int decider_always_yes(const co_mst_irn_t *node, int col) {
+ (void) node;
+ (void) col;
return 1;
}
res->irn = irn;
res->chunk = NULL;
res->fixed = 0;
- res->tmp_fixed = 0;
res->tmp_col = -1;
res->int_neighs = NULL;
res->int_aff_neigh = 0;
res->col = arch_register_get_index(arch_get_irn_register(env->aenv, irn));
res->init_col = res->col;
+ INIT_LIST_HEAD(&res->list);
DB((dbg, LEVEL_4, "Creating phase info for %+F\n", irn));
* Check if affinity chunk @p chunk interferes with node @p irn.
*/
static INLINE int aff_chunk_interferes(co_mst_env_t *env, const aff_chunk_t *chunk, ir_node *irn) {
+ (void) env;
return bitset_is_set(chunk->interfere, get_irn_idx(irn));
}
aff_chunk_t *c1 = get_aff_chunk(env, src);
aff_chunk_t *c2 = get_aff_chunk(env, tgt);
- DEBUG_ONLY(
+#ifdef DEBUG_libfirm
DB((dbg, LEVEL_4, "Attempt to let c1 (id %d): ", c1 ? c1->id : -1));
if (c1) {
DBG_AFF_CHUNK(env, LEVEL_4, c1);
DB((dbg, LEVEL_4, "{%+F}", tgt));
}
DB((dbg, LEVEL_4, "\n"));
- )
+#endif
if (c1 == NULL) {
if (c2 == NULL) {
affinity_node_t *am = get_affinity_info(env->co, m);
n2->int_aff_neigh = count_interfering_aff_neighs(env, am);
}
+ /*
+ * these weights are pure hackery ;-).
+ * It's not chriswue's fault but mine.
+ */
edge.weight = (double)neigh->costs / (double)(1 + n1->int_aff_neigh + n2->int_aff_neigh);
ARR_APP1(aff_edge_t, edges, edge);
}
* Resets the temporary fixed color of all nodes within wait queue @p nodes.
* ATTENTION: the queue is empty after calling this function!
*/
-static INLINE void reject_coloring(waitq *nodes) {
+static INLINE void reject_coloring(struct list_head *nodes) {
+ co_mst_irn_t *n, *temp;
DB((dbg, LEVEL_4, "\treject coloring for"));
- while (! waitq_empty(nodes)) {
- co_mst_irn_t *n = waitq_get(nodes);
+ list_for_each_entry_safe(co_mst_irn_t, n, temp, nodes, list) {
DB((dbg, LEVEL_4, " %+F", n->irn));
- n->tmp_fixed = 0;
+ assert(n->tmp_col >= 0);
+ n->tmp_col = -1;
+ list_del_init(&n->list);
}
DB((dbg, LEVEL_4, "\n"));
}
+static INLINE void materialize_coloring(struct list_head *nodes) {
+ co_mst_irn_t *n, *temp;
+ list_for_each_entry_safe(co_mst_irn_t, n, temp, nodes, list) {
+ assert(n->tmp_col >= 0);
+ n->col = n->tmp_col;
+ n->tmp_col = -1;
+ list_del_init(&n->list);
+ }
+}
+
+static INLINE void set_temp_color(co_mst_irn_t *node, int col, struct list_head *changed)
+{
+ assert(col >= 0);
+ assert(!node->fixed);
+ assert(node->tmp_col < 0);
+ assert(node->list.next == &node->list && node->list.prev == &node->list);
+
+ list_add_tail(&node->list, changed);
+ node->tmp_col = col;
+}
+
+static INLINE int is_loose(co_mst_irn_t *node)
+{
+ return !node->fixed && node->tmp_col < 0;
+}
+
/**
* Determines the costs for each color if it would be assigned to node @p node.
*/
static void determine_color_costs(co_mst_env_t *env, co_mst_irn_t *node, col_cost_t *costs) {
affinity_node_t *an = get_affinity_info(env->co, node->irn);
neighb_t *aff_neigh;
- int idx, i;
+ bitset_pos_t idx;
+ int i;
col_cost_init(env, costs, 0.0);
c = (double)aff_neigh->costs;
/* calculate costs for fixed affinity neighbours */
- if (neigh->tmp_fixed || neigh->fixed) {
+ if (!is_loose(neigh)) {
int col = get_mst_irn_col(neigh);
costs[col].cost -= c * AFF_NEIGHBOUR_FIX_BENEFIT;
}
int_neigh = node->int_neighs[i];
- /* skip ignore nodes */
- if (arch_irn_is(env->aenv, int_neigh, ignore))
- continue;
+ assert(!arch_irn_is(env->aenv, int_neigh, ignore));
neigh = get_co_mst_irn(env, int_neigh);
col = get_mst_irn_col(neigh);
col_cnt = bitset_popcnt(neigh->adm_colors);
- if (neigh->tmp_fixed || neigh->fixed) {
+ if (!is_loose(neigh)) {
/* colors of fixed interfering neighbours are infeasible */
costs[col].cost = COL_COST_INFEASIBLE;
}
}
}
}
+
+ DB((dbg, LEVEL_4, "\tneigh %+F, loose: %d, color: %d\n", int_neigh, is_loose(neigh), col));
}
/* set all not admissible colors to COL_COST_INFEASIBLE */
}
/* need forward declaration due to recursive call */
-static int recolor_nodes(co_mst_env_t *env, co_mst_irn_t *node, col_cost_t *costs, waitq *changed_ones);
+static int recolor_nodes(co_mst_env_t *env, co_mst_irn_t *node, col_cost_t *costs, struct list_head *changed_ones);
/**
* Tries to change node to a color but @p explude_col.
* @return 1 if succeeded, 0 otherwise.
*/
-static int change_node_color_excluded(co_mst_env_t *env, co_mst_irn_t *node, int exclude_col, waitq *changed_ones) {
+static int change_node_color_excluded(co_mst_env_t *env, co_mst_irn_t *node, int exclude_col, struct list_head *changed_ones) {
int col = get_mst_irn_col(node);
int res = 0;
/* neighbours has already a different color -> good, temporary fix it */
if (col != exclude_col) {
- node->tmp_fixed = 1;
- node->tmp_col = col;
- waitq_put(changed_ones, node);
+ if (is_loose(node))
+ set_temp_color(node, col, changed_ones);
return 1;
}
/* The node has the color it should not have _and_ has not been visited yet. */
- if (! (node->tmp_fixed || node->fixed)) {
+ if (is_loose(node)) {
col_cost_t *costs = alloca(env->n_regs * sizeof(costs[0]));
/* Get the costs for giving the node a specific color. */
* ATTENTION: Expect @p costs already sorted by increasing costs.
* @return 1 if coloring could be applied, 0 otherwise.
*/
-static int recolor_nodes(co_mst_env_t *env, co_mst_irn_t *node, col_cost_t *costs, waitq *changed_ones) {
+static int recolor_nodes(co_mst_env_t *env, co_mst_irn_t *node, col_cost_t *costs, struct list_head *changed_ones) {
int i;
- waitq *local_changed = new_waitq();
- waitq *tmp = new_waitq();
+ struct list_head local_changed;
DBG((dbg, LEVEL_1, "\tRecoloring %+F with color-costs", node->irn));
DBG_COL_COST(env, LEVEL_1, costs);
/* If the costs for that color (and all successive) are infinite, bail out we won't make it anyway. */
if (costs[i].cost == COL_COST_INFEASIBLE) {
- node->tmp_fixed = 0;
- del_waitq(local_changed);
- del_waitq(tmp);
return 0;
}
/* Set the new color of the node and mark the node as temporarily fixed. */
- assert(! node->tmp_fixed && "Node must not have been temporary fixed.");
- node->tmp_fixed = 1;
- node->tmp_col = tgt_col;
+ assert(node->tmp_col < 0 && "Node must not have been temporary fixed.");
+ INIT_LIST_HEAD(&local_changed);
+ set_temp_color(node, tgt_col, &local_changed);
DBG((dbg, LEVEL_4, "\tTemporary setting %+F to color %d\n", node->irn, tgt_col));
- assert(waitq_empty(local_changed) && "Node queue should be empty here.");
- waitq_put(local_changed, node);
-
/* try to color all interfering neighbours with current color forbidden */
for (j = 0; j < node->n_neighs; ++j) {
co_mst_irn_t *nn;
continue;
nn = get_co_mst_irn(env, neigh);
- DB((dbg, LEVEL_4, "\tHandling neighbour %+F, at position %d (fixed: %d, tmp_fixed: %d, tmp_col: %d, col: %d)\n",
- neigh, j, nn->fixed, nn->tmp_fixed, nn->tmp_col, nn->col));
+ DB((dbg, LEVEL_4, "\tHandling neighbour %+F, at position %d (fixed: %d, tmp_col: %d, col: %d)\n",
+ neigh, j, nn->fixed, nn->tmp_col, nn->col));
/*
Try to change the color of the neighbor and record all nodes which
*/
if (get_mst_irn_col(nn) == tgt_col) {
/* try to color neighbour with tgt_col forbidden */
- neigh_ok = change_node_color_excluded(env, nn, tgt_col, tmp);
+ neigh_ok = change_node_color_excluded(env, nn, tgt_col, &local_changed);
- /* join lists of changed nodes */
- while (! waitq_empty(tmp))
- waitq_put(local_changed, waitq_get(tmp));
-
- if (! neigh_ok)
+ if (!neigh_ok)
break;
}
}
*/
if (neigh_ok) {
/* append the local_changed ones to global ones */
- while (! waitq_empty(local_changed))
- waitq_put(changed_ones, waitq_get(local_changed));
- del_waitq(local_changed);
- del_waitq(tmp);
+ list_splice(&local_changed, changed_ones);
return 1;
}
else {
/* coloring of neighbours failed, so we try next color */
- reject_coloring(local_changed);
+ reject_coloring(&local_changed);
}
}
- del_waitq(local_changed);
- del_waitq(tmp);
return 0;
}
* Tries to bring node @p node and all it's neighbours to color @p tgt_col.
* @return 1 if color @p col could be applied, 0 otherwise
*/
-static int change_node_color(co_mst_env_t *env, co_mst_irn_t *node, int tgt_col, waitq *changed_ones) {
+static int change_node_color(co_mst_env_t *env, co_mst_irn_t *node, int tgt_col, struct list_head *changed_ones) {
int col = get_mst_irn_col(node);
/* if node already has the target color -> good, temporary fix it */
if (col == tgt_col) {
DBG((dbg, LEVEL_4, "\t\tCNC: %+F has already color %d, fix temporary\n", node->irn, tgt_col));
- if (! node->tmp_fixed) {
- node->tmp_fixed = 1;
- node->tmp_col = tgt_col;
- waitq_put(changed_ones, node);
- }
+ if (is_loose(node))
+ set_temp_color(node, tgt_col, changed_ones);
return 1;
}
Node has not yet a fixed color and target color is admissible
-> try to recolor node and it's affinity neighbours
*/
- if (! (node->fixed || node->tmp_fixed) && bitset_is_set(node->adm_colors, tgt_col)) {
+ if (is_loose(node) && bitset_is_set(node->adm_colors, tgt_col)) {
col_cost_t *costs = alloca(env->n_regs * sizeof(costs[0]));
int res;
return res;
}
- DEBUG_ONLY(
+#ifdef DEBUG_libfirm
if (firm_dbg_get_mask(dbg) & LEVEL_4) {
- if (node->fixed || node->tmp_fixed)
+ if (!is_loose(node))
DB((dbg, LEVEL_4, "\t\tCNC: %+F has already fixed color %d\n", node->irn, col));
else {
DB((dbg, LEVEL_4, "\t\tCNC: color %d not admissible for %+F (", tgt_col, node->irn));
DB((dbg, LEVEL_4, ")\n"));
}
}
- )
+#endif
return 0;
}
static void color_aff_chunk(co_mst_env_t *env, aff_chunk_t *c) {
aff_chunk_t *best_chunk = NULL;
int best_color = -1;
- waitq *changed_ones = new_waitq();
+ int did_all = 0;
waitq *tmp_chunks = new_waitq();
waitq *best_starts = NULL;
bitset_t *visited;
int col, idx, len;
+ struct list_head changed_ones;
DB((dbg, LEVEL_2, "fragmentizing chunk #%d", c->id));
DBG_AFF_CHUNK(env, LEVEL_2, c);
DB((dbg, LEVEL_2, "\n"));
- /* check which color is the "best" for the given chunk */
- for (col = 0; col < env->n_regs; ++col) {
+ /* check which color is the "best" for the given chunk.
+ * if we found a color which was ok for all nodes, we take it
+ * and do not look further. (see did_all flag usage below.)
+ * If we have many colors which fit all nodes it is hard to decide
+ * which one to take anyway.
+ * TODO Sebastian: Perhaps we should at all nodes and figure out
+ * a suitable color using costs as done above (determine_color_costs).
+ */
+ for (col = 0; col < env->n_regs && !did_all; ++col) {
int one_good = 0;
waitq *good_starts = new_waitq();
aff_chunk_t *local_best;
DB((dbg, LEVEL_3, "\ttrying color %d\n", col));
+ /* suppose we can color all nodes to the same color */
+ did_all = 1;
+
+ INIT_LIST_HEAD(&changed_ones);
+
/* try to bring all nodes of given chunk to the current color. */
for (idx = 0, len = ARR_LEN(c->n); idx < len; ++idx) {
ir_node *irn = c->n[idx];
The order of the colored nodes is important, so we record the successfully
colored ones in the order they appeared.
*/
- good = change_node_color(env, node, col, changed_ones);
- if (good)
+ good = change_node_color(env, node, col, &changed_ones);
+ if (good) {
waitq_put(good_starts, node);
+ }
+
one_good |= good;
+ did_all &= good;
DB((dbg, LEVEL_4, "\t\t... %+F attempt from %d to %d %s\n", irn, node->col, col, one_good ? "succeeded" : "failed"));
}
/* try next color when failed */
- if (! one_good)
+ if (! one_good) {
+ reject_coloring(&changed_ones);
continue;
+ }
/* fragment the chunk according to the coloring */
local_best = fragment_chunk(env, col, c, tmp_chunks);
del_waitq(good_starts);
}
- /* reject the coloring and bring the coloring to the initial state */
- reject_coloring(changed_ones);
+ reject_coloring(&changed_ones);
}
/* free all intermediate created chunks except best one */
/* return if coloring failed */
if (! best_chunk) {
- del_waitq(changed_ones);
if (best_starts)
del_waitq(best_starts);
return;
DBG_AFF_CHUNK(env, LEVEL_2, best_chunk);
DB((dbg, LEVEL_2, "using color %d\n", best_color));
- /* get the best fragment from the best list and color it */
- while (! waitq_empty(best_starts)) {
- co_mst_irn_t *node = waitq_get(best_starts);
- int res;
-
- if (! bitset_is_set(best_chunk->nodes, get_irn_idx(node->irn)))
- continue;
-
- res = change_node_color(env, node, best_color, changed_ones);
- if (! res)
- panic("Color manifesting failed for %+F, color %d in chunk %d\n", node->irn, best_color, best_chunk->id);
- node->fixed = 1;
- node->chunk = best_chunk;
- }
- /* we colored the successful start nodes, now color the rest of the chunk */
+ INIT_LIST_HEAD(&changed_ones);
for (idx = 0, len = ARR_LEN(best_chunk->n); idx < len; ++idx) {
ir_node *irn = best_chunk->n[idx];
co_mst_irn_t *node = get_co_mst_irn(env, irn);
- int res;
-
- res = change_node_color(env, node, best_color, changed_ones);
- if (! res)
- panic("Color manifesting failed for %+F, color %d in chunk %d\n", irn, best_color, best_chunk->id);
- DB((dbg, LEVEL_4, "\tManifesting color %d for %+F, chunk #%d\n", best_color, irn, best_chunk->id));
- node->fixed = 1;
- node->chunk = best_chunk;
- }
-
- /* materialize colors on changed nodes */
- while (! waitq_empty(changed_ones)) {
- co_mst_irn_t *n = waitq_get(changed_ones);
- n->tmp_fixed = 0;
- n->col = n->tmp_col;
+ int res;
+
+ /* bring the node to the color. */
+ DB((dbg, LEVEL_4, "\tManifesting color %d for %+F, chunk #%d\n", best_color, node->irn, best_chunk->id));
+ INIT_LIST_HEAD(&changed_ones);
+ res = change_node_color(env, node, best_color, &changed_ones);
+ if (res) {
+ materialize_coloring(&changed_ones);
+ node->fixed = 1;
+ }
}
/* remove the nodes in best chunk from original chunk */
/* clear obsolete chunks and free some memory */
delete_aff_chunk(env, best_chunk);
bitset_free(visited);
- del_waitq(changed_ones);
if (best_starts)
del_waitq(best_starts);
}
if (arch_irn_is(mst_env.aenv, irn, ignore))
continue;
- assert(mirn->fixed && "Node should have fixed color");
+ // assert(mirn->fixed && "Node should have fixed color");
/* skip nodes where color hasn't changed */
if (mirn->init_col == mirn->col)