Commit 8b76d044 authored by Sebastian Hack's avatar Sebastian Hack
Browse files

Prototypely implemented constrained coloring

parent b46c894c
......@@ -11,6 +11,14 @@
#include "config.h"
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <ctype.h>
#include "obst.h"
......@@ -33,6 +41,7 @@
#include "benumb_t.h"
#include "besched_t.h"
#include "belive_t.h"
#include "benode_t.h"
#include "bearch.h"
#include "beifg.h"
......@@ -152,6 +161,91 @@ static INLINE int has_reg_class(const be_chordal_env_t *env, const ir_node *irn)
return arch_irn_has_reg_class(env->main_env->arch_env, irn, -1, env->cls);
}
static border_t *handle_constraint_perm(be_chordal_alloc_env_t *alloc_env, border_t *perm_border)
{
const arch_env_t *arch_env = alloc_env->chordal_env->main_env->arch_env;
bitset_t *bs = bitset_alloca(alloc_env->chordal_env->cls->n_regs);
ir_node *perm = perm_border->irn;
int n = get_irn_arity(perm_border->irn);
int n_projs = 0;
border_t *b, *next_border;
arch_register_req_t req;
ir_node *cnstr;
int has_cnstr = 0;
int i, m;
assert(is_Perm(perm));
/*
* After the Perm, there must be a sequence of Projs
* which extract the permuted values of the Perm.
*/
for(b = border_next(perm_border); is_Proj(b->irn); b = border_next(b)) {
assert(is_Proj(b->irn));
assert(b->is_def);
n_projs++;
}
cnstr = b->irn;
next_border = border_next(b);
assert(n_projs == n && "There must be as many Projs as the Perm is wide");
/* The node after the last perm proj must be the constrained node. */
cnstr = b->irn;
for(i = -1, m = get_irn_arity(cnstr); i < m; ++i) {
req.type = arch_register_req_type_normal;
if(arch_get_register_req(arch_env, &req, cnstr, i) && req.type == arch_register_req_type_limited) {
has_cnstr = 1;
break;
}
}
assert(has_cnstr && "The node must have a register constraint");
/*
* Consider the code in beconstrperm.c
* We turned each input constraint of a node into an output
* constraint of the Perm's Proj. So we only have to
* consider output constraints here.
*/
for(b = border_next(perm_border); b != next_border; b = border_next(b)) {
ir_node *irn = b->irn;
req.type = arch_register_req_type_normal;
if(arch_get_register_req(arch_env, &req, irn, -1) && req.type == arch_register_req_type_limited) {
const arch_register_t *reg;
int col;
bitset_clear_all(bs);
req.data.limited(irn, -1, bs);
col = bitset_next_set(bs, 0);
reg = arch_register_for_index(alloc_env->chordal_env->cls, col);
arch_set_irn_register(arch_env, irn, reg);
bitset_set(alloc_env->colors, col);
}
}
for(b = border_next(perm_border); b != next_border; b = border_next(b)) {
ir_node *irn = b->irn;
int nr = get_irn_graph_nr(irn);
bitset_set(alloc_env->live, nr);
if(arch_get_irn_register(arch_env, irn) == NULL) {
int col = bitset_next_clear(alloc_env->colors, 0);
const arch_register_t *reg = arch_register_for_index(alloc_env->chordal_env->cls, col);
arch_set_irn_register(arch_env, irn, reg);
}
}
return next_border;
}
/**
* Annotate the register pressure to the nodes and compute
* the liveness intervals.
......@@ -211,10 +305,10 @@ static void pressure(ir_node *block, void *env_ptr)
DBG((dbg, LEVEL_1, "\tinsn: %+F, pressure: %d\n", irn, pressure));
DBG((dbg, LEVEL_2, "\tlive: %b\n", live));
/*
* If the node defines some value, which can put into a
* register of the current class, make a border for it.
*/
/*
* If the node defines some value, which can put into a
* register of the current class, make a border for it.
*/
if(has_reg_class(env, irn)) {
int nr = get_irn_graph_nr(irn);
......@@ -356,6 +450,18 @@ static void assign(ir_node *block, void *env_ptr)
bitset_clear(colors, col);
bitset_clear(live, nr);
/*
* If we encounter a Perm, it is due to register constraints.
* To achieve a valid coloring in the presence of register
* constraints, we invoke a special function which takes care
* that all constraints are fulfilled.
* This function assigned valid colors to the projs of the
* Perm and the constrained node itself and skips these
* nodes in the border list.
*/
if(is_Perm(b->irn))
b = handle_constraint_perm(alloc_env, b);
}
}
......
......@@ -30,8 +30,6 @@
/** Defines an invalid register index. */
#define NO_COLOR (-1)
#define DBG_CHORDAL "firm.be.ra.chordal"
/**
* A liveness interval border.
*/
......@@ -71,6 +69,8 @@ static INLINE struct list_head *_get_block_border_head(const be_chordal_env_t *i
#define get_block_border_head(info, bl) _get_block_border_head(info, bl)
#define foreach_border_head(head, pos) list_for_each_entry_reverse(border_t, pos, head, list)
#define border_next(b) (list_entry((b)->list.next, border_t, list))
#define border_prev(b) (list_entry((b)->list.prev, border_t, list))
#define chordal_has_class(chordal_env, irn) \
arch_irn_has_reg_class(chordal_env->main_env->arch_env, irn, -1, chordal_env->cls)
......@@ -108,7 +108,7 @@ enum {
};
typedef struct {
int dump_flags;
unsigned dump_flags;
int spill_method;
int copymin_method;
int ifg_flavor;
......
......@@ -28,18 +28,48 @@ static void walker_insert_constr_perms(ir_node *bl, void *env) {
int pos, max;
sched_foreach(bl, irn) {
ir_node *perm = NULL;
/* check for a restriction of the result (-1) or one of the operands (0..n) */
max = get_irn_arity(irn);
for(pos=-1; pos<max; ++pos) {
arch_get_register_req(aenv, &req, irn, pos);
/* if a restriction is found, insert a perm before the irn */
if (cenv->cls == arch_get_irn_reg_class(aenv, irn, pos) && req.type == arch_register_req_type_limited) {
insert_Perm_after(menv, cenv->cls, cenv->dom_front, sched_prev(irn));
/* TODO: Next line is overkill. Update_liveness would suffice. */
be_liveness(get_irn_irg(bl));
break;
if(!perm)
perm = insert_Perm_after(menv, cenv->cls, cenv->dom_front, sched_prev(irn));
/*
* Turn an input constraint into an output constraint:
* The Proj of the Perm which corresponds to the input
* constraint will have the input constraint of the node
* as an output constraint
*/
if(pos >= 0) {
ir_node *op = get_irn_n(irn, pos);
/*
* The operand must be a proj now, since a perm cut
* all live ranges.
*/
assert(is_Proj(op));
be_set_Perm_out_req(perm, get_Proj_proj(op), &req);
}
}
}
/*
* If we inserted a perm,
* we have to recompute liveness analysis since inserting
* a Perm changes the liveness situation at the end
* of the block.
* (its needed by successive calls to insert_Perm_after)
* Perhaps thinking about an online liveness analysis
* would help.
*/
if(perm)
be_liveness(get_irn_irg(bl));
}
}
......
......@@ -5,6 +5,8 @@
*
* Backend node support.
*
* This file provdies Perm, Copy, Spill and Reload nodes.
*
* Copyright (C) 2005 Universitaet Karlsruhe
* Released under the GPL
*/
......@@ -20,6 +22,7 @@
#include "pmap.h"
#include "util.h"
#include "debug.h"
#include "fourcc.h"
#include "irop_t.h"
#include "irmode_t.h"
......@@ -36,6 +39,8 @@
#define DBG_LEVEL 0
#define BENODE_MAGIC FOURCC('B', 'E', 'N', 'O')
typedef enum _node_kind_t {
node_kind_spill,
node_kind_reload,
......@@ -53,14 +58,22 @@ typedef struct {
} be_op_t;
typedef struct {
const be_node_factory_t *factory;
int n_regs;
const arch_register_t *reg[1];
const arch_register_t *reg;
arch_register_req_t req;
} be_reg_data_t;
typedef struct {
unsigned magic;
const be_op_t *op;
int n_regs;
be_reg_data_t reg_data[1];
} be_node_attr_t;
typedef struct {
be_node_attr_t attr;
ir_node *spill_ctx;
ir_node *spill_ctx; /**< The node in whose context this spill was introduced. */
unsigned offset; /**< The offset of the memory location the spill writes to
in the spill area. */
} be_spill_attr_t;
static int templ_pos_Spill[] = {
......@@ -93,17 +106,44 @@ static const ir_op_ops be_node_ops = {
NULL
};
static INLINE int is_be_node(const ir_node *irn)
{
const be_node_attr_t *attr = (const be_node_attr_t *) &irn->attr;
return attr->magic == BENODE_MAGIC;
}
static INLINE int is_be_kind(const ir_node *irn, node_kind_t kind)
{
const be_node_attr_t *a = (const be_node_attr_t *) &irn->attr;
return a->magic == BENODE_MAGIC && a->op && a->op->kind == kind;
}
static INLINE void *get_attr_and_check(ir_node *irn, node_kind_t kind)
{
is_be_kind(irn, kind);
return &irn->attr;
}
static be_node_attr_t *init_node_attr(ir_node *irn,
const be_node_factory_t *fact, int n_regs)
const be_op_t *op,
const arch_register_class_t *cls,
int n_regs)
{
be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
int i;
attr->magic = BENODE_MAGIC;
attr->n_regs = n_regs;
attr->factory = fact;
attr->op = op;
for(i = 0; i < n_regs; ++i)
attr->reg[i] = NULL;
for(i = 0; i < n_regs; ++i) {
be_reg_data_t *rd = attr->reg_data + i;
rd->reg = NULL;
rd->req.cls = cls;
rd->req.type = arch_register_req_type_normal;
}
return attr;
}
......@@ -134,33 +174,97 @@ ir_node *new_Spill(const be_node_factory_t *factory,
const arch_register_class_t *cls,
ir_graph *irg, ir_node *bl, ir_node *node_to_spill, ir_node *ctx)
{
be_spill_attr_t *attr;
be_spill_attr_t *a;
ir_node *irn;
ir_node *in[1];
ir_op *op = get_op(factory, cls, node_kind_spill)->op;
be_op_t *bop = get_op(factory, cls, node_kind_spill);
ir_op *op = bop->op;
assert(op && "Spill opcode must be present for this register class");
in[0] = node_to_spill;
irn = new_ir_node(NULL, irg, bl, op, mode_M, 1, in);
attr = (be_spill_attr_t *) init_node_attr(irn, factory, 0);
attr->spill_ctx = ctx;
in[0] = node_to_spill;
irn = new_ir_node(NULL, irg, bl, op, mode_M, 1, in);
a = (be_spill_attr_t *) init_node_attr(irn, bop, cls, 0);
a->spill_ctx = ctx;
a->offset = 0;
return irn;
}
void set_Spill_offset(ir_node *irn, unsigned offset)
{
be_spill_attr_t *a = (be_spill_attr_t *) &irn->attr;
assert(is_be_kind(irn, node_kind_spill));
a->offset = offset;
}
static ir_node *find_a_spill_walker(ir_node *irn, unsigned visited_nr)
{
if(get_irn_visited(irn) < visited_nr) {
set_irn_visited(irn, visited_nr);
if(is_Phi(irn)) {
int i, n;
for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
ir_node *n = find_a_spill_walker(get_irn_n(irn, i), visited_nr);
if(n != NULL)
return n;
}
}
else if(is_be_kind(irn, node_kind_spill))
return irn;
}
return NULL;
}
/**
* Finds a spill for a reload.
* If the reload is directly using the spill, this is simple,
* else we perform DFS from the reload (over all PhiMs) and return
* the first spill node we find.
*/
static INLINE ir_node *find_a_spill(ir_node *irn)
{
ir_graph *irg = get_irn_irg(irn);
unsigned visited_nr = get_irg_visited(irg) + 1;
assert(is_be_kind(irn, node_kind_reload));
set_irg_visited(irg, visited_nr);
return find_a_spill_walker(irn, visited_nr);
}
unsigned get_irn_spill_offset(ir_node *irn)
{
be_node_attr_t *a = (be_node_attr_t *) &irn->attr;
assert(is_be_node(irn));
switch(a->op->kind) {
case node_kind_reload:
assert(0 && "not yet implemented");
return get_irn_spill_offset(find_a_spill(irn));
case node_kind_spill:
return ((be_spill_attr_t *) a)->offset;
default:
assert(0 && "Illegal node kind (spill/reload required)");
}
return 0;
}
ir_node *new_Reload(const be_node_factory_t *factory,
const arch_register_class_t *cls, ir_graph *irg,
ir_node *bl, ir_mode *mode, ir_node *spill_node)
{
ir_node *irn, *in[1];
ir_op *op = get_op(factory, cls, node_kind_reload)->op;
be_op_t *bop = get_op(factory, cls, node_kind_reload);
ir_op *op = bop->op;
assert(op && "Reload opcode must be present for this register class");
// assert(is_Spill(factory, spill_node) && "Operand of Reload must be a Spill");
in[0] = spill_node;
irn = new_ir_node(NULL, irg, bl, op, mode, 1, in);
init_node_attr(irn, factory, 1);
init_node_attr(irn, bop, cls, 1);
return irn;
}
......@@ -170,10 +274,11 @@ ir_node *new_Perm(const be_node_factory_t *factory,
ir_graph *irg, ir_node *bl, int arity, ir_node **in)
{
ir_node *irn;
ir_op *op = get_op(factory, cls, node_kind_perm)->op;
be_op_t *bop = get_op(factory, cls, node_kind_perm);
ir_op *op = bop->op;
irn = new_ir_node(NULL, irg, bl, op, mode_T, arity, in);
init_node_attr(irn, factory, arity);
init_node_attr(irn, bop, cls, arity);
return irn;
}
......@@ -183,12 +288,13 @@ ir_node *new_Copy(const be_node_factory_t *factory,
ir_graph *irg, ir_node *bl, ir_node *in)
{
ir_node *irn, *ins[1];
ir_op *op = get_op(factory, cls, node_kind_copy)->op;
be_op_t *bop = get_op(factory, cls, node_kind_copy);
ir_op *op = bop->op;
ins[0] = in;
irn = new_ir_node(NULL, irg, bl, op, get_irn_mode(in), 1, ins);
init_node_attr(irn, factory, 1);
init_node_attr(irn, bop, cls, 1);
return irn;
}
......@@ -228,7 +334,7 @@ ir_node *be_reload(const be_node_factory_t *factory,
ir_node *bl = get_nodes_block(irn);
ir_graph *irg = get_irn_irg(bl);
assert(is_Spill(factory, spill)
assert(is_Spill(spill)
|| (is_Phi(spill) && get_irn_mode(spill) == mode_M));
reload = new_Reload(factory, cls, irg, bl, mode, spill);
......@@ -238,6 +344,17 @@ ir_node *be_reload(const be_node_factory_t *factory,
return reload;
}
static INLINE arch_register_req_t *get_Perm_reqs(ir_node *perm)
{
be_node_attr_t *attr = (be_node_attr_t *) &perm->attr;
char *ptr = (char *) &perm->attr;
ptr += sizeof(be_node_attr_t);
ptr += sizeof(arch_register_t *) * attr->n_regs;
return (arch_register_req_t *) ptr;
}
/**
* If the node is a proj, reset the node to the proj's target and return
* the proj number.
......@@ -267,32 +384,60 @@ be_node_get_irn_reg_req(const arch_irn_ops_t *_self,
const be_node_factory_t *factory =
container_of(_self, const be_node_factory_t, irn_ops);
/* We cannot get output requirements for tuple nodes. */
if(get_irn_mode(irn) == mode_T && pos < 0)
return NULL;
/*
* were interested in an output operand, so
* let's resolve projs.
* if we're interested in an output operand (pos < 0), so let's resolve projs.
*/
if(pos < 0)
pos = redir_proj((const ir_node **) &irn, pos);
/* look if the node is one of ours. */
bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
if(bo) {
int i;
req->type = arch_register_req_type_normal;
req->cls = bo->cls;
for(i = 0; i < bo->n_pos; ++i) {
if(pos == bo->pos[i]) {
/* be nodes have no input constraints.
so return normal register requirements. */
if(pos >= 0) {
req->cls = bo->cls;
req->type = arch_register_req_type_normal;
}
/*
* if an output requirement is requested,
* return the one stored in the node.
*/
else {
be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
*req = attr->reg_data[pos].req;
}
for(i = 0; i < bo->n_pos; ++i)
if(pos == bo->pos[i])
return req;
}
}
}
return NULL;
}
void be_set_Perm_out_req(ir_node *irn, int pos, const arch_register_req_t *req)
{
be_op_t *bo;
be_node_attr_t *a = get_attr_and_check(irn, node_kind_perm);
assert(pos >= 0 && pos < get_irn_arity(irn) && "position out of range");
assert(a->op->kind == node_kind_perm && "node must be a perm node");
a->reg_data[pos].req = *req;
}
void
be_node_set_irn_reg(const arch_irn_ops_t *_self, ir_node *irn,
const arch_register_t *reg)
......@@ -303,7 +448,7 @@ be_node_set_irn_reg(const arch_irn_ops_t *_self, ir_node *irn,
const be_node_factory_t *factory =
container_of(_self, const be_node_factory_t, irn_ops);
if(get_irn_mode(irn) == mode_T && pos < 0)
if(get_irn_mode(irn) == mode_T)
return;
pos = redir_proj((const ir_node **) &irn, -1);
......@@ -313,7 +458,7 @@ be_node_set_irn_reg(const arch_irn_ops_t *_self, ir_node *irn,
return;
attr = (be_node_attr_t *) &irn->attr;
attr->reg[-pos - 1] = reg;
attr->reg_data[-pos - 1].reg = reg;
}
const arch_register_t *
......@@ -324,7 +469,7 @@ be_node_get_irn_reg(const arch_irn_ops_t *_self, const ir_node *irn)
const be_node_factory_t *factory =
container_of(_self, const be_node_factory_t, irn_ops);
if(get_irn_mode(irn) == mode_T && pos < 0)
if(get_irn_mode(irn) == mode_T)
return NULL;
pos = redir_proj((const ir_node **) &irn, -1);
......@@ -336,7 +481,7 @@ be_node_get_irn_reg(const arch_irn_ops_t *_self, const ir_node *irn)
for(i = 0; i < bo->n_pos; ++i) {
if(bo->pos[i] == pos) {
be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
return attr->reg[-pos - 1];
return attr->reg_data[-pos - 1].reg;
}
}
......@@ -390,11 +535,14 @@ const arch_irn_handler_t *be_node_get_irn_handler(const be_node_factory_t *f)
return &f->handler;
}
int is_Spill(const be_node_factory_t *f, const ir_node *irn)
int is_Spill(const ir_node *irn)
{
be_op_t *bo;
bo = pmap_get(f->irn_op_map, get_irn_op(irn));
return bo != NULL && bo->kind == node_kind_spill;
return is_be_kind(irn, node_kind_spill);
}
int is_Perm(const ir_node *irn)
{
return is_be_kind(irn, node_kind_perm);
}
be_node_factory_t *be_node_factory_init(be_node_factory_t *factory, const arch_isa_t *isa)
......@@ -441,7 +589,8 @@ be_node_factory_t *be_node_factory_init(be_node_factory_t *factory, const arch_i
ent = get_op(factory, cls, node_kind_perm);
ent->op = new_ir_op(get_next_ir_opcode(), "Perm", op_pin_state_pinned, 0,
oparity_variable, 0,
sizeof(be_node_attr_t) + sizeof(arch_register_t) * cls->n_regs, &be_node_ops);
sizeof(be_node_attr_t)
+ sizeof(be_reg_data_t) * cls->n_regs, &be_node_ops);
ent->n_pos = 2 * cls->n_regs;
ent->pos = obstack_alloc(&factory->obst, sizeof(ent->pos[0]) * ent->n_pos);
for(j = 0; j < ent->n_pos; j += 2) {
......@@ -458,11 +607,13 @@ be_node_factory_t *be_node_factory_init(be_node_factory_t *factory, const arch_i
static int dump_node(ir_node *irn, FILE *f, dump_reason_t reason)
{
be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
be_op_t *bo = pmap_get(attr->factory->irn_op_map, get_irn_op(irn));
be_node_attr_t *at = (be_node_attr_t *) &irn->attr;
const be_op_t *bo;
int i;
assert(is_be_node(irn));