Commit 9d0b843b authored by Matthias Braun's avatar Matthias Braun
Browse files

be: change pre spill prepare phase to work on all register classes at once

- Only iterating over the graph once should be slightly faster
- We don't need to insert the middle of register allocation logic but
  can perform it once before.
- We can gather statistics on the prepared graph before spilling/regalloc has
  happened.
parent 771728a9
......@@ -184,12 +184,6 @@ static void pre_spill(be_chordal_env_t *const chordal_env, arch_register_class_t
/* put all ignore registers into the ignore register set. */
be_get_allocatable_regs(irg, cls, chordal_env->allocatable_regs->data);
be_timer_push(T_RA_CONSTR);
be_pre_spill_prepare_constr(irg, cls);
be_timer_pop(T_RA_CONSTR);
dump(BE_CH_DUMP_CONSTR, irg, cls, "constr-pre");
}
/**
......@@ -219,7 +213,7 @@ static void post_spill(be_chordal_env_t *const chordal_env, ir_graph *const irg)
be_ra_chordal_coloring(chordal_env);
be_timer_pop(T_RA_COLOR);
dump(BE_CH_DUMP_CONSTR, irg, chordal_env->cls, "color");
dump(BE_CH_DUMP_COLOR, irg, chordal_env->cls, "color");
/* Create the ifg with the selected flavor */
be_timer_push(T_RA_IFG);
......
......@@ -610,6 +610,13 @@ static void be_main_loop(FILE *file_handle, const char *cup_name)
stat_ev_ull("bemain_blocks_before_ra", be_count_blocks(irg));
}
/* add missing copies to make hidden register pressure increases
* explicit */
be_timer_push(T_RA_CONSTR);
be_add_missing_copies(irg);
be_timer_pop(T_RA_CONSTR);
be_dump(DUMP_RA, irg, "spillprepare");
/* Do register allocation */
be_allocate_registers(irg);
......
......@@ -1801,13 +1801,6 @@ static void dump(int mask, ir_graph *irg, const char *suffix)
*/
static void spill(void)
{
/* make sure all nodes show their real register pressure */
be_timer_push(T_RA_CONSTR);
be_pre_spill_prepare_constr(irg, cls);
be_timer_pop(T_RA_CONSTR);
dump(DUMP_RA, irg, "spillprepare");
/* spill */
be_timer_push(T_RA_SPILL);
be_do_spill(irg, cls);
......
......@@ -11,21 +11,198 @@
*/
#include <stdlib.h>
#include "irnode.h"
#include "irmode.h"
#include "irdom.h"
#include "iredges.h"
#include "irgwalk.h"
#include "irmode.h"
#include "irnode.h"
#include "irtools.h"
#include "statev_t.h"
#include "bera.h"
#include "beutil.h"
#include "besched.h"
#include "beintlive_t.h"
#include "beirg.h"
#include "belive_t.h"
#include "bemodule.h"
#include "benode.h"
#include "bera.h"
#include "besched.h"
#include "beutil.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg);
static be_irg_t *birg;
static be_lv_t *lv;
static void prepare_constr_insn(ir_node *const node)
{
/* Insert a copy for constraint inputs attached to a value which can't
* fulfill the constraint
* (typical example: stack pointer as input to copyb)
* TODO: This really just checks precolored registers at the moment and
* ignores the general case of not matching in/out constraints */
foreach_irn_in(node, i, op) {
const arch_register_req_t *const req
= arch_get_irn_register_req_in(node, i);
if (req->cls == NULL)
continue;
const arch_register_t *const reg = arch_get_irn_register(op);
if (reg == NULL)
continue;
/* Precolored with an ignore register (which is not virtual). */
if ((reg->type & arch_register_type_virtual) ||
rbitset_is_set(birg->allocatable_regs, reg->global_index))
continue;
if (!arch_register_req_is(req, limited))
continue;
if (rbitset_is_set(req->limited, reg->index))
continue;
ir_node *block = get_nodes_block(node);
ir_node *copy = be_new_Copy(block, op);
sched_add_before(node, copy);
set_irn_n(node, i, copy);
stat_ev_int("constr_copy", 1);
DBG((dbg, LEVEL_3, "inserting ignore arg copy %+F for %+F pos %d\n",
copy, node, i));
}
/* insert copies for nodes that occur constrained more than once. */
for (int i = 0, arity = get_irn_arity(node); i < arity; ++i) {
const arch_register_req_t *const req
= arch_get_irn_register_req_in(node, i);
const arch_register_class_t *const cls = req->cls;
if (cls == NULL)
continue;
if (!arch_register_req_is(req, limited))
continue;
ir_node *in = get_irn_n(node, i);
const arch_register_req_t *const in_req
= arch_get_irn_register_req(in);
if (arch_register_req_is(in_req, ignore))
continue;
for (int i2 = i + 1; i2 < arity; ++i2) {
const arch_register_req_t *const req2
= arch_get_irn_register_req_in(node, i2);
if (req2->cls != cls)
continue;
if (!arch_register_req_is(req2, limited))
continue;
ir_node *in2 = get_irn_n(node, i2);
if (in2 != in)
continue;
/* if the constraint is the same, no copy is necessary
* TODO generalise to unequal but overlapping constraints */
if (rbitsets_equal(req->limited, req2->limited, cls->n_regs))
continue;
ir_node *block = get_nodes_block(node);
ir_node *copy = be_new_Copy(block, in);
sched_add_before(node, copy);
set_irn_n(node, i2, copy);
stat_ev_int("constr_copy", 1);
DBG((dbg, LEVEL_3,
"inserting multiple constr copy %+F for %+F pos %d\n",
copy, node, i2));
}
}
/* collect all registers occurring in out constraints. */
unsigned *def_constr = NULL;
be_foreach_value(node, value,
const arch_register_req_t *const req = arch_get_irn_register_req(value);
const arch_register_class_t *const cls = req->cls;
if (cls == NULL)
continue;
if (!arch_register_req_is(req, limited))
continue;
if (def_constr == NULL) {
const arch_env_t *const arch_env = birg->main_env->arch_env;
def_constr = rbitset_alloca(arch_env->n_registers);
}
rbitset_foreach(req->limited, cls->n_regs, e) {
const arch_register_t *reg = arch_register_for_index(cls, e);
rbitset_set(def_constr, reg->global_index);
}
);
/* no output constraints => we're good */
if (def_constr == NULL)
return;
/* Insert copies for all constrained arguments living through the node and
* being constrained to a register which also occurs in out constraints. */
for (int i = 0, arity = get_irn_arity(node); i < arity; ++i) {
/* Check, if
* 1) the operand is constrained.
* 2) lives through the node.
* 3) is constrained to a register occurring in out constraints. */
const arch_register_req_t *const req
= arch_get_irn_register_req_in(node, i);
const arch_register_class_t *const cls = req->cls;
if (cls == NULL)
continue;
if (!arch_register_req_is(req, limited))
continue;
ir_node *in = get_irn_n(node, i);
const arch_register_req_t *const in_req
= arch_get_irn_register_req(in);
if (arch_register_req_is(in_req, ignore))
continue;
/* Only create the copy if the operand is no copy.
* this is necessary since the assure constraints phase inserts
* Copies and Keeps for operands which must be different from the
* results. Additional copies here would destroy this. */
if (be_is_Copy(in))
continue;
if (!be_values_interfere(lv, node, in))
continue;
bool common_limits = false;
rbitset_foreach(req->limited, cls->n_regs, e) {
const arch_register_t *reg = arch_register_for_index(cls, e);
if (rbitset_is_set(def_constr, reg->global_index)) {
common_limits = true;
break;
}
}
if (!common_limits)
continue;
ir_node *block = get_nodes_block(node);
ir_node *copy = be_new_Copy(block, in);
sched_add_before(node, copy);
set_irn_n(node, i, copy);
DBG((dbg, LEVEL_3, "inserting constr copy %+F for %+F pos %d\n",
copy, node, i));
be_liveness_update(lv, in);
}
}
static void add_missing_copies_in_block(ir_node *block, void *data)
{
(void)data;
sched_foreach(block, node) {
prepare_constr_insn(node);
}
}
void be_add_missing_copies(ir_graph *irg)
{
be_assure_live_sets(irg);
birg = be_birg_from_irg(irg);
lv = be_get_irg_liveness(irg);
irg_block_walk_graph(irg, add_missing_copies_in_block, NULL, NULL);
}
/** The list of register allocators */
static be_module_list_entry_t *register_allocators = NULL;
static be_ra_t *selected_allocator = NULL;
static be_module_list_entry_t *register_allocators;
static be_ra_t *selected_allocator;
void be_register_allocator(const char *name, be_ra_t *allocator)
{
......@@ -49,4 +226,5 @@ void be_init_ra(void)
be_add_module_list_opt(be_grp, "regalloc", "register allocator",
&register_allocators, (void**) &selected_allocator);
FIRM_DBG_REGISTER(dbg, "firm.be.regalloc");
}
......@@ -25,4 +25,10 @@ void be_register_allocator(const char *name, be_ra_t *allocator);
*/
void be_allocate_registers(ir_graph *irg);
/**
* Adds additional copies in cases where special register constraints make them
* unavailable and therefore increase the actual register pressure.
*/
void be_add_missing_copies(ir_graph *irg);
#endif
......@@ -30,167 +30,6 @@
#include "lc_opts.h"
#include "lc_opts_enum.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
typedef struct be_pre_spill_env_t {
ir_graph *irg;
const arch_register_class_t *cls;
} be_pre_spill_env_t;
static void prepare_constr_insn(be_pre_spill_env_t *env, ir_node *node)
{
const arch_register_class_t *cls = env->cls;
ir_node *block = get_nodes_block(node);
const ir_graph *irg = env->irg;
be_irg_t *birg = be_birg_from_irg(irg);
be_lv_t *lv = be_get_irg_liveness(irg);
unsigned *def_constr = NULL;
/* Insert a copy for constraint inputs attached to a value which can't
* fulfill the constraint
* (typical example: stack pointer as input to copyb)
* TODO: This really just checks precolored registers at the moment and
* ignores the general case of not matching in/out constraints */
foreach_irn_in(node, i, op) {
const arch_register_req_t *req = arch_get_irn_register_req_in(node, i);
if (req->cls != cls)
continue;
const arch_register_t *reg = arch_get_irn_register(op);
if (reg == NULL)
continue;
/* Precolored with an ignore register (which is not virtual). */
if (reg->type & arch_register_type_virtual ||
rbitset_is_set(birg->allocatable_regs, reg->global_index))
continue;
if (!arch_register_req_is(req, limited))
continue;
if (rbitset_is_set(req->limited, reg->index))
continue;
ir_node *copy = be_new_Copy(block, op);
stat_ev_int("constr_copy", 1);
sched_add_before(node, copy);
set_irn_n(node, i, copy);
DBG((dbg, LEVEL_3, "inserting ignore arg copy %+F for %+F pos %d\n",
copy, node, i));
}
/* insert copies for nodes that occur constrained more than once. */
int const arity = get_irn_arity(node);
be_foreach_use(node, cls, req, in, in_req_,
if (!arch_register_req_is(req, limited))
continue;
for (int i2 = i_ + 1; i2 < arity; ++i2) {
const arch_register_req_t *req2
= arch_get_irn_register_req_in(node, i2);
if (req2->cls != cls)
continue;
if (!arch_register_req_is(req2, limited))
continue;
ir_node *in2 = get_irn_n(node, i2);
if (in2 != in)
continue;
/* if the constraint is the same, no copy is necessary
* TODO generalise unequal but overlapping constraints */
if (rbitsets_equal(req->limited, req2->limited, cls->n_regs))
continue;
ir_node *copy = be_new_Copy(block, in);
stat_ev_int("constr_copy", 1);
sched_add_before(node, copy);
set_irn_n(node, i2, copy);
DBG((dbg, LEVEL_3,
"inserting multiple constr copy %+F for %+F pos %d\n",
copy, node, i2));
}
);
/* collect all registers occurring in out constraints. */
be_foreach_definition(node, cls, def, req,
(void)def;
if (!arch_register_req_is(req, limited))
continue;
if (def_constr == NULL) {
def_constr = rbitset_alloca(cls->n_regs);
}
rbitset_or(def_constr, req->limited, cls->n_regs);
);
/* no output constraints => we're good */
if (def_constr == NULL) {
return;
}
/*
* insert copies for all constrained arguments living through the node
* and being constrained to a register which also occurs in out constraints.
*/
unsigned *const tmp = rbitset_alloca(cls->n_regs);
be_foreach_use(node, cls, req, in, in_req_,
/* Check, if
* 1) the operand is constrained.
* 2) lives through the node.
* 3) is constrained to a register occurring in out constraints.
*/
if (!arch_register_req_is(req, limited))
continue;
if (!be_values_interfere(lv, node, in))
continue;
rbitset_copy(tmp, req->limited, cls->n_regs);
rbitset_and(tmp, def_constr, cls->n_regs);
if (rbitset_is_empty(tmp, cls->n_regs))
continue;
/*
* only create the copy if the operand is no copy.
* this is necessary since the assure constraints phase inserts
* Copies and Keeps for operands which must be different from the
* results. Additional copies here would destroy this.
*/
if (be_is_Copy(in))
continue;
ir_node *copy = be_new_Copy(block, in);
sched_add_before(node, copy);
set_irn_n(node, i_, copy);
DBG((dbg, LEVEL_3, "inserting constr copy %+F for %+F pos %d\n",
copy, node, i_));
be_liveness_update(lv, in);
);
}
static void pre_spill_prepare_constr_walker(ir_node *block, void *data)
{
be_pre_spill_env_t *env = (be_pre_spill_env_t*)data;
sched_foreach(block, node) {
prepare_constr_insn(env, node);
}
}
void be_pre_spill_prepare_constr(ir_graph *irg,
const arch_register_class_t *cls)
{
be_pre_spill_env_t env;
memset(&env, 0, sizeof(env));
env.irg = irg;
env.cls = cls;
be_assure_live_sets(irg);
irg_block_walk_graph(irg, pre_spill_prepare_constr_walker, NULL, &env);
}
int be_coalesce_spill_slots = 1;
int be_do_remats = 1;
......@@ -225,6 +64,4 @@ void be_init_spilloptions(void)
lc_opt_add_table(spill_grp, be_spill_options);
be_add_module_list_opt(be_grp, "spiller", "spill algorithm",
&spillers, (void**) &selected_spiller);
FIRM_DBG_REGISTER(dbg, "firm.be.spillprepare");
}
......@@ -36,11 +36,4 @@ void be_register_spiller(const char *name, be_spill_func spiller);
*/
void be_do_spill(ir_graph *irg, const arch_register_class_t *cls);
/**
* Adds additional copies, so constraints needing additional registers to be
* solved correctly induce the additional register pressure.
*/
void be_pre_spill_prepare_constr(ir_graph *irg,
const arch_register_class_t *cls);
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment