fehler64 (emit a 0 if none of the addressmode things is set)
[libfirm] / ir / be / bespillbelady2.c
index 60ec142..6551b1a 100644 (file)
@@ -78,7 +78,9 @@
 #define DBG_WORKSET 128
 #define DBG_GLOBAL  256
 
-#define DEAD UINT_MAX
+#define DEAD     UINT_MAX
+#define LIVE_END (DEAD-1)
+
 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
 
 /**
@@ -89,8 +91,7 @@ typedef struct _loc_t {
   unsigned time;       /**< A use time (see beuses.h). */
   unsigned version;    /**< That is used in the global pass below.
                                                 For usage see the comments below.
-                                                In the local belady pass, this is not
-                                                important. */
+                                                In the local belady pass, this is not important. */
 } loc_t;
 
 typedef struct _workset_t {
@@ -121,7 +122,7 @@ static int loc_compare(const void *a, const void *b)
 {
        const loc_t *p = a;
        const loc_t *q = b;
-       return (int) p->time - (int) q->time;
+       return (p->time > q->time) - (p->time < q->time);
 }
 
 static INLINE void workset_print(const workset_t *w)
@@ -351,8 +352,31 @@ static INLINE void advance_current_use(block_info_t *bi, const ir_node *irn)
        phase_set_irn_data(&bi->next_uses, irn, use->next);
 }
 
+static INLINE unsigned get_curr_distance(block_info_t *bi, const ir_node *irn, int is_usage)
+{
+       belady_env_t *env = bi->bel;
+       next_use_t *use   = get_current_use(bi, irn);
+       int curr_step     = sched_get_time_step(env->instr);
+       int flags         = arch_irn_get_flags(env->arch, irn);
+
+       assert(!(flags & arch_irn_flags_ignore));
+
+       /* We have to keep nonspillable nodes in the workingset */
+       if(flags & arch_irn_flags_dont_spill)
+               return 0;
+
+       if (!is_usage && use && use->step == curr_step)
+               use = use->next;
+
+       if (use) {
+               assert(use->step >= curr_step);
+               return use->step - curr_step;
+       }
 
-static INLINE int is_local_phi(const ir_node *irn, const ir_node *bl)
+       return be_is_live_end(env->lv, bi->bl, irn) ? LIVE_END : DEAD;
+}
+
+static INLINE int is_local_phi(const ir_node *bl, const ir_node *irn)
 {
        return is_Phi(irn) && get_nodes_block(irn) == bl;
 }
@@ -370,7 +394,7 @@ static INLINE int is_local_phi(const ir_node *irn, const ir_node *bl)
  */
 static INLINE int is_transport_in(const ir_node *bl, const ir_node *irn)
 {
-       return is_local_phi(irn, bl) || get_nodes_block(irn) != bl;
+       return is_local_phi(bl, irn) || get_nodes_block(irn) != bl;
 }
 
 /**
@@ -413,7 +437,7 @@ static void displace(block_info_t *bi, workset_t *new_vals, int is_usage) {
                                assert(use);
                                assert(sched_get_time_step(env->instr) == use->step);
                                if (is_transport_in(bi->bl, val) && use->is_first_use) {
-                                       DBG((dbg, DBG_DECIDE, "entrance node %+F, capacity %d:\n", val, bi->pressure));
+                                       DBG((dbg, DBG_DECIDE, "entrance node %+F, pressure %d:\n", val, bi->pressure));
                                        if (bi->pressure < env->n_regs) {
                                                workset_insert(env, bi->entrance_reg, val);
                                                insert_reload = 0;
@@ -427,8 +451,7 @@ static void displace(block_info_t *bi, workset_t *new_vals, int is_usage) {
                                        be_add_reload(env->senv, val, env->instr, env->cls, 1);
                                }
                        }
-               }
-               else {
+               } else {
                        assert(is_usage || "Defined value already in workset?!?");
                        DBG((dbg, DBG_DECIDE, "    skip %+F\n", val));
                }
@@ -443,20 +466,15 @@ static void displace(block_info_t *bi, workset_t *new_vals, int is_usage) {
 
        /* Only make more free room if we do not have enough */
        if (len > max_allowed) {
-               int curr_step = sched_get_time_step(env->instr);
+               // int curr_step = sched_get_time_step(env->instr);
 
                DBG((dbg, DBG_DECIDE, "    disposing %d values\n", len - max_allowed));
 
                /* get current next-use distance */
                for (i = 0; i < ws->len; ++i) {
-                       ir_node *val = workset_get_val(ws, i);
-                       next_use_t *use = phase_get_irn_data(&bi->next_uses, val);
-                       assert(use == NULL || use->step >= curr_step);
-
-                       if (!is_usage && use)
-                               use = use->next;
-
-                       workset_set_time(ws, i, use ? (unsigned) (use->step - curr_step) : DEAD);
+                       ir_node *val  = workset_get_val(ws, i);
+                       unsigned dist = get_curr_distance(bi, val, is_usage);
+                       workset_set_time(ws, i, dist);
                }
 
                /* sort entries by increasing nextuse-distance*/
@@ -487,6 +505,7 @@ static void displace(block_info_t *bi, workset_t *new_vals, int is_usage) {
 static void belady(ir_node *block, void *data) {
        belady_env_t *env        = data;
        block_info_t *block_info = new_block_info(env, block);
+       void *obst_state         = obstack_base(&env->ob);
 
        workset_t *new_vals;
        ir_node *irn;
@@ -564,12 +583,14 @@ static void belady(ir_node *block, void *data) {
        }
 
        phase_free(&block_info->next_uses);
+       obstack_free(&env->ob, obst_state);
 
        /* Remember end-workset for this block */
        block_info->ws_end = workset_clone(env, &env->ob, env->ws);
        DBG((dbg, DBG_WSETS, "End workset for %+F:\n", block));
        workset_foreach(block_info->ws_end, irn, iter)
                DBG((dbg, DBG_WSETS, "  %+F (%u)\n", irn, workset_get_time(block_info->ws_end, iter)));
+       DBG((dbg, DBG_WSETS, "Max pressure in block: %d\n", block_info->pressure));
 }
 
 /*
@@ -588,7 +609,8 @@ static int block_freq_gt(const void *a, const void *b)
        const ir_node * const *q = b;
        block_info_t *pi = get_block_info(*p);
        block_info_t *qi = get_block_info(*q);
-       return qi->exec_freq - pi->exec_freq;
+       double diff = qi->exec_freq - pi->exec_freq;
+       return (diff > 0) - (diff < 0);
 }
 
 typedef struct _block_end_state_t {
@@ -602,13 +624,32 @@ typedef struct _block_end_state_t {
 
 typedef struct _global_end_state_t {
        belady_env_t *env;
-       bitset_t *failed_phis;
+       bitset_t *succ_phis;
        struct obstack obst;
        block_end_state_t *end_info;
        unsigned gauge;
        unsigned version;
 } global_end_state_t;
 
+typedef struct {
+       void *obst_level;
+       unsigned gauge;
+} rollback_info_t;
+
+static INLINE rollback_info_t trans_begin(global_end_state_t *ges)
+{
+       rollback_info_t rb;
+       rb.obst_level = obstack_base(&ges->obst);
+       rb.gauge      = ges->gauge;
+       return rb;
+}
+
+static INLINE void trans_rollback(global_end_state_t *ges, rollback_info_t *rb)
+{
+       ges->gauge = rb->gauge;
+       obstack_free(&ges->obst, rb->obst_level);
+}
+
 static block_end_state_t *get_block_end_state(global_end_state_t *state, ir_node *bl, ir_node *irn)
 {
        unsigned i;
@@ -638,9 +679,9 @@ static block_end_state_t *get_block_end_state(global_end_state_t *state, ir_node
        }
 }
 
-static double can_bring_in(global_end_state_t *ges, ir_node *bl, ir_node *irn, int level);
+static double can_bring_in(global_end_state_t *ges, ir_node *bl, ir_node *irn, double limit, int level);
 
-static double can_make_available_at_end(global_end_state_t *ges, ir_node *bl, ir_node *irn, int level)
+static double can_make_available_at_end(global_end_state_t *ges, ir_node *bl, ir_node *irn, double limit, int level)
 {
        block_end_state_t *bes = get_block_end_state(ges, bl, irn);
        workset_t *end         = bes->end_state;
@@ -733,8 +774,7 @@ static double can_make_available_at_end(global_end_state_t *ges, ir_node *bl, ir
                        DBG((dbg, DBG_GLOBAL, "\t%2Dthe end set has %d free slots\n",
                                                level, n_regs - len));
                        slot = len;
-               }
-               else {
+               } else {
                        for (i = 0; i < len; ++i)
                                if (end->vals[i].version < ges->version)
                                        break;
@@ -747,10 +787,11 @@ static double can_make_available_at_end(global_end_state_t *ges, ir_node *bl, ir
                }
 
                if (slot >= 0) {
-                       int gauge           = ges->gauge;
+                       rollback_info_t rb  = trans_begin(ges);
                        ir_node *ins_before = block_info_get_last_ins(bi);
                        double reload_here  = be_get_reload_costs(bi->bel->senv, irn, ins_before);
-                       double bring_in     = bi->pressure < n_regs ? can_bring_in(ges, bl, irn, level + 1) : HUGE_VAL;
+                       double new_limit    = MIN(reload_here, limit);
+                       double bring_in     = bi->pressure < n_regs ? can_bring_in(ges, bl, irn, new_limit, level + 1) : HUGE_VAL;
 
                        DBG((dbg, DBG_GLOBAL, "\t%2Dthere is a free slot. capacity=%d, reload here=%f, bring in=%f\n",
                                                level, n_regs - bi->pressure, reload_here, bring_in));
@@ -761,18 +802,17 @@ static double can_make_available_at_end(global_end_state_t *ges, ir_node *bl, ir
                         * the gauge.
                         */
                        if (reload_here <= bring_in) {
-                               ges->gauge = gauge;
+                               trans_rollback(ges, &rb);
                                bes->costs = reload_here;
                                bes->reload_at_end = 1;
-                       }
-
-                       else {
+                       } else {
                                bes->live_through = 1;
                                bes->costs = bring_in;
                        }
 
                        end->vals[slot].irn     = irn;
                        end->vals[slot].version = ges->version;
+                       end->len = MAX(end->len, slot + 1);
                }
        }
 
@@ -781,33 +821,31 @@ end:
        return bes->costs;
 }
 
-static double can_bring_in(global_end_state_t *ges, ir_node *bl, ir_node *irn, int level)
+static double can_bring_in(global_end_state_t *ges, ir_node *bl, ir_node *irn, double limit, int level)
 {
        double glob_costs = HUGE_VAL;
-       int def_block     = bl == get_nodes_block(irn);
-       int phi           = is_Phi(irn);
 
        DBG((dbg, DBG_GLOBAL, "\t%2Dcan bring in for %+F at block %+F\n", level, irn, bl));
 
-       if (phi || !def_block) {
+       if (is_transport_in(bl, irn)) {
                int i, n           = get_irn_arity(bl);
                ir_node **nodes    = alloca(get_irn_arity(bl) * sizeof(nodes[0]));
+               rollback_info_t rb = trans_begin(ges);
 
-               int gauge_begin    = ges->gauge;
 
                glob_costs = 0.0;
                for (i = 0; i < n; ++i) {
                        ir_node *pr = get_Block_cfgpred_block(bl, i);
-                       ir_node *op = phi && def_block ? get_irn_n(irn, i) : irn;
-                       double c    = can_make_available_at_end(ges, pr, op, level + 1);
+                       ir_node *op = is_local_phi(bl, irn) ? get_irn_n(irn, i) : irn;
+                       double c    = can_make_available_at_end(ges, pr, op, limit, level + 1);
 
-                       if (c >= HUGE_VAL) {
-                               ges->gauge = gauge_begin;
+                       glob_costs += c;
+
+                       if (glob_costs >= limit) {
                                glob_costs = HUGE_VAL;
+                               trans_rollback(ges, &rb);
                                goto end;
                        }
-
-                       glob_costs += c;
                }
        }
 
@@ -825,7 +863,10 @@ static void materialize_and_commit_end_state(global_end_state_t *ges)
        for (i = 0; i < ges->gauge; ++i) {
                block_end_state_t *bes = &ges->end_info[i];
                block_info_t *bi       = get_block_info(bes->bl);
-               int idx;
+               int idx, end_pressure;
+
+               DBG((dbg, DBG_GLOBAL, "\t\t%+F in %+F, cost %f through: %d, rel: %d\n",
+                               bes->irn, bes->bl, bes->costs, bes->live_through, bes->reload_at_end));
 
                /* insert the reload if the val was reloaded at the block's end */
                if (bes->reload_at_end) {
@@ -833,14 +874,11 @@ static void materialize_and_commit_end_state(global_end_state_t *ges)
                        DBG((dbg, DBG_GLOBAL, "\t\tadding reload of %+F at end of %+F\n", bes->irn, bes->bl));
                }
 
-               /*
-                * if the variable is live through the block,
-                * update the pressure indicator.
-                */
-               bi->pressure += bes->live_through;
-
                idx = workset_get_index(bes->end_state, bes->irn);
 
+               if (is_local_phi(bes->bl, bes->irn) && bes->live_through)
+                       bitset_add_irn(ges->succ_phis, bes->irn);
+
                /*
                 * set the version number in the workset.
                 * That will mark this value as fixed in the end set
@@ -854,6 +892,23 @@ static void materialize_and_commit_end_state(global_end_state_t *ges)
                        bes->end_state->vals[idx].version = ges->version;
                        workset_copy(env, bi->ws_end, bes->end_state);
                }
+
+               end_pressure = 0;
+               for (idx = workset_get_length(bes->end_state) - 1; idx >= 0; --idx)
+                       if (bes->end_state->vals[idx].version >= ges->version)
+                               end_pressure += 1;
+
+               /*
+                * if the variable is live through the block,
+                * update the pressure indicator.
+                */
+               DBG((dbg, DBG_GLOBAL, "\t\told pressure %d, ", bi->pressure));
+
+               bi->pressure = MAX(bi->pressure + bes->live_through, end_pressure);
+
+               DBG((dbg, DBG_GLOBAL, "new pressure: %d, end pressure: %d, end length: %d\n",
+                                       bi->pressure, end_pressure, workset_get_length(bes->end_state)));
+
        }
 }
 
@@ -882,7 +937,7 @@ static void fix_block_borders(global_end_state_t *ges, ir_node *block) {
 
                DBG((dbg, DBG_GLOBAL, "\ttrans in var %+F, version %x\n", irn, ges->version));
 
-               bring_in_costs = can_bring_in(ges, block, irn, 1);
+               bring_in_costs = can_bring_in(ges, block, irn, local_costs, 1);
 
                DBG((dbg, DBG_GLOBAL, "\tbring in: %f, local: %f", bring_in_costs, local_costs));
 
@@ -891,20 +946,17 @@ static void fix_block_borders(global_end_state_t *ges, ir_node *block) {
                 * in a register at the entrance of the block
                 * or it is too costly, so we have to do the reload locally
                 */
-               if (bring_in_costs > local_costs) {
-
+               if (bring_in_costs >= local_costs) {
                        DBG((dbg, DBG_GLOBAL, " -> do local reload\n"));
                        be_add_reload(env->senv, irn, bi->first_non_in, env->cls, 1);
-
+               } else {
                        /*
                         * if the transport-in was a phi (that is actually used in block)
                         * it will no longer remain and we have to spill it completely.
                         */
-                       if (is_local_phi(irn, block))
-                               bitset_add_irn(ges->failed_phis, irn);
-               }
+                       if (is_local_phi(block, irn))
+                               bitset_add_irn(ges->succ_phis, irn);
 
-               else  {
                        DBG((dbg, DBG_GLOBAL, " -> do remote reload\n"));
                        materialize_and_commit_end_state(ges);
                }
@@ -919,11 +971,11 @@ static void global_assign(belady_env_t *env)
        int i;
 
        obstack_init(&ges.obst);
-       ges.gauge       = 0;
-       ges.env         = env;
-       ges.version     = -1;
-       ges.end_info    = NEW_ARR_F(block_end_state_t, env->n_blocks);
-       ges.failed_phis = bitset_irg_obstack_alloc(&env->ob, env->irg);
+       ges.gauge     = 0;
+       ges.env       = env;
+       ges.version   = -1;
+       ges.end_info  = NEW_ARR_F(block_end_state_t, env->n_blocks);
+       ges.succ_phis = bitset_irg_obstack_alloc(&env->ob, env->irg);
 
        /*
         * sort the blocks according to execution frequency.
@@ -947,11 +999,12 @@ static void global_assign(belady_env_t *env)
                                break;
 
                        if (arch_irn_consider_in_reg_alloc(env->arch, env->cls, irn)
-                                       && bitset_contains_irn(ges.failed_phis, irn))
+                                       && !bitset_contains_irn(ges.succ_phis, irn))
                                be_spill_phi(env->senv, irn);
                }
        }
 
+       DEL_ARR_F(ges.end_info);
 }
 
 static void collect_blocks(ir_node *bl, void *data)