Commit 39266b95 authored by Sebastian Hack's avatar Sebastian Hack
Browse files

Added a first version of a Perm mover.

Moving nodes (mainly reloads) through Perms if they need not to be permed.
Also fixed bug in liveness.

[r14538]
parent 4e93e1cb
......@@ -44,6 +44,8 @@
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
/* see comment in compute_liveness() */
#define LV_COMPUTE_SORTED
#define LV_STD_SIZE 64
#define LV_USE_BINARY_SEARCH
#undef LV_INTESIVE_CHECKS
......@@ -477,13 +479,51 @@ static void *lv_phase_data_init(ir_phase *phase, ir_node *irn, void *old)
return info;
}
static void collect_nodes(ir_node *irn, void *data)
{
struct obstack *obst = data;
if (is_liveness_node(irn))
obstack_ptr_grow(obst, irn);
}
static int node_idx_cmp(const void *a, const void *b)
{
int ia = get_irn_idx(a);
int ib = get_irn_idx(b);
return ia - ib;
}
static void compute_liveness(be_lv_t *lv)
{
struct obstack obst;
struct _lv_walker_t w;
ir_node **nodes;
int i, n;
obstack_init(&obst);
irg_walk_graph(lv->irg, collect_nodes, NULL, &obst);
n = obstack_object_size(&obst) / sizeof(nodes[0]);
nodes = obstack_finish(&obst);
/*
* inserting the variables sorted by their ID is probably
* more efficient since the binary sorted set insertion
* will not need to move arounf the data.
* However, if sorting the variables a priori pays off
* needs to be checked, hence the define.
*/
#ifdef LV_COMPUTE_SORTED
qsort(nodes, n, sizeof(nodes[0]), node_idx_cmp);
#endif
w.lv = lv;
w.data = bitset_malloc(get_irg_last_idx(lv->irg));
irg_walk_graph(lv->irg, liveness_for_node, NULL, &w);
bitset_free(w.data);
w.data = bitset_obstack_alloc(&obst, get_irg_last_idx(lv->irg));
for (i = 0; i < n; ++i)
liveness_for_node(nodes[i], &w);
obstack_free(&obst, NULL);
register_hook(hook_node_info, &lv->hook_info);
}
void be_liveness_assure_sets(be_lv_t *lv)
......@@ -505,6 +545,7 @@ void be_liveness_assure_chk(be_lv_t *lv)
void be_liveness_invalidate(be_lv_t *lv)
{
if (lv && lv->nodes) {
unregister_hook(hook_node_info, &lv->hook_info);
phase_free(&lv->ph);
bitset_free(lv->nodes);
lv->nodes = NULL;
......@@ -521,7 +562,6 @@ be_lv_t *be_liveness(ir_graph *irg)
lv->lvc = lv_chk_new(irg);
lv->hook_info.context = lv;
lv->hook_info.hook._hook_node_info = lv_dump_block;
register_hook(hook_node_info, &lv->hook_info);
return lv;
}
......@@ -546,7 +586,6 @@ void be_liveness_recompute(be_lv_t *lv)
void be_liveness_free(be_lv_t *lv)
{
be_liveness_invalidate(lv);
unregister_hook(hook_node_info, &lv->hook_info);
free(lv);
}
......
......@@ -46,6 +46,7 @@
#include "bestat.h"
#include "bessaconstr.h"
#include "benodesets.h"
#include "beintlive_t.h"
#undef KEEP_ALIVE_COPYKEEP_HACK
......@@ -876,6 +877,142 @@ void assure_constraints(be_irg_t *birg) {
}
/**
* Push nodes that do not need to be permed through the Perm.
* This is commonly a reload cascade at block ends.
* @note This routine needs interference.
* @note Probably, we can implement it a little more efficient.
* Especially searching the frontier lazily might be better.
* @param perm The perm.
* @param data The walker data (lower_env_t).
* @return 1, if there is something left to perm over.
* 0, if removed the complete perm.
*/
static int push_through_perm(ir_node *perm, void *data)
{
lower_env_t *env = data;
const arch_env_t *aenv = env->arch_env;
ir_graph *irg = get_irn_irg(perm);
ir_node *bl = get_nodes_block(perm);
int n = get_irn_arity(perm);
int *map = alloca(n * sizeof(map[0]));
ir_node **projs = alloca(n * sizeof(projs[0]));
bitset_t *keep = bitset_alloca(n);
ir_node *frontier = sched_first(bl);
FIRM_DBG_REGISTER(firm_dbg_module_t *mod, "firm.be.lower.permmove");
int i, new_size, n_keep;
const ir_edge_t *edge;
ir_node *last_proj, *irn;
const arch_register_class_t *cls;
DBG((mod, LEVEL_1, "perm move %+F irg %+F\n", perm, irg));
/* get some proj and find out the register class of the proj. */
foreach_out_edge (perm, edge) {
last_proj = get_edge_src_irn(edge);
cls = arch_get_irn_reg_class(aenv, last_proj, -1);
assert(is_Proj(last_proj));
break;
}
/* find the point in the schedule after which the
* potentially movable nodes must be defined.
* A perm will only be pushed up to first instruction
* which lets an operand of itself die. */
sched_foreach_reverse_from (sched_prev(perm), irn) {
for(i = get_irn_arity(irn) - 1; i >= 0; --i) {
ir_node *op = get_irn_n(irn, i);
if(arch_irn_consider_in_reg_alloc(aenv, cls, op)
&& !values_interfere(env->birg, op, last_proj)) {
frontier = sched_next(irn);
goto found_front;
}
}
}
found_front:
DBG((mod, LEVEL_2, "\tfrontier: %+F\n", frontier));
foreach_out_edge (perm, edge) {
ir_node *proj = get_edge_src_irn(edge);
int nr = get_Proj_proj(proj);
ir_node *op = get_irn_n(perm, nr);
assert(nr < n);
/* we will need the last Proj as an insertion point
* for the instruction(s) pushed through the Perm */
if (sched_comes_after(last_proj, proj))
last_proj = proj;
projs[nr] = proj;
bitset_set(keep, nr);
if (!is_Proj(op) && get_nodes_block(op) == bl
&& (op == frontier || sched_comes_after(frontier, op))) {
for (i = get_irn_arity(op) - 1; i >= 0; --i) {
ir_node *opop = get_irn_n(op, i);
if (!arch_irn_consider_in_reg_alloc(aenv, cls, opop)) {
bitset_clear(keep, nr);
break;
}
}
}
}
n_keep = bitset_popcnt(keep);
/* well, we could not push enything through the perm */
if (n_keep == n)
return 1;
assert(is_Proj(last_proj));
DBG((mod, LEVEL_2, "\tkeep: %d, total: %d, mask: %b\n", n_keep, n, keep));
last_proj = sched_next(last_proj);
for (new_size = 0, i = 0; i < n; ++i) {
ir_node *proj = projs[i];
if (bitset_is_set(keep, i)) {
map[i] = new_size++;
set_Proj_proj(proj, map[i]);
DBG((mod, LEVEL_1, "\targ %d remap to %d\n", i, map[i]));
}
else {
ir_node *move = get_irn_n(perm, i);
DBG((mod, LEVEL_2, "\tmoving %+F before %+F, killing %+F\n", move, last_proj, proj));
/* move the movable node in front of the Perm */
sched_remove(move);
sched_add_before(last_proj, move);
/* give it the proj's register */
arch_set_irn_register(aenv, move, arch_get_irn_register(aenv, proj));
/* reroute all users of the proj to the moved node. */
edges_reroute(proj, move, irg);
/* remove the proj from the schedule. */
sched_remove(proj);
/* and like it to bad so it is no more in the use array of the perm */
set_Proj_pred(proj, get_irg_bad(irg));
map[i] = -1;
}
}
if (n_keep > 0)
be_Perm_reduce(perm, new_size, map);
return n_keep > 0;
}
/**
* Calls the corresponding lowering function for the node.
......@@ -886,7 +1023,9 @@ void assure_constraints(be_irg_t *birg) {
static void lower_nodes_after_ra_walker(ir_node *irn, void *walk_env) {
if (! is_Block(irn) && ! is_Proj(irn)) {
if (be_is_Perm(irn)) {
lower_perm_node(irn, walk_env);
int perm_stayed = push_through_perm(irn, walk_env);
if (perm_stayed)
lower_perm_node(irn, walk_env);
}
}
......@@ -909,5 +1048,8 @@ void lower_nodes_after_ra(be_irg_t *birg, int do_copy) {
env.do_copy = do_copy;
FIRM_DBG_REGISTER(env.dbg_module, "firm.be.lower");
/* we will need interference */
be_liveness_assure_chk(be_get_birg_liveness(birg));
irg_walk_blkwise_graph(irg, NULL, lower_nodes_after_ra_walker, &env);
}
......@@ -486,6 +486,35 @@ ir_node *be_new_Perm(const arch_register_class_t *cls, ir_graph *irg, ir_node *b
return irn;
}
void be_Perm_reduce(ir_node *perm, int new_size, int *map)
{
ir_graph *irg = get_irn_irg(perm);
int n = get_irn_arity(perm);
be_reg_data_t *old_data = malloc(n * sizeof(old_data[0]));
be_node_attr_t *attr = get_irn_attr(perm);
ir_node **new_in = NEW_ARR_D(ir_node *, irg->obst, new_size + 1);
int i;
assert(be_is_Perm(perm));
assert(new_size <= n);
/* save the old register data */
memcpy(old_data, attr->reg_data, n * sizeof(old_data[0]));
/* compose the new in array and set the new register data directly in place */
for (i = 0; i < n; ++i) {
int idx = map[i];
if (idx >= 0) {
new_in[idx] = get_irn_n(perm, i);
attr->reg_data[idx] = old_data[i];
}
}
free(old_data);
set_irn_in(perm, new_size, new_in);
}
ir_node *be_new_MemPerm(const arch_env_t *arch_env, ir_graph *irg, ir_node *bl, int n, ir_node *in[])
{
int i;
......
......@@ -157,6 +157,20 @@ void be_set_Copy_op(ir_node *cpy, ir_node *op);
* Make a new Perm node.
*/
ir_node *be_new_Perm(const arch_register_class_t *cls, ir_graph *irg, ir_node *bl, int arity, ir_node *in[]);
/**
* Reduce a Perm.
* Basically, we provide a map to remap the Perm's arguments. If an entry in the
* map is -1, the argument gets deleted.
* This function takes care, that the register data and the input array reflects
* the changes described by the map.
* This is needed by the Perm optimization/movement in belower.c, see push_through_perm().
* @param perm The perm node.
* @param new_size The new number of arguments (must be smaller or equal to the current one).
* @param map A map assigning each operand a new index (or -1 to indicate deletion).
*/
void be_Perm_reduce(ir_node *perm, int new_size, int *map);
/**
* Create a new MemPerm node.
*/
......
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