Commit f3b87a11 authored by Matthias Braun's avatar Matthias Braun
Browse files

- improve scheduling of memperm

- Added some naive rematerialisation code, which should work for constants,
  reloads (so that no 2nd spill is created) and should later support stackparms
  once backend leaves them in the graph.
parent 59be6414
......@@ -32,6 +32,9 @@
#include "bechordal_t.h"
#include "bejavacoal.h"
// only rematerialise when costs are less than REMAT_COST_LIMIT
#define REMAT_COST_LIMIT 4
/* This enables re-computation of values. Current state: Unfinished and buggy. */
#undef BUGGY_REMAT
......@@ -113,21 +116,105 @@ void be_delete_spill_env(spill_env_t *env) {
free(env);
}
/**
* ____ _ ____ _ _
* | _ \| | __ _ ___ ___ | _ \ ___| | ___ __ _ __| |___
* | |_) | |/ _` |/ __/ _ \ | |_) / _ \ |/ _ \ / _` |/ _` / __|
* | __/| | (_| | (_| __/ | _ < __/ | (_) | (_| | (_| \__ \
* |_| |_|\__,_|\___\___| |_| \_\___|_|\___/ \__,_|\__,_|___/
*
*/
void be_add_reload(spill_env_t *env, ir_node *to_spill, ir_node *before) {
spill_info_t *info;
reloader_t *rel;
assert(sched_is_scheduled(before));
assert(arch_irn_consider_in_reg_alloc(env->chordal_env->birg->main_env->arch_env, env->cls, to_spill));
info = get_spillinfo(env, to_spill);
if(is_Phi(to_spill)) {
int i, arity;
// create spillinfos for the phi arguments
for(i = 0, arity = get_irn_arity(to_spill); i < arity; ++i) {
ir_node *arg = get_irn_n(to_spill, i);
get_spillinfo(env, arg);
}
}
rel = obstack_alloc(&env->obst, sizeof(rel[0]));
rel->reloader = before;
rel->next = info->reloaders;
info->reloaders = rel;
be_liveness_add_missing(env->chordal_env->lv);
}
void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block, int pos) {
ir_node *predblock, *last;
/* simply add the reload to the beginning of the block if we only have 1 predecessor
* (we don't need to check for phis as there can't be any in a block with only 1 pred)
*/
if(get_Block_n_cfgpreds(block) == 1) {
assert(!is_Phi(sched_first(block)));
be_add_reload(env, to_spill, sched_first(block));
return;
}
/* We have to reload the value in pred-block */
predblock = get_Block_cfgpred_block(block, pos);
last = sched_last(predblock);
/* we might have projs and keepanys behind the jump... */
while(is_Proj(last) || be_is_Keep(last)) {
last = sched_prev(last);
assert(!sched_is_end(last));
}
assert(is_cfop(last));
// add the reload before the (cond-)jump
be_add_reload(env, to_spill, last);
}
void be_spill_phi(spill_env_t *env, ir_node *node) {
int i, arity;
assert(is_Phi(node));
pset_insert_ptr(env->mem_phis, node);
// create spillinfos for the phi arguments
get_spillinfo(env, node);
for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
ir_node *arg = get_irn_n(node, i);
get_spillinfo(env, arg);
}
}
/*
* ____ _ ____ _ _ _
* / ___|_ __ ___ __ _| |_ ___ / ___| _ __ (_) | |___
* | | | '__/ _ \/ _` | __/ _ \ \___ \| '_ \| | | / __|
* | |___| | | __/ (_| | || __/ ___) | |_) | | | \__ \
* \____|_| \___|\__,_|\__\___| |____/| .__/|_|_|_|___/
* |_|
*/
/**
* Schedules a node after an instruction. (That is the place after all projs and phis
* that are scheduled after the instruction)
* This function also skips phi nodes at the beginning of a block
*/
static void sched_add_after_insn(ir_node *sched_after, ir_node *node) {
ir_node *next = sched_next(sched_after);
while(!sched_is_end(next)) {
if(!is_Proj(next) && !is_Phi(next))
break;
while(is_Proj(next) || is_Phi(next)) {
next = sched_next(next);
}
assert(next != NULL);
if(sched_is_end(next)) {
next = sched_last(get_nodes_block(sched_after));
sched_add_after(next, node);
sched_add_after(sched_last(get_nodes_block(sched_after)), node);
} else {
sched_add_before(next, node);
}
......@@ -150,6 +237,9 @@ static void spill_irn(spill_env_t *env, spill_info_t *spillinfo) {
/* Trying to spill an already spilled value, no need for a new spill
* node then, we can simply connect to the same one for this reload
*
* (although rematerialisation code should handle most of these cases
* this can still happen when spilling Phis)
*/
if(be_is_Reload(to_spill)) {
spillinfo->spill = get_irn_n(to_spill, be_pos_Reload_mem);
......@@ -219,129 +309,138 @@ static void spill_node(spill_env_t *env, spill_info_t *spillinfo) {
}
}
static INLINE ir_node *skip_projs(ir_node *node) {
while(is_Proj(node)) {
node = sched_next(node);
assert(!sched_is_end(node));
}
return node;
}
/*
*
* ____ _ _ _ _
* | _ \ ___ _ __ ___ __ _| |_ ___ _ __(_) __ _| (_)_______
* | |_) / _ \ '_ ` _ \ / _` | __/ _ \ '__| |/ _` | | |_ / _ \
* | _ < __/ | | | | | (_| | || __/ | | | (_| | | |/ / __/
* |_| \_\___|_| |_| |_|\__,_|\__\___|_| |_|\__,_|_|_/___\___|
*
*/
#if 0
/**
* Searchs the schedule backwards until we reach the first use or def of a
* value or a phi.
* Returns the node after this node (so that you can do sched_add_before)
* Tests whether value @p arg is available before node @p reloader
* @returns 1 if value is available, 0 otherwise
*/
static ir_node *find_last_use_def(spill_env_t *env, ir_node *block, ir_node *value) {
ir_node *node, *last;
static int is_value_available(spill_env_t *env, ir_node *arg, ir_node *reloader) {
if(is_Unknown(arg) || arg == new_NoMem())
return 1;
last = NULL;
sched_foreach_reverse(block, node) {
int i, arity;
if(be_is_Spill(arg))
return 1;
if(is_Phi(node)) {
return last;
}
if(value == node) {
return skip_projs(last);
}
for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
ir_node *arg = get_irn_n(node, i);
if(arg == value) {
return skip_projs(last);
}
}
last = node;
if(arg == get_irg_frame(env->chordal_env->irg))
return 1;
#if 0
/* we want to remat before the insn reloader
* thus an arguments is alive if
* - it interferes with the reloaders result
* or
* - or it is (last-) used by reloader itself
*/
int i, m;
if (values_interfere(reloader, arg))
return 1;
for (i=0, m=get_irn_arity(reloader); i<m; ++i) {
ir_node *rel_arg = get_irn_n(reloader, i);
if (rel_arg == arg)
return 1;
}
// simply return first node if no def or use found
return sched_first(block);
}
/* arg is not alive before reloader */
return 0;
#endif
#ifdef BUGGY_REMAT
return 0;
}
/**
* Check if a spilled node could be rematerialized.
*
* @param senv the spill environment
* @param spill the Spill node
* @param spilled the node that was spilled
* @param reloader a irn that requires a reload
* Checks whether the node can principally be rematerialized
*/
static int check_remat_conditions(spill_env_t *senv, ir_node *spilled, ir_node *reloader) {
int pos, max;
static int is_remat_node(spill_env_t *env, ir_node *node) {
const arch_env_t *arch_env = env->chordal_env->birg->main_env->arch_env;
/* check for 'normal' spill and general remat condition */
if (!arch_irn_is(senv->chordal_env->birg->main_env->arch_env, spilled, rematerializable))
return 0;
assert(!be_is_Spill(node));
/* check availability of original arguments */
if (is_Block(reloader)) {
if(be_is_Reload(node))
return 1;
/* we want to remat at the end of a block.
* thus all arguments must be alive at the end of the block
*/
for (pos=0, max=get_irn_arity(spilled); pos<max; ++pos) {
ir_node *arg = get_irn_n(spilled, pos);
if (!is_live_end(reloader, arg))
return 0;
}
// TODO why does arch_irn_is say rematerializable anyway?
if(be_is_Barrier(node))
return 0;
} else {
if(arch_irn_is(arch_env, node, rematerializable))
return 1;
/* we want to remat before the insn reloader
* thus an arguments is alive if
* - it interferes with the reloaders result
* or
* - or it is (last-) used by reloader itself
*/
for (pos=0, max=get_irn_arity(spilled); pos<max; ++pos) {
ir_node *arg = get_irn_n(spilled, pos);
int i, m;
if (values_interfere(reloader, arg))
goto is_alive;
for (i=0, m=get_irn_arity(reloader); i<m; ++i) {
ir_node *rel_arg = get_irn_n(reloader, i);
if (rel_arg == arg)
goto is_alive;
}
if(be_is_StackParam(node))
return 1;
/* arg is not alive before reloader */
return 0;
return 0;
}
/**
* Check if a node is rematerializable. This tests for the following conditions:
*
* - The node itself is rematerializable
* - All arguments of the node are available or also rematerialisable
* - The costs for the rematerialisation operation is less or equal a limit
*
* Returns the costs needed for rematerialisation or something
* > REMAT_COST_LIMIT if remat is not possible.
*/
static int check_remat_conditions_costs(spill_env_t *env, ir_node *spilled, ir_node *reloader, int parentcosts) {
int i, arity;
int argremats;
int costs = 0;
is_alive: ;
if(!is_remat_node(env, spilled))
return REMAT_COST_LIMIT;
if(be_is_Reload(spilled)) {
costs += 2;
} else if(is_Proj(spilled)) {
costs += 0;
} else {
costs += 1;
}
if(parentcosts + costs >= REMAT_COST_LIMIT)
return REMAT_COST_LIMIT;
argremats = 0;
for(i = 0, arity = get_irn_arity(spilled); i < arity; ++i) {
ir_node *arg = get_irn_n(spilled, i);
if(is_value_available(env, arg, reloader))
continue;
// we have to rematerialize the argument as well...
if(argremats >= 1) {
/* we only support rematerializing 1 argument at the moment,
* so that we don't have to care about register pressure
*/
return REMAT_COST_LIMIT;
}
argremats++;
// TODO can we get more accurate costs than +1?
costs += check_remat_conditions_costs(env, arg, reloader, parentcosts + costs);
if(parentcosts + costs >= REMAT_COST_LIMIT)
return REMAT_COST_LIMIT;
}
return 1;
return costs;
}
#else /* BUGGY_REMAT */
/**
* A very simple rematerialization checker.
*
* @param senv the spill environment
* @param spill the Spill node
* @param spilled the node that was spilled
* @param reloader a irn that requires a reload
*/
static int check_remat_conditions(spill_env_t *senv, ir_node *spilled, ir_node *reloader) {
const arch_env_t *aenv = senv->chordal_env->birg->main_env->arch_env;
static int check_remat_conditions(spill_env_t *env, ir_node *spilled, ir_node *reloader) {
int costs = check_remat_conditions_costs(env, spilled, reloader, 1);
return get_irn_arity(spilled) == 0 &&
arch_irn_is(aenv, spilled, rematerializable);
return costs < REMAT_COST_LIMIT;
}
#endif /* BUGGY_REMAT */
/**
* Re-materialize a node.
*
......@@ -349,45 +448,49 @@ static int check_remat_conditions(spill_env_t *senv, ir_node *spilled, ir_node *
* @param spilled the node that was spilled
* @param reloader a irn that requires a reload
*/
static ir_node *do_remat(spill_env_t *senv, ir_node *spilled, ir_node *reloader) {
static ir_node *do_remat(spill_env_t *env, ir_node *spilled, ir_node *reloader) {
int i, arity;
ir_node *res;
ir_node *bl = (is_Block(reloader)) ? reloader : get_nodes_block(reloader);
ir_node *bl = get_nodes_block(reloader);
ir_node **ins;
ins = alloca(get_irn_arity(spilled) * sizeof(ins[0]));
for(i = 0, arity = get_irn_arity(spilled); i < arity; ++i) {
ir_node *arg = get_irn_n(spilled, i);
/* recompute the value */
res = new_ir_node(get_irn_dbg_info(spilled), senv->chordal_env->irg, bl,
if(is_value_available(env, arg, reloader)) {
ins[i] = arg;
} else {
ins[i] = do_remat(env, arg, reloader);
}
}
/* create a copy of the node */
res = new_ir_node(get_irn_dbg_info(spilled), env->chordal_env->irg, bl,
get_irn_op(spilled),
get_irn_mode(spilled),
get_irn_arity(spilled),
get_irn_in(spilled) + 1);
ins);
copy_node_attr(spilled, res);
DBG((senv->dbg, LEVEL_1, "Insert remat %+F before reloader %+F\n", res, reloader));
DBG((env->dbg, LEVEL_1, "Insert remat %+F before reloader %+F\n", res, reloader));
ir_printf("Insert remat %+F for %+F before reloader %+F(%s)\n", res, spilled, reloader, get_irg_dump_name(get_irn_irg(reloader)));
/* insert in schedule */
if (is_Block(reloader)) {
ir_node *insert = sched_skip(reloader, 0, sched_skip_cf_predicator, (void *) senv->chordal_env->birg->main_env->arch_env);
sched_add_after(insert, res);
} else {
sched_add_before(reloader, res);
}
assert(!is_Block(reloader));
sched_add_before(reloader, res);
return res;
}
void be_spill_phi(spill_env_t *env, ir_node *node) {
int i, arity;
assert(is_Phi(node));
pset_insert_ptr(env->mem_phis, node);
// create spillinfos for the phi arguments
get_spillinfo(env, node);
for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
ir_node *arg = get_irn_n(node, i);
get_spillinfo(env, arg);
}
}
/*
* ___ _ ____ _ _
* |_ _|_ __ ___ ___ _ __| |_ | _ \ ___| | ___ __ _ __| |___
* | || '_ \/ __|/ _ \ '__| __| | |_) / _ \ |/ _ \ / _` |/ _` / __|
* | || | | \__ \ __/ | | |_ | _ < __/ | (_) | (_| | (_| \__ \
* |___|_| |_|___/\___|_| \__| |_| \_\___|_|\___/ \__,_|\__,_|___/
*
*/
void be_insert_spills_reloads(spill_env_t *env) {
const arch_env_t *arch_env = env->chordal_env->birg->main_env->arch_env;
......@@ -431,55 +534,3 @@ void be_insert_spills_reloads(spill_env_t *env) {
del_set(env->spills);
env->spills = new_set(cmp_spillinfo, 1024);
}
void be_add_reload(spill_env_t *env, ir_node *to_spill, ir_node *before) {
spill_info_t *info;
reloader_t *rel;
assert(sched_is_scheduled(before));
assert(arch_irn_consider_in_reg_alloc(env->chordal_env->birg->main_env->arch_env, env->cls, to_spill));
info = get_spillinfo(env, to_spill);
if(is_Phi(to_spill)) {
int i, arity;
// create spillinfos for the phi arguments
for(i = 0, arity = get_irn_arity(to_spill); i < arity; ++i) {
ir_node *arg = get_irn_n(to_spill, i);
get_spillinfo(env, arg);
}
}
rel = obstack_alloc(&env->obst, sizeof(rel[0]));
rel->reloader = before;
rel->next = info->reloaders;
info->reloaders = rel;
be_liveness_add_missing(env->chordal_env->lv);
}
void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block, int pos) {
ir_node *predblock, *last;
/* simply add the reload to the beginning of the block if we only have 1 predecessor
* (we don't need to check for phis as there can't be any in a block with only 1 pred)
*/
if(get_Block_n_cfgpreds(block) == 1) {
assert(!is_Phi(sched_first(block)));
be_add_reload(env, to_spill, sched_first(block));
return;
}
/* We have to reload the value in pred-block */
predblock = get_Block_cfgpred_block(block, pos);
last = sched_last(predblock);
/* we might have projs and keepanys behind the jump... */
while(is_Proj(last) || be_is_Keep(last)) {
last = sched_prev(last);
assert(!sched_is_end(last));
}
assert(is_cfop(last));
// add the reload before the (cond-)jump
be_add_reload(env, to_spill, last);
}
......@@ -633,12 +633,36 @@ static void assign_spillslots(ss_env_t *env) {
}
}
/**
* Returns the last node in a block which is no control flow changing node
*/
static ir_node *get_end_of_block_insertion_point(ir_node* block)
{
ir_node* ins = sched_last(block);
while(is_Proj(ins) && get_irn_mode(ins) == mode_X) {
ins = sched_prev(ins);
assert(ins != NULL);
}
if(is_cfop(ins)) {
while(1) {
ir_node *prev = sched_prev(ins);
if(!is_cfop(prev))
break;
ins = prev;
}
}
return ins;
}
static void create_memperms(ss_env_t *env) {
memperm_t *memperm;
for(memperm = set_first(env->memperms); memperm != NULL; memperm = set_next(env->memperms)) {
int i;
memperm_entry_t *entry;
ir_node *blockend;
ir_node** nodes = alloca(memperm->entrycount * sizeof(nodes[0]));
ir_node* mempermnode;
......@@ -653,7 +677,8 @@ static void create_memperms(ss_env_t *env) {
memperm->entrycount, nodes);
// insert node into schedule
sched_add_before(sched_last(memperm->block), mempermnode);
blockend = get_end_of_block_insertion_point(memperm->block);
sched_add_after(blockend, mempermnode);
for(entry = memperm->entries, i = 0; entry != NULL; entry = entry->next, ++i) {
ir_node *proj;
......@@ -663,7 +688,7 @@ static void create_memperms(ss_env_t *env) {
be_set_MemPerm_out_entity(mempermnode, i, entry->out);
set_irg_current_block(env->chordal_env->irg, memperm->block);
proj = new_Proj(mempermnode, get_irn_mode(arg), i);
sched_add_before(sched_last(memperm->block), proj);
sched_add_after(blockend, proj);
set_irn_n(entry->node, entry->pos, proj);
}
......
......@@ -3,8 +3,8 @@ EDG=edg
GCC=gcc
GCC_CFLAGS=-O3 -g
EDG_CFLAGS=-f win32 -b ra-chordal-spill=morgan --c -Ic:\\devstudio\\include
ASM_FLAGS=-g3
EDG_CFLAGS=-f win32 --c -Ic:\\devstudio\\include
ASM_FLAGS=-g3 -lm
EXCLUDE=bf_localinit.c bf_store.c calls.c compress95.c convtest.c \
fe_bug.c gnu_def.c harness.c if.c psi_test.c
......
......@@ -11,6 +11,7 @@
* URL: http://www-info1.informatik.uni-wuerzburg.de/staff/wolf/teaching/pi1_ws98/java/QuickSort.java
*/
#include <stdio.h>
#include <stdlib.h>
// Variablen, in denen die Bewegungen und Vergleiche gespeichert werden
......
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