- Added 2 new blockschedulers, a greedy algorithm and an "optimal" ILP that
[libfirm] / ir / be / bespill.c
index 449f8ba..7784817 100644 (file)
@@ -14,6 +14,7 @@
 #include "irnode_t.h"
 #include "ircons_t.h"
 #include "iredges_t.h"
+#include "irbackedge_t.h"
 #include "irprintf.h"
 #include "ident_t.h"
 #include "type_t.h"
 #include "benode_t.h"
 #include "bechordal_t.h"
 #include "bejavacoal.h"
+#include "benodesets.h"
 
 // only rematerialise when costs are less than REMAT_COST_LIMIT
 // TODO determine a good value here...
-#define REMAT_COST_LIMIT       80
+#define REMAT_COST_LIMIT       10
 
 typedef struct _reloader_t reloader_t;
 
@@ -45,10 +47,16 @@ struct _reloader_t {
 };
 
 typedef struct _spill_info_t {
+       /** the value that should get spilled */
        ir_node *spilled_node;
+       /** list of places where the value should get reloaded */
        reloader_t *reloaders;
 
+       /** the spill node, or a PhiM node */
        ir_node *spill;
+       /** if we had the value of a phi spilled before but not the phi itself then
+        * this field contains the spill for the phi value */
+       ir_node *old_spill;
 } spill_info_t;
 
 struct _spill_env_t {
@@ -56,8 +64,10 @@ struct _spill_env_t {
        const arch_env_t *arch_env;
        const be_chordal_env_t *chordal_env;
        struct obstack obst;
-       set *spills;                            /**< all spill_info_t's, which must be placed */
-       pset *mem_phis;                         /**< set of all special spilled phis. allocated and freed separately */
+       int spill_cost;     /**< the cost of a single spill node */
+       int reload_cost;    /**< the cost of a reload node */
+       set *spills;        /**< all spill_info_t's, which must be placed */
+       pset *mem_phis;     /**< set of all special spilled phis. allocated and freed separately */
 
        DEBUG_ONLY(firm_dbg_module_t *dbg;)
 };
@@ -71,12 +81,25 @@ static int cmp_spillinfo(const void *x, const void *y, size_t size) {
        return xx->spilled_node != yy->spilled_node;
 }
 
+/**
+ * Returns spill info for a specific value (returns NULL if the info doesn't
+ * exist yet)
+ */
+static spill_info_t *find_spillinfo(const spill_env_t *env, ir_node *value) {
+       spill_info_t info;
+       int hash = nodeset_hash(value);
+
+       info.spilled_node = value;
+
+       return set_find(env->spills, &info, sizeof(info), hash);
+}
+
 /**
  * Returns spill info for a specific value (the value that is to be spilled)
  */
 static spill_info_t *get_spillinfo(const spill_env_t *env, ir_node *value) {
        spill_info_t info, *res;
-       int hash = HASH_PTR(value);
+       int hash = nodeset_hash(value);
 
        info.spilled_node = value;
        res = set_find(env->spills, &info, sizeof(info), hash);
@@ -84,6 +107,7 @@ static spill_info_t *get_spillinfo(const spill_env_t *env, ir_node *value) {
        if (res == NULL) {
                info.reloaders = NULL;
                info.spill = NULL;
+               info.old_spill = NULL;
                res = set_insert(env->spills, &info, sizeof(info), hash);
        }
 
@@ -105,6 +129,9 @@ spill_env_t *be_new_spill_env(const be_chordal_env_t *chordal_env) {
        env->chordal_env        = chordal_env;
        env->arch_env       = env->chordal_env->birg->main_env->arch_env;
        env->mem_phis           = pset_new_ptr_default();
+       // TODO, ask backend about costs...
+       env->spill_cost     = 8;
+       env->reload_cost    = 5;
        obstack_init(&env->obst);
        return env;
 }
@@ -117,7 +144,7 @@ void be_delete_spill_env(spill_env_t *env) {
        free(env);
 }
 
-/**
+/*
  *  ____  _                  ____      _                 _
  * |  _ \| | __ _  ___ ___  |  _ \ ___| | ___   __ _  __| |___
  * | |_) | |/ _` |/ __/ _ \ | |_) / _ \ |/ _ \ / _` |/ _` / __|
@@ -150,7 +177,7 @@ void be_add_reload(spill_env_t *env, ir_node *to_spill, ir_node *before) {
        info->reloaders = rel;
 }
 
-void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block, int pos) {
+static ir_node *get_reload_insertion_point(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
@@ -158,8 +185,7 @@ void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block,
         */
        if(get_Block_n_cfgpreds(block) == 1) {
                assert(!is_Phi(sched_first(block)));
-               be_add_reload(env, to_spill, sched_first(block));
-               return;
+               return sched_first(block);
        }
 
        /* We have to reload the value in pred-block */
@@ -174,10 +200,16 @@ void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block,
        assert(is_cfop(last));
 
        // add the reload before the (cond-)jump
-       be_add_reload(env, to_spill, last);
+       return last;
+}
+
+void be_add_reload_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block, int pos) {
+       ir_node *before = get_reload_insertion_point(block, pos);
+       be_add_reload(env, to_spill, before);
 }
 
 void be_spill_phi(spill_env_t *env, ir_node *node) {
+       spill_info_t* spill;
        int i, arity;
 
        assert(is_Phi(node));
@@ -185,11 +217,18 @@ void be_spill_phi(spill_env_t *env, ir_node *node) {
        pset_insert_ptr(env->mem_phis, node);
 
        // create spillinfos for the phi arguments
-       get_spillinfo(env, node);
+       spill = 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);
        }
+
+       // if we had a spill for the phi value before, then remove this spill from
+       // schedule, as we will remove it in the insert spill/reload phase
+       if(spill->spill != NULL && !is_Phi(spill->spill)) {
+               spill->old_spill = spill->spill;
+               spill->spill = NULL;
+       }
 }
 
 /*
@@ -292,6 +331,17 @@ static void spill_phi(spill_env_t *env, spill_info_t *spillinfo) {
 
                set_irn_n(spillinfo->spill, i, arg_info->spill);
        }
+
+       // rewire reloads from old_spill to phi
+       if(spillinfo->old_spill != NULL) {
+               const ir_edge_t *edge, *next;
+               foreach_out_edge_safe(spillinfo->old_spill, edge, next) {
+                       ir_node* reload = get_edge_src_irn(edge);
+                       assert(be_is_Reload(reload) || is_Phi(reload));
+                       set_irn_n(reload, get_edge_src_pos(edge), spillinfo->spill);
+               }
+               spillinfo->old_spill = NULL;
+       }
 }
 
 /**
@@ -377,15 +427,9 @@ static int is_remat_node(spill_env_t *env, ir_node *node) {
 
        assert(!be_is_Spill(node));
 
-       if(be_is_Reload(node))
-               return 1;
-
-       // TODO why does arch_irn_is say rematerializable anyway?
-       if(be_is_Barrier(node))
-               return 0;
-
-       if(arch_irn_is(arch_env, node, rematerializable))
+       if(arch_irn_is(arch_env, node, rematerializable)) {
                return 1;
+       }
 
        if(be_is_StackParam(node))
                return 1;
@@ -416,8 +460,9 @@ static int check_remat_conditions_costs(spill_env_t *env, ir_node *spilled, ir_n
        } else {
                costs += arch_get_op_estimated_cost(env->arch_env, spilled);
        }
-       if(parentcosts + costs >= REMAT_COST_LIMIT)
+       if(parentcosts + costs >= REMAT_COST_LIMIT) {
                return REMAT_COST_LIMIT;
+       }
 
        argremats = 0;
        for(i = 0, arity = get_irn_arity(spilled); i < arity; ++i) {
@@ -435,7 +480,6 @@ static int check_remat_conditions_costs(spill_env_t *env, ir_node *spilled, ir_n
                }
                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;
@@ -445,7 +489,7 @@ static int check_remat_conditions_costs(spill_env_t *env, ir_node *spilled, ir_n
 }
 
 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);
+       int costs = check_remat_conditions_costs(env, spilled, reloader, 0);
 
        return costs < REMAT_COST_LIMIT;
 }
@@ -481,6 +525,7 @@ static ir_node *do_remat(spill_env_t *env, ir_node *spilled, ir_node *reloader)
                get_irn_arity(spilled),
                ins);
        copy_node_attr(spilled, res);
+       new_backedge_info(res);
 
        DBG((env->dbg, LEVEL_1, "Insert remat %+F before reloader %+F\n", res, reloader));
 
@@ -491,6 +536,27 @@ static ir_node *do_remat(spill_env_t *env, ir_node *spilled, ir_node *reloader)
        return res;
 }
 
+int be_get_reload_costs(spill_env_t *env, ir_node *to_spill, ir_node *before) {
+       spill_info_t *spill_info;
+
+       // is the node rematerializable?
+       int costs = check_remat_conditions_costs(env, to_spill, before, 0);
+       if(costs < REMAT_COST_LIMIT)
+               return costs;
+
+       // do we already have a spill?
+       spill_info = find_spillinfo(env, to_spill);
+       if(spill_info != NULL && spill_info->spill != NULL)
+               return env->reload_cost;
+
+       return env->spill_cost + env->reload_cost;
+}
+
+int be_get_reload_costs_on_edge(spill_env_t *env, ir_node *to_spill, ir_node *block, int pos) {
+       ir_node *before = get_reload_insertion_point(block, pos);
+       return be_get_reload_costs(env, to_spill, before);
+}
+
 /*
  *  ___                     _     ____      _                 _
  * |_ _|_ __  ___  ___ _ __| |_  |  _ \ ___| | ___   __ _  __| |___
@@ -505,7 +571,6 @@ void be_insert_spills_reloads(spill_env_t *env) {
        spill_info_t *si;
 
        /* process each spilled node */
-       DBG((env->dbg, LEVEL_1, "Insert spills and reloads:\n"));
        for(si = set_first(env->spills); si; si = set_next(env->spills)) {
                reloader_t *rld;
                ir_mode *mode = get_irn_mode(si->spilled_node);
@@ -536,13 +601,10 @@ void be_insert_spills_reloads(spill_env_t *env) {
                }
 
                del_pset(values);
-       }
 
-       // reloads are placed now, but we might reuse the spill environment for further spilling decisions
-       del_set(env->spills);
-       env->spills = new_set(cmp_spillinfo, 1024);
+               si->reloaders = NULL;
+       }
 
        be_remove_dead_nodes_from_schedule(env->chordal_env->irg);
-       //be_liveness_add_missing(env->chordal_env->lv);
        be_liveness_recompute(env->chordal_env->lv);
 }