* - merge multiple start worksets of blocks ~ok
* - how to and when to do the tentative phase...
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
#include <stdbool.h>
#include "besched_t.h"
#include "be_t.h"
+#ifndef NDEBUG
+#define EXPENSIVE_CHECKS
+#endif
+
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
typedef struct loop_edge_t loop_edge_t;
ir_loop *unused_livethrough_loop;
};
+#define foreach_worklist(entry, wl) list_for_each_entry(worklist_entry_t, entry, &(wl)->live_values, head)
+
typedef struct worklist_t worklist_t;
struct worklist_t {
struct list_head live_values;
size_t n_live_values;
+ ir_visited_t visited;
};
typedef struct block_info_t block_info_t;
worklist_t *end_worklist;
};
-static const arch_env_t *arch_env;
+/** The register class we currently run on. */
static const arch_register_class_t *cls;
static struct obstack obst;
static spill_env_t *senv;
static size_t max_register_pressure;
static bool tentative_mode;
static bool should_have_reached_fixpoint;
+static bool do_push_unused_livethroughs;
+/** Execution frequency for the current graph. */
static ir_exec_freq *exec_freq;
+static ir_visited_t worklist_visited;
static worklist_t *new_worklist(void)
{
static void deactivate_worklist(const worklist_t *worklist)
{
- struct list_head *entry;
+ worklist_entry_t *entry;
- list_for_each(entry, &worklist->live_values) {
- worklist_entry_t *wl_entry
- = list_entry(entry, worklist_entry_t, head);
- assert(worklist_contains(wl_entry->value));
- mark_irn_not_visited(wl_entry->value);
- set_irn_link(wl_entry->value, NULL);
+ foreach_worklist(entry, worklist) {
+ assert(worklist_contains(entry->value));
+ mark_irn_not_visited(entry->value);
+ set_irn_link(entry->value, NULL);
}
}
static void activate_worklist(const worklist_t *worklist)
{
- struct list_head *entry;
+ worklist_entry_t *entry;
- list_for_each(entry, &worklist->live_values) {
- worklist_entry_t *wl_entry
- = list_entry(entry, worklist_entry_t, head);
- assert(!worklist_contains(wl_entry->value));
- mark_irn_visited(wl_entry->value);
- set_irn_link(wl_entry->value, wl_entry);
+ foreach_worklist(entry, worklist) {
+ assert(!worklist_contains(entry->value));
+ mark_irn_visited(entry->value);
+ set_irn_link(entry->value, entry);
}
}
/**
* duplicate a worklist and directly activates it
*/
-static worklist_t *duplicate_activate_worklist(const worklist_t *worklist,
- ir_node *block, ir_node *succ_block, int succ_pos)
+static void fill_and_activate_worklist(worklist_t *new_worklist,
+ const worklist_t *worklist, ir_node *block, ir_node *succ_block,
+ int succ_pos)
{
ir_node *reload_point = NULL;
size_t n_live_values = 0;
- worklist_t *new_worklist;
- struct list_head *entry;
+ worklist_entry_t *entry;
if (succ_block != NULL &&
(get_Block_n_cfgpreds(succ_block) > 1
reload_point = be_get_end_of_block_insertion_point(block);
}
- new_worklist = obstack_alloc(&obst, sizeof(new_worklist[0]));
- INIT_LIST_HEAD(&new_worklist->live_values);
-
- list_for_each(entry, &worklist->live_values) {
- worklist_entry_t *wl_entry = list_entry(entry, worklist_entry_t, head);
- ir_node *value = wl_entry->value;
+ foreach_worklist(entry, worklist) {
+ ir_node *value = entry->value;
worklist_entry_t *new_entry;
+ if (new_worklist->n_live_values >= n_regs)
+ break;
+
if (is_Phi(value) && get_nodes_block(value) == succ_block) {
value = get_Phi_pred(value, succ_pos);
/* can happen for unknown phi preds */
- if (!arch_irn_consider_in_reg_alloc(arch_env, cls, value))
+ if (!arch_irn_consider_in_reg_alloc(cls, value))
continue;
}
+ if (irn_visited_else_mark(value))
+ continue;
+
new_entry = obstack_alloc(&obst, sizeof(new_entry[0]));
memset(new_entry, 0, sizeof(new_entry[0]));
if (reload_point != NULL) {
new_entry->reload_point = reload_point;
} else {
- new_entry->reload_point = wl_entry->reload_point;
+ new_entry->reload_point = entry->reload_point;
}
- if (irn_not_visited(value)) {
- list_add_tail(&new_entry->head, &new_worklist->live_values);
- ++n_live_values;
+ list_add_tail(&new_entry->head, &new_worklist->live_values);
+ ++n_live_values;
- mark_irn_visited(value);
- set_irn_link(value, new_entry);
- }
+ set_irn_link(value, new_entry);
+ new_worklist->n_live_values++;
}
- new_worklist->n_live_values = n_live_values;
-
- return new_worklist;
}
static worklist_t *duplicate_worklist(const worklist_t *worklist)
struct list_head *entry;
new_worklist = obstack_alloc(&obst, sizeof(new_worklist[0]));
+ memset(new_worklist, 0, sizeof(new_worklist[0]));
INIT_LIST_HEAD(&new_worklist->live_values);
new_worklist->n_live_values = worklist->n_live_values;
#endif
-
+/**
+ * Clear the link fields of a loop and all
+ * its child loops.
+ *
+ * @param loop the root loop
+ */
static void clear_loop_info(ir_loop *loop)
{
int n_elements = get_loop_n_elements(loop);
edge->pos = i;
if (is_exit_edge) {
- ir_fprintf(stderr, "Loop %p exit edge %+F:%d\n",
- l, block, i);
edge->next = l_info->exit_edges;
l_info->exit_edges = edge;
assert(get_loop_depth(loop) < get_loop_depth(l));
} else {
- ir_fprintf(stderr, "Loop %p entry edge %+F:%d\n",
- l, block, i);
edge->next = l_info->entry_edges;
l_info->entry_edges = edge;
assert(get_loop_depth(cfgpred_loop) < get_loop_depth(l));
}
l = get_loop_outer_loop(l);
- } while(l != goal);
+ } while (l != goal);
}
}
/* already in the worklist? move around, otherwise add at back */
worklist_entry_t *entry = get_irn_link(value);
- assert(arch_irn_consider_in_reg_alloc(arch_env, cls, value));
+ assert(arch_irn_consider_in_reg_alloc(cls, value));
if (worklist_contains(value)) {
assert(entry != NULL);
static void worklist_remove(worklist_t *worklist, ir_node *value)
{
worklist_entry_t *entry = get_irn_link(value);
- ir_fprintf(stderr, "removing %+F\n", value);
assert(entry != NULL);
list_del(&entry->head);
--worklist->n_live_values;
if (worklist_contains(node2))
continue;
- if (!arch_irn_consider_in_reg_alloc(arch_env, cls, node2))
+ if (!arch_irn_consider_in_reg_alloc(cls, node2))
continue;
if (!tentative_mode)
foreach_out_edge(node, edge) {
ir_node *proj = get_edge_src_irn(edge);
- if (!arch_irn_consider_in_reg_alloc(arch_env, cls, proj))
+ if (!arch_irn_consider_in_reg_alloc(cls, proj))
continue;
if (worklist_contains(proj)) {
worklist_remove(worklist, proj);
++n_defs;
}
}
- } else if (arch_irn_consider_in_reg_alloc(arch_env, cls, node)) {
+ } else if (arch_irn_consider_in_reg_alloc(cls, node)) {
if (worklist_contains(node)) {
worklist_remove(worklist, node);
} else {
for(i = 0; i < arity; ++i) {
ir_node *use = get_irn_n(node, i);
- if (!arch_irn_consider_in_reg_alloc(arch_env, cls, use))
+ if (!arch_irn_consider_in_reg_alloc(cls, use))
continue;
val_used(worklist, use, node);
return true;
}
-static void process_block(ir_node *block, void *env)
+static bool fill_start_worklist(worklist_t *new_worklist, ir_node *block)
{
- block_info_t *block_info = NULL;
- worklist_t *worklist = NULL;
double best_execfreq = -1;
+ worklist_t *best_worklist = NULL;
ir_node *best_succ_block = NULL;
- int best_pos = -1;
- int n_preds;
+ int best_pos;
const ir_edge_t *edge;
- (void) env;
- DB((dbg, LEVEL_1, "Processing %+F\n", block));
-
/* construct worklist */
foreach_block_succ(block, edge) {
- ir_node *succ_block = get_edge_src_irn(edge);
- double execfreq = get_block_execfreq(exec_freq, succ_block);
-
- if (execfreq > best_execfreq) {
- block_info_t *block_info = get_block_info(succ_block);
- worklist_t *succ_worklist = block_info->start_worklist;
- if (succ_worklist != NULL) {
- best_execfreq = execfreq;
- worklist = succ_worklist;
- best_succ_block = succ_block;
- best_pos = get_edge_src_pos(edge);
- }
- }
+ ir_node *succ_block = get_edge_src_irn(edge);
+ double execfreq = get_block_execfreq(exec_freq, succ_block);
+ block_info_t *block_info;
+ worklist_t *succ_worklist;
+
+ if (execfreq < best_execfreq)
+ continue;
+
+ block_info = get_block_info(succ_block);
+ succ_worklist = block_info->start_worklist;
+
+ if (succ_worklist == NULL || succ_worklist->visited >= worklist_visited)
+ continue;
+
+ best_execfreq = execfreq;
+ best_worklist = succ_worklist;
+ best_succ_block = succ_block;
+ best_pos = get_edge_src_pos(edge);
}
- if (worklist == NULL) {
- /* at least 1 successor block must have been processed unless it was
- * the end block */
- assert(tentative_mode || block == get_irg_end_block(get_irn_irg(block)));
- worklist = new_worklist();
- } else {
- worklist = duplicate_activate_worklist(worklist, block, best_succ_block,
- best_pos);
+ if (best_worklist == NULL)
+ return false;
+ best_worklist->visited = worklist_visited;
+
+ fill_and_activate_worklist(new_worklist, best_worklist, block,
+ best_succ_block, best_pos);
+ return true;
+}
+
+static worklist_t *construct_start_worklist(ir_node *block)
+{
+ worklist_t *worklist = new_worklist();
+
+ ++worklist_visited;
+
+ while(fill_start_worklist(worklist, block)) {
+ if (worklist->n_live_values >= n_regs)
+ break;
}
+ return worklist;
+}
+
+static void process_block(ir_node *block, void *env)
+{
+ block_info_t *block_info;
+ worklist_t *worklist;
+ int n_preds;
+
+ (void) env;
+ DB((dbg, LEVEL_1, "Processing %+F\n", block));
+
+ worklist = construct_start_worklist(block);
block_info = get_block_info(block);
#ifdef DEBUG_libfirm
/* we shouldn't have any live values left at the start block */
n_preds = get_Block_n_cfgpreds(block);
- assert(n_preds != 0 || worklist->n_live_values == 0);
+ //assert(n_preds != 0 || worklist->n_live_values == 0);
}
-typedef struct block_or_loop_t block_or_loop_t;
-struct block_or_loop_t {
- union {
- ir_node *block;
- ir_loop *loop;
- } v;
- bool is_loop;
+typedef union block_or_loop_t block_or_loop_t;
+union block_or_loop_t {
+ ir_node *block;
+ ir_loop *loop;
};
static block_or_loop_t *loop_blocks;
if (Block_block_visited(some_block))
return;
- block_or_loop.v.loop = loop;
- block_or_loop.is_loop = true;
+ block_or_loop.loop = loop;
ARR_APP1(block_or_loop_t, loop_blocks, block_or_loop);
#ifndef NDEBUG
loop_edge_t *edge = loop_info->entry_edges;
bool found = false;
for ( ; edge != NULL; edge = edge->next) {
- if (edge->block == entry)
+ if (edge->block == entry) {
found = true;
+ break;
+ }
}
assert(found);
}
+#else
+ (void) entry;
#endif
+
/* check all loop successors */
for (edge = loop_info->exit_edges; edge != NULL; edge = edge->next) {
ir_node *succ = edge->block;
if (Block_block_visited(block))
return;
- block_or_loop.v.block = block;
- block_or_loop.is_loop = false;
+ block_or_loop.block = block;
ARR_APP1(block_or_loop_t, loop_blocks, block_or_loop);
mark_Block_block_visited(block);
ir_node *reload_point,
ir_loop *unused_livethrough_loop)
{
- worklist_entry_t *entry = obstack_alloc(&obst, sizeof(entry[0]));
+ worklist_entry_t *entry;
+
+#ifdef EXPENSIVE_CHECKS
+ foreach_worklist(entry, worklist) {
+ assert(entry->value != value);
+ }
+#endif
+
+ entry = obstack_alloc(&obst, sizeof(*entry));
memset(entry, 0, sizeof(entry[0]));
entry->value = value;
static void push_unused_livethrough(loop_info_t *loop_info, ir_node *value)
{
+ loop_edge_t *edge;
+ ++worklist_visited;
+
/* add the value to all loop exit and entry blocks */
- loop_edge_t *edge = loop_info->exit_edges;
- for ( ; edge != NULL; edge = edge->next) {
+ for (edge = loop_info->exit_edges; edge != NULL; edge = edge->next) {
ir_node *block
= get_Block_cfgpred_block(edge->block, edge->pos);
const block_info_t *info = get_block_info(block);
worklist_t *worklist = info->end_worklist;
+ ir_node *reload_point = NULL;
+
+ if (worklist->visited >= worklist_visited)
+ continue;
+ worklist->visited = worklist_visited;
+
/* TODO: we need a smarter mechanism here, that makes the reloader place
* reload nodes on all loop exits... */
- ir_node *reload_point = NULL;
worklist_append(worklist, value, reload_point, loop_info->loop);
}
ir_node *entry_block = edge->block;
const block_info_t *info = get_block_info(entry_block);
worklist_t *worklist = info->start_worklist;
+ ir_node *pred_block;
+ ir_node *reload_point;
- ir_node *pred_block
- = get_Block_cfgpred_block(entry_block, edge->pos);
- ir_node *reload_point = be_get_end_of_block_insertion_point(pred_block);
+ if (worklist->visited >= worklist_visited)
+ continue;
+ worklist->visited = worklist_visited;
+
+ pred_block = get_Block_cfgpred_block(entry_block, edge->pos);
+ reload_point = be_get_end_of_block_insertion_point(pred_block);
worklist_append(worklist, value, reload_point, loop_info->loop);
}
= list_entry(entry, worklist_entry_t, head);
ir_node *value = wl_entry->value;
+ if (loop_info->max_register_pressure >= n_regs)
+ break;
+
+
if (!worklist_contains(value)) {
/* add it to all loop exits */
- ir_fprintf(stderr, "Found unused livethrough: %+F (regpressure in loop %d)\n", value, loop_info->max_register_pressure);
+ DB((dbg, LEVEL_2, "Found unused livethrough: %+F (regpressure in loop %d)\n", value, loop_info->max_register_pressure));
push_unused_livethrough(loop_info, value);
+ /* the value should now be part of end_worklist, so mark it */
mark_irn_visited(value);
}
-
- if (loop_info->max_register_pressure >= n_regs)
- break;
}
+
deactivate_worklist(end_worklist);
}
}
-static void process_block_or_loop(const block_or_loop_t *block_or_loop)
+static void process_block_or_loop(const block_or_loop_t block_or_loop)
{
- if (block_or_loop->is_loop) {
- loop_info_t *loop_info = get_loop_info(block_or_loop->v.loop);
+ if (block_or_loop.loop->kind == k_ir_loop) {
+ loop_info_t *loop_info = get_loop_info(block_or_loop.loop);
+
+ if (do_push_unused_livethroughs)
+ push_unused_livethroughs(loop_info);
if (loop_info->max_register_pressure > max_register_pressure)
max_register_pressure = loop_info->max_register_pressure;
- push_unused_livethroughs(loop_info);
return;
}
- process_block(block_or_loop->v.block, NULL);
+ process_block(block_or_loop.block, NULL);
}
static void process_loop(ir_loop *loop)
some_block = get_irg_start_block(current_ir_graph);
}
- loop_blocks = NEW_ARR_F(block_or_loop_t,0);
+ loop_blocks = NEW_ARR_F(block_or_loop_t, 0);
current_loop = loop;
ir_reserve_resources(current_ir_graph, IR_RESOURCE_BLOCK_VISITED);
}
ir_free_resources(current_ir_graph, IR_RESOURCE_BLOCK_VISITED);
- fprintf(stderr, "Block List for loop %p\n", loop);
+ DB((dbg, LEVEL_3, "Block List for loop %p\n", loop));
len = ARR_LEN(loop_blocks);
for (i = 0; i < len; ++i) {
- block_or_loop_t *block_or_loop = &loop_blocks[i];
- if (block_or_loop->is_loop) {
- ir_fprintf(stderr, " L-%p", block_or_loop->v.loop);
+ block_or_loop_t block_or_loop = loop_blocks[i];
+ if (block_or_loop.loop->kind == k_ir_loop) {
+ DB((dbg, LEVEL_3, " L-%p", block_or_loop.loop));
} else {
- ir_fprintf(stderr, " B-%+F", block_or_loop->v.block);
+ DB((dbg, LEVEL_3, " B-%+F", block_or_loop.block));
}
}
- fprintf(stderr, "\n");
+ DB((dbg, LEVEL_3, "\n"));
max_register_pressure = 0;
/* run1: tentative phase */
tentative_mode = true;
for (i = len-1; i >= 0; --i) {
- process_block_or_loop(&loop_blocks[i]);
+ process_block_or_loop(loop_blocks[i]);
}
/* run2: tentative phase - should reach fixpoint */
tentative_mode = true;
for (i = len-1; i >= 0; --i) {
- process_block_or_loop(&loop_blocks[i]);
+ process_block_or_loop(loop_blocks[i]);
}
#ifndef NDEBUG
tentative_mode = true;
should_have_reached_fixpoint = true;
for (i = len-1; i >= 0; --i) {
- process_block_or_loop(&loop_blocks[i]);
+ process_block_or_loop(loop_blocks[i]);
}
should_have_reached_fixpoint = false;
#endif
/* run4: add spills/reloads */
- tentative_mode = false;
+ tentative_mode = false;
+ do_push_unused_livethroughs = true;
for (i = len-1; i >= 0; --i) {
- process_block_or_loop(&loop_blocks[i]);
+ process_block_or_loop(loop_blocks[i]);
}
+ do_push_unused_livethroughs = false;
loop_info->max_register_pressure = max_register_pressure;
- ir_fprintf(stderr, "Regpressure in loop %p: %u\n", loop,
- (unsigned) max_register_pressure);
+ DB((dbg, LEVEL_2, "Regpressure in loop %p: %u\n", loop,
+ (unsigned) max_register_pressure));
DEL_ARR_F(loop_blocks);
}
worklist_t *end_worklist = pred_block_info->end_worklist;
ir_loop *pred_loop = get_irn_loop(pred_block);
bool is_loop_entry = false;
- struct list_head *entry;
+ worklist_entry_t *entry;
assert(end_worklist != NULL);
/* reload missing values */
activate_worklist(end_worklist);
- list_for_each(entry, &start_worklist->live_values) {
- worklist_entry_t *wl_entry
- = list_entry(entry, worklist_entry_t, head);
- ir_node *value = wl_entry->value;
+ foreach_worklist(entry, start_worklist) {
+ ir_node *value = entry->value;
+
+ if (!is_loop_entry && entry->unused_livethrough_loop != NULL)
+ continue;
if (is_Phi(value) && get_nodes_block(value) == block) {
value = get_irn_n(value, i);
/* we might have unknowns as argument for the phi */
- if (!arch_irn_consider_in_reg_alloc(arch_env, cls, value))
+ if (!arch_irn_consider_in_reg_alloc(cls, value))
continue;
}
-
if (worklist_contains(value))
continue;
- if (wl_entry->unused_livethrough_loop != NULL && !is_loop_entry)
- continue;
-
be_add_reload_on_edge(senv, value, block, i, cls, 1);
}
}
}
+/**
+ * Run the spill algorithm.
+ *
+ * @param birg the graph to run on
+ * @param ncls the register class to run on
+ */
static void be_spill_belady3(be_irg_t *birg, const arch_register_class_t *ncls)
{
ir_graph *irg = be_get_birg_irg(birg);
if (n_regs == 0)
return;
- arch_env = be_get_birg_arch_env(birg);
- exec_freq = be_get_birg_exec_freq(birg);
+ worklist_visited = 0;
+ exec_freq = be_get_birg_exec_freq(birg);
be_clear_links(irg);
ir_reserve_resources(irg, IR_RESOURCE_IRN_VISITED | IR_RESOURCE_IRN_LINK
clear_loop_info(get_irg_loop(irg));
irg_block_walk_graph(irg, construct_loop_edges, NULL, NULL);
- process_loop(get_irg_loop(current_ir_graph));
+ process_loop(get_irg_loop(irg));
irg_block_walk_graph(irg, fix_block_borders, NULL, NULL);