Indentation.
[libfirm] / ir / be / bespillbelady3.c
index 29a196a..738148d 100644 (file)
  * @version     $Id$
  *
  * TODO:
- *   - handle phis correctly, decide wether we should spill them
- *   - merge multiple start worksets of blocks
+ *   - handle phis correctly, decide whether we should spill them ~ok
+ *   - merge multiple start worksets of blocks ~ok
  *   - how to and when to do the tentative phase...
  */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <stdbool.h>
+
 #include "debug.h"
 #include "list.h"
 #include "pdeq.h"
@@ -40,6 +42,8 @@
 #include "irnode_t.h"
 #include "irprintf.h"
 #include "iredges_t.h"
+#include "irloop_t.h"
+#include "irgwalk.h"
 #include "execfreq.h"
 
 #include "bemodule.h"
 
 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
 
+typedef struct loop_edge_t loop_edge_t;
+struct loop_edge_t {
+       ir_node     *block;
+       int          pos;
+       loop_edge_t *next;
+};
+
+typedef struct loop_info_t loop_info_t;
+struct loop_info_t {
+       loop_edge_t *entry_edges;
+       loop_edge_t *exit_edges;
+       size_t       max_register_pressure;
+};
+
 typedef struct worklist_entry_t worklist_entry_t;
 struct worklist_entry_t {
        struct list_head  head;
        ir_node          *value;
-       unsigned          timestep;
        ir_node          *reload_point;
+       worklist_entry_t *next_reload;
 };
 
 typedef struct worklist_t worklist_t;
 struct worklist_t {
        struct list_head  live_values;
        size_t            n_live_values;
-       unsigned          current_timestep;
+};
+
+typedef struct block_info_t block_info_t;
+struct block_info_t {
+       worklist_t *start_worklist;
+       worklist_t *end_worklist;
 };
 
 static const arch_env_t            *arch_env;
@@ -71,14 +94,19 @@ static const arch_register_class_t *cls;
 static struct obstack               obst;
 static spill_env_t                 *senv;
 static size_t                       n_regs;
-static int                          tentative_mode;
+static size_t                       max_register_pressure;
+static bool                         tentative_mode;
+static bool                         should_have_reached_fixpoint;
 static ir_exec_freq                *exec_freq;
 
-static void init_worklist(worklist_t *worklist, unsigned timestep)
+static worklist_t *new_worklist(void)
 {
+       worklist_t *worklist = obstack_alloc(&obst, sizeof(worklist[0]));
+
        INIT_LIST_HEAD(&worklist->live_values);
        worklist->n_live_values    = 0;
-       worklist->current_timestep = timestep;
+
+       return worklist;
 }
 
 static void mark_irn_not_visited(ir_node *node)
@@ -86,6 +114,23 @@ static void mark_irn_not_visited(ir_node *node)
        set_irn_visited(node, get_irg_visited(current_ir_graph) - 1);
 }
 
+static bool worklist_contains(const ir_node *node)
+{
+       return irn_visited(node);
+}
+
+static block_info_t *get_block_info(ir_node *block)
+{
+       block_info_t *info = get_irn_link(block);
+       if (info != NULL)
+               return info;
+
+       info = obstack_alloc(&obst, sizeof(info[0]));
+       memset(info, 0, sizeof(info[0]));
+       set_irn_link(block, info);
+       return info;
+}
+
 static void deactivate_worklist(const worklist_t *worklist)
 {
        struct list_head *entry;
@@ -93,7 +138,7 @@ static void deactivate_worklist(const worklist_t *worklist)
        list_for_each(entry, &worklist->live_values) {
                worklist_entry_t *wl_entry
                        = list_entry(entry, worklist_entry_t, head);
-               assert(irn_visited(wl_entry->value));
+               assert(worklist_contains(wl_entry->value));
                mark_irn_not_visited(wl_entry->value);
                set_irn_link(wl_entry->value, NULL);
        }
@@ -106,50 +151,90 @@ static void activate_worklist(const worklist_t *worklist)
        list_for_each(entry, &worklist->live_values) {
                worklist_entry_t *wl_entry
                        = list_entry(entry, worklist_entry_t, head);
-               ir_node          *value = wl_entry->value;
-
-               assert(irn_not_visited(value));
-               mark_irn_visited(value);
-               set_irn_link(value, wl_entry);
+               assert(!worklist_contains(wl_entry->value));
+               mark_irn_visited(wl_entry->value);
+               set_irn_link(wl_entry->value, wl_entry);
        }
 }
 
-static worklist_t *duplicate_worklist(const worklist_t *worklist,
-                                      ir_node *block,
-                                      ir_node *succ_block, int succ_pos)
+/**
+ * duplicate a worklist and directly activates it
+ */
+static worklist_t *duplicate_activate_worklist(const worklist_t *worklist,
+               ir_node *block, ir_node *succ_block, int succ_pos)
 {
-       ir_node          *reload_point = NULL;
+       ir_node          *reload_point  = NULL;
+       size_t            n_live_values = 0;
+       worklist_t       *new_worklist;
        struct list_head *entry;
 
-       if(succ_block != NULL && get_Block_n_cfgpreds(succ_block) > 1) {
+       if (succ_block != NULL &&
+                       (get_Block_n_cfgpreds(succ_block) > 1
+                        || get_irn_n_edges_kind(block, EDGE_KIND_BLOCK) > 1)) {
                reload_point = be_get_end_of_block_insertion_point(block);
        }
 
-       worklist_t *new_worklist = obstack_alloc(&obst, sizeof(new_worklist[0]));
+       new_worklist = obstack_alloc(&obst, sizeof(new_worklist[0]));
        INIT_LIST_HEAD(&new_worklist->live_values);
 
-       new_worklist->current_timestep = worklist->current_timestep;
-       new_worklist->n_live_values    = worklist->n_live_values;
-
        list_for_each(entry, &worklist->live_values) {
-               worklist_entry_t *wl_entry
-                       = list_entry(entry, worklist_entry_t, head);
-               worklist_entry_t *new_entry
-                       = obstack_alloc(&obst, sizeof(new_entry[0]));
-               ir_node          *value = wl_entry->value;
+               worklist_entry_t *wl_entry  = list_entry(entry, worklist_entry_t, head);
+               ir_node          *value     = wl_entry->value;
+               worklist_entry_t *new_entry;
 
-               if(is_Phi(value) && get_nodes_block(value) == succ_block) {
+               if (is_Phi(value) && get_nodes_block(value) == succ_block) {
                        value = get_Phi_pred(value, succ_pos);
+
+                       /* can happen for unknown phi preds */
+                       if (!arch_irn_consider_in_reg_alloc(arch_env, cls, value))
+                               continue;
                }
 
-               new_entry->value        = value;
-               new_entry->timestep     = wl_entry->timestep;
-               if(reload_point != NULL) {
+               new_entry = obstack_alloc(&obst, sizeof(new_entry[0]));
+               new_entry->value = value;
+               if (reload_point != NULL) {
                        new_entry->reload_point = reload_point;
                } else {
                        new_entry->reload_point = wl_entry->reload_point;
                }
+               new_entry->next_reload = NULL;
+
+               if (irn_not_visited(value)) {
+                       list_add_tail(&new_entry->head, &new_worklist->live_values);
+                       ++n_live_values;
+
+                       mark_irn_visited(value);
+                       set_irn_link(value, new_entry);
+               }
+#if 0
+               else {
+                       /* the only way we might visit a value again, is when we get it as
+                        * phi predecessor */
+                       assert(is_Phi(wl_entry->value)
+                                       && get_nodes_block(wl_entry->value) == succ_block);
+               }
+#endif
+       }
+       new_worklist->n_live_values = n_live_values;
+
+       return new_worklist;
+}
+
+static worklist_t *duplicate_worklist(const worklist_t *worklist)
+{
+       worklist_t       *new_worklist;
+       struct list_head *entry;
 
+       new_worklist = obstack_alloc(&obst, sizeof(new_worklist[0]));
+       INIT_LIST_HEAD(&new_worklist->live_values);
+       new_worklist->n_live_values = worklist->n_live_values;
+
+       list_for_each(entry, &worklist->live_values) {
+               worklist_entry_t *wl_entry  = list_entry(entry, worklist_entry_t, head);
+               worklist_entry_t *new_entry
+                       = obstack_alloc(&obst, sizeof(new_entry[0]));
+
+               memcpy(new_entry, wl_entry, sizeof(new_entry[0]));
                list_add_tail(&new_entry->head, &new_worklist->live_values);
        }
 
@@ -161,66 +246,198 @@ static void print_worklist(const worklist_t *worklist, int level)
 {
        struct list_head *entry;
 
-       DB((dbg, level, "%d values (TS %u): ", worklist->n_live_values,
-           worklist->current_timestep));
+       DB((dbg, level, "%d values: ", worklist->n_live_values));
        list_for_each(entry, &worklist->live_values) {
                worklist_entry_t *wl_entry
                        = list_entry(entry, worklist_entry_t, head);
 
-               //DB((dbg, level, "%+F(%+F) ", wl_entry->value,
-               //    wl_entry->reload_point));
                DB((dbg, level, "%+F ", wl_entry->value));
        }
 }
 #endif
 
+
+
+static void clear_loop_info(ir_loop *loop)
+{
+       int n_elements = get_loop_n_elements(loop);
+       int i;
+
+       loop->link = NULL;
+       for (i = 0; i < n_elements; ++i) {
+               loop_element element = get_loop_element(loop, i);
+               if (*element.kind != k_ir_loop)
+                       continue;
+
+               clear_loop_info(element.son);
+       }
+}
+
+static loop_info_t *get_loop_info(ir_loop *loop)
+{
+       loop_info_t *info = loop->link;
+       if (info != NULL)
+               return info;
+
+       info = obstack_alloc(&obst, sizeof(info[0]));
+       memset(info, 0, sizeof(info[0]));
+       loop->link = info;
+       return info;
+}
+
+/**
+ * constructs loop in+out edges when called in a block walker
+ */
+static void construct_loop_edges(ir_node *block, void *data)
+{
+       int      n_cfgpreds = get_Block_n_cfgpreds(block);
+       ir_loop *loop       = get_irn_loop(block);
+       int      i;
+
+       (void) data;
+
+       for (i = 0; i < n_cfgpreds; ++i) {
+               ir_node     *cfgpred_block = get_Block_cfgpred_block(block, i);
+               ir_loop     *cfgpred_loop  = get_irn_loop(cfgpred_block);
+               loop_edge_t *edge;
+               bool        is_exit_edge;
+               ir_loop     *l, *goal;
+
+               if (cfgpred_loop == loop)
+                       continue;
+
+               /* critical edges are splitted, so we can't jump from 1 loop
+                * directly into another without going out of it */
+               assert(get_loop_depth(cfgpred_loop) != get_loop_depth(loop));
+
+               /* edge out of loop */
+               if (get_loop_depth(cfgpred_loop) > get_loop_depth(loop)) {
+                       is_exit_edge = true;
+                       l            = cfgpred_loop;
+                       goal         = loop;
+               } else {
+                       is_exit_edge = false;
+                       l            = loop;
+                       goal         = cfgpred_loop;
+               }
+
+               /* this might be a jump out/in multiple loops */
+               do {
+                       loop_info_t *l_info = get_loop_info(l);
+
+                       edge = obstack_alloc(&obst, sizeof(edge[0]));
+                       memset(edge, 0, sizeof(edge[0]));
+                       edge->block = block;
+                       edge->pos   = i;
+
+                       if (is_exit_edge) {
+                               ir_fprintf(stderr, "Loop %p exit edge %+F:%d\n",
+                                          l, block, i);
+                               edge->next         = l_info->exit_edges;
+                               l_info->exit_edges = edge;
+                               assert(get_loop_depth(loop) < get_loop_depth(l));
+                       } else {
+                               ir_fprintf(stderr, "Loop %p entry edge %+F:%d\n",
+                                          l, block, i);
+                               edge->next          = l_info->entry_edges;
+                               l_info->entry_edges = edge;
+                               assert(get_loop_depth(cfgpred_loop) < get_loop_depth(l));
+                       }
+                       l = get_loop_outer_loop(l);
+               } while(l != goal);
+       }
+}
+
+
+
+
 static void place_reload(worklist_entry_t *entry)
 {
-       if(tentative_mode)
+       if (tentative_mode)
                return;
-       DB((dbg, LEVEL_1, "reload of %+F before %+F", entry->value,
-           entry->reload_point));
-       be_add_reload(senv, entry->value, entry->reload_point, cls, 1);
+
+       /* walk list of reload points */
+       do {
+               DB((dbg, LEVEL_1, "reload of %+F before %+F\n", entry->value,
+                   entry->reload_point));
+               assert(entry->reload_point != NULL);
+               be_add_reload(senv, entry->value, entry->reload_point, cls, 1);
+               entry->reload_point = NULL;
+
+               entry = entry->next_reload;
+       } while(entry != NULL);
 }
 
-static void spill_non_live_nodes(const worklist_t *worklist)
+#if 0
+/**
+ * does stuff required for non-active worklists: spills all values not live
+ * in the active worklist; links live values to the current worklist
+ */
+static void process_non_active_worklist(const worklist_t *worklist,
+                                        worklist_t *current_worklist)
 {
        struct list_head *entry;
 
        list_for_each(entry, &worklist->live_values) {
-               worklist_entry_t *wl_entry
-                       = list_entry(entry, worklist_entry_t, head);
-               ir_node          *value = wl_entry->value;
-
-               if(irn_visited(value))
-                       continue;
-
-               place_reload(wl_entry);
+               worklist_entry_t *wl_entry = list_entry(entry, worklist_entry_t, head);
+               ir_node          *value    = wl_entry->value;
+
+               if (!worklist_contains(value)) {
+                       /* if we still have room in the worklist, then we can simply take
+                        * the value */
+                       if (current_worklist->n_live_values < n_regs) {
+                               worklist_entry_t *new_entry
+                                       = obstack_alloc(&obst, sizeof(new_entry[0]));
+
+                               new_entry->value        = value;
+                               new_entry->reload_point = wl_entry->reload_point;
+                               new_entry->next_reload  = wl_entry->next_reload;
+                               set_irn_link(value, new_entry);
+                               mark_irn_visited(value);
+                               list_add(&new_entry->head, &current_worklist->live_values);
+                               ++current_worklist->n_live_values;
+                       } else {
+                               /* value is not in current worklist, place reloads */
+                               place_reload(wl_entry);
+                       }
+               } else {
+                       /* value is in current worklist, link it with the reload point
+                        * from this path */
+                       worklist_entry_t *active_entry = get_irn_link(value);
+                       wl_entry->next_reload          = active_entry->next_reload;
+                       active_entry->next_reload      = wl_entry;
+               }
        }
 }
+#endif
 
 /**
  * makes sure the worklist contains not more than n_regs - room_needed entries
  */
 static void make_room(worklist_t *worklist, size_t room_needed)
 {
-       int spills_needed = worklist->n_live_values + room_needed - n_regs;
-       if(spills_needed > 0) {
-               int               i     = spills_needed;
-               struct list_head *entry = worklist->live_values.next;
-               for(i = spills_needed; i > 0; --i) {
-                       struct list_head *next = entry->next;
-                       worklist_entry_t *wl_entry
-                               = list_entry(entry, worklist_entry_t, head);
-                       assert(irn_visited(wl_entry->value));
-                       mark_irn_not_visited(wl_entry->value);
-                       place_reload(wl_entry);
-                       list_del(entry);
+       int               i;
+       int               spills_needed;
+       struct list_head *entry;
 
-                       entry = next;
-               }
-               worklist->n_live_values -= spills_needed;
+       spills_needed = worklist->n_live_values + room_needed - n_regs;
+       if (spills_needed <= 0)
+               return;
+
+       entry = worklist->live_values.next;
+       for(i = spills_needed; i > 0; --i) {
+               struct list_head *next = entry->next;
+               worklist_entry_t *wl_entry
+                       = list_entry(entry, worklist_entry_t, head);
+
+               assert(worklist_contains(wl_entry->value));
+               mark_irn_not_visited(wl_entry->value);
+               place_reload(wl_entry);
+               list_del(entry);
+
+               entry = next;
        }
+       worklist->n_live_values -= spills_needed;
 }
 
 /**
@@ -229,15 +446,17 @@ static void make_room(worklist_t *worklist, size_t room_needed)
  */
 static void val_used(worklist_t *worklist, ir_node *value, ir_node *sched_point)
 {
-       /* is the node in the worklist already? */
+       /* already in the worklist? move around, otherwise add at back */
        worklist_entry_t *entry = get_irn_link(value);
-       if(irn_visited(value)) {
+
+       assert(arch_irn_consider_in_reg_alloc(arch_env, cls, value));
+
+       if (worklist_contains(value)) {
                assert(entry != NULL);
 
-               assert(irn_visited(value));
                list_del(&entry->head);
        } else {
-               if(entry == NULL) {
+               if (entry == NULL) {
                        entry        = obstack_alloc(&obst, sizeof(entry[0]));
                        entry->value = value;
                        set_irn_link(value, entry);
@@ -247,8 +466,8 @@ static void val_used(worklist_t *worklist, ir_node *value, ir_node *sched_point)
                mark_irn_visited(value);
        }
 
-       entry->timestep     = worklist->current_timestep;
        entry->reload_point = sched_point;
+       entry->next_reload  = NULL;
        list_add_tail(&entry->head, &worklist->live_values);
 }
 
@@ -259,29 +478,30 @@ static void worklist_remove(worklist_t *worklist, ir_node *value)
        list_del(&entry->head);
        --worklist->n_live_values;
 
-       assert(irn_visited(value));
+       assert(worklist_contains(value));
        mark_irn_not_visited(value);
 }
 
+static void update_max_pressure(worklist_t *worklist)
+{
+       if (worklist->n_live_values > max_register_pressure)
+               max_register_pressure = worklist->n_live_values;
+}
+
 static void do_spilling(ir_node *block, worklist_t *worklist)
 {
        ir_node *node;
 
        assert(worklist != NULL);
 
-#ifdef DEBUG_libfirm
-       DB((dbg, LEVEL_1, "worklist at end of %+F:", block));
-       print_worklist(worklist, LEVEL_1);
-       DB((dbg, LEVEL_1, "\n"));
-#endif
-
        sched_foreach_reverse(block, node) {
                int    i, arity;
                size_t n_defs = 0;
 
                DB((dbg, LEVEL_2, "\t%+F... ", node));
+               update_max_pressure(worklist);
 
-               if(is_Phi(node)) {
+               if (is_Phi(node)) {
                        ir_node *node2;
                        /* TODO: if we have some free registers, then we could decide to
                         * not spill some phis (but not for phis where at least 1 input is
@@ -291,33 +511,35 @@ static void do_spilling(ir_node *block, worklist_t *worklist)
                        sched_foreach_reverse_from(node, node2) {
                                assert(is_Phi(node2));
 
-                               if(irn_visited(node2))
+                               if (worklist_contains(node2))
                                        continue;
-                               if(!arch_irn_consider_in_reg_alloc(arch_env, cls, node2))
+                               if (!arch_irn_consider_in_reg_alloc(arch_env, cls, node2))
                                        continue;
 
-                               be_spill_phi(senv, node2);
+                               if (!tentative_mode)
+                                       be_spill_phi(senv, node2);
                        }
+                       DB((dbg, LEVEL_2, "\n"));
                        break;
                }
 
                /* remove values defined by this instruction from the workset. Values
                 * defined but not in the workset need free registers */
-               if(get_irn_mode(node) == mode_T) {
+               if (get_irn_mode(node) == mode_T) {
                        const ir_edge_t *edge;
 
                        foreach_out_edge(node, edge) {
                                ir_node *proj = get_edge_src_irn(edge);
-                               if(!arch_irn_consider_in_reg_alloc(arch_env, cls, proj))
+                               if (!arch_irn_consider_in_reg_alloc(arch_env, cls, proj))
                                        continue;
-                               if(irn_visited(proj)) {
+                               if (worklist_contains(proj)) {
                                        worklist_remove(worklist, proj);
                                } else {
                                        ++n_defs;
                                }
                        }
-               } else if(arch_irn_consider_in_reg_alloc(arch_env, cls, node)) {
-                       if(irn_visited(node)) {
+               } else if (arch_irn_consider_in_reg_alloc(arch_env, cls, node)) {
+                       if (worklist_contains(node)) {
                                worklist_remove(worklist, node);
                        } else {
                                n_defs = 1;
@@ -327,12 +549,12 @@ static void do_spilling(ir_node *block, worklist_t *worklist)
                /* make sure we have enough free registers for the spills */
                make_room(worklist, n_defs);
 
-               /* put all values by the instruction into the workset */
+               /* put all values used by the instruction into the workset */
                arity = get_irn_arity(node);
                for(i = 0; i < arity; ++i) {
                        ir_node *use = get_irn_n(node, i);
 
-                       if(!arch_irn_consider_in_reg_alloc(arch_env, cls, use))
+                       if (!arch_irn_consider_in_reg_alloc(arch_env, cls, use))
                                continue;
 
                        val_used(worklist, use, node);
@@ -342,14 +564,14 @@ static void do_spilling(ir_node *block, worklist_t *worklist)
                 * some */
                make_room(worklist, 0);
 
-               ++worklist->current_timestep;
-
 #ifdef DEBUG_libfirm
                print_worklist(worklist, LEVEL_2);
                DB((dbg, LEVEL_2, "\n"));
 #endif
        }
 
+       update_max_pressure(worklist);
+
 #ifdef DEBUG_libfirm
        DB((dbg, LEVEL_1, "worklist at begin of %+F:", block));
        print_worklist(worklist, LEVEL_1);
@@ -357,26 +579,48 @@ static void do_spilling(ir_node *block, worklist_t *worklist)
 #endif
 }
 
+static bool worklists_equal(const worklist_t *wl1, const worklist_t *wl2)
+{
+       const struct list_head *i1 = &wl1->live_values;
+       const struct list_head *i2 = &wl2->live_values;
+
+       for ( ; i1 != &wl1->live_values && i2 != &wl2->live_values;
+                       i1 = i1->next, i2 = i2->next) {
+               worklist_entry_t *entry1 = list_entry(i1, worklist_entry_t, head);
+               worklist_entry_t *entry2 = list_entry(i2, worklist_entry_t, head);
+
+               if (entry1->value != entry2->value)
+                       return false;
+       }
+       /* both lists at the end */
+       if (i1 != &wl1->live_values || i2 != &wl2->live_values)
+               return false;
+
+       return true;
+}
+
 static void process_block(ir_node *block, void *env)
 {
+       block_info_t    *block_info      = NULL;
+       worklist_t      *worklist        = NULL;
+       double           best_execfreq   = -1;
+       ir_node         *best_succ_block = NULL;
+       int              best_pos        = -1;
        int              n_preds;
        const ir_edge_t *edge;
-       (void) env;
 
+       (void) env;
        DB((dbg, LEVEL_1, "Processing %+F\n", block));
 
        /* construct worklist */
-       worklist_t *worklist        = NULL;
-       double      best_execfreq   = -1;
-       ir_node    *best_succ_block = NULL;
-       int         best_pos        = -1;
        foreach_block_succ(block, edge) {
                ir_node *succ_block = get_edge_src_irn(edge);
                double   execfreq   = get_block_execfreq(exec_freq, succ_block);
 
-               if(execfreq > best_execfreq) {
-                       worklist_t *succ_worklist = get_irn_link(succ_block);
-                       if(succ_worklist != NULL) {
+               if (execfreq > best_execfreq) {
+                       block_info_t *block_info    = get_block_info(succ_block);
+                       worklist_t   *succ_worklist = block_info->start_worklist;
+                       if (succ_worklist != NULL) {
                                best_execfreq   = execfreq;
                                worklist        = succ_worklist;
                                best_succ_block = succ_block;
@@ -384,68 +628,339 @@ static void process_block(ir_node *block, void *env)
                        }
                }
        }
-       if(worklist == NULL) {
-               /* only the end-block has 0 successors */
-               assert(block == get_irg_end_block(get_irn_irg(block)));
+       if (worklist == NULL) {
+               /* at least 1 successor block must have been processed unless it was
+                * the end block */
+               assert(tentative_mode || block == get_irg_end_block(get_irn_irg(block)));
 
-               worklist = obstack_alloc(&obst, sizeof(worklist[0]));
-               init_worklist(worklist, 0);
+               worklist = new_worklist();
        } else {
-               worklist = duplicate_worklist(worklist, block, best_succ_block,
-                                             best_pos);
-               activate_worklist(worklist);
+               worklist = duplicate_activate_worklist(worklist, block, best_succ_block,
+                                                      best_pos);
 
+               /* handle this in fix_blocks later... */
+#if 0
                /* now we could have live values in the succ worklists that are not
-                * live anymore in the worklist we picked. We need reloads for them.
-                */
-               if(!tentative_mode) {
-                       foreach_block_succ(block, edge) {
-                               ir_node      *succ_block    = get_edge_src_irn(edge);
-                               worklist_t   *succ_worklist = get_irn_link(succ_block);
-
-                               spill_non_live_nodes(succ_worklist);
-                       }
+                * live anymore in the worklist we picked. We need reloads for them. */
+               foreach_block_succ(block, edge) {
+                       ir_node      *succ_block    = get_edge_src_irn(edge);
+                       block_info_t *block_info    = get_block_info(succ_block);
+                       worklist_t   *succ_worklist = block_info->start_worklist;
+
+                       if (succ_block == best_succ_block)
+                               continue;
+
+                       process_non_active_worklist(succ_worklist, worklist);
                }
+#endif
+       }
+
+       block_info = get_block_info(block);
+
+#ifdef DEBUG_libfirm
+       DB((dbg, LEVEL_1, "worklist at end of %+F:", block));
+       print_worklist(worklist, LEVEL_1);
+       DB((dbg, LEVEL_1, "\n"));
+
+       if (should_have_reached_fixpoint) {
+               assert(worklists_equal(block_info->end_worklist, worklist));
        }
+#endif
+       block_info->end_worklist = duplicate_worklist(worklist);
 
        do_spilling(block, worklist);
        deactivate_worklist(worklist);
 
-       set_irn_link(block, worklist);
+#ifdef DEBUG_libfirm
+       if (should_have_reached_fixpoint) {
+               assert(worklists_equal(block_info->start_worklist, worklist));
+       }
+#endif
+       block_info->start_worklist = worklist;
 
        /* we shouldn't have any live values left at the start block */
        n_preds = get_Block_n_cfgpreds(block);
        assert(n_preds != 0 || worklist->n_live_values == 0);
 }
 
+typedef struct block_or_loop_t block_or_loop_t;
+struct block_or_loop_t {
+       union {
+               ir_node *block;
+               ir_loop *loop;
+       } v;
+       bool is_loop;
+};
+
+static block_or_loop_t *loop_blocks;
+static ir_loop         *current_loop;
+
+static void find_blocks(ir_node *block);
+
+static void find_in_loop(ir_loop *loop, ir_node *entry)
+{
+       loop_info_t     *loop_info = get_loop_info(loop);
+       block_or_loop_t block_or_loop;
+       loop_edge_t     *edge;
+
+       /* simply mark 1 block in the loop to indicate that the loop was already
+        * processed */
+       ir_node *some_block = loop_info->entry_edges->block;
+       if (Block_block_visited(some_block))
+               return;
+
+       block_or_loop.v.loop  = loop;
+       block_or_loop.is_loop = true;
+       ARR_APP1(block_or_loop_t, loop_blocks, block_or_loop);
+
+#ifndef NDEBUG
+       {
+               /* we should be 1 of the entry blocks */
+               loop_edge_t *edge  = loop_info->entry_edges;
+               bool         found = false;
+               for ( ; edge != NULL; edge = edge->next) {
+                       if (edge->block == entry)
+                               found = true;
+               }
+               assert(found);
+       }
+#endif
+       /* check all loop successors */
+       for (edge = loop_info->exit_edges; edge != NULL; edge = edge->next) {
+               ir_node *succ      = edge->block;
+               ir_loop *succ_loop = get_irn_loop(succ);
+
+               if (succ_loop == current_loop) {
+                       find_blocks(succ);
+               } else {
+                       assert(get_loop_depth(succ_loop) < get_loop_depth(current_loop));
+               }
+       }
+}
+
+static void find_blocks(ir_node *block)
+{
+       const ir_edge_t *edge;
+       block_or_loop_t block_or_loop;
+
+       if (Block_block_visited(block))
+               return;
+
+       block_or_loop.v.block = block;
+       block_or_loop.is_loop = false;
+
+       ARR_APP1(block_or_loop_t, loop_blocks, block_or_loop);
+       mark_Block_block_visited(block);
+
+       foreach_block_succ(block, edge) {
+               ir_node *succ = get_edge_src_irn(edge);
+
+               /* is it part of the current loop? */
+               ir_loop *loop = get_irn_loop(succ);
+               if (loop != current_loop) {
+                       /* a sub-loop? */
+                       if (get_loop_depth(loop) > get_loop_depth(current_loop)) {
+                               find_in_loop(loop, succ);
+                       } else {
+                               /* parent loop: we're not interested in the block */
+                       }
+               } else {
+                       find_blocks(succ);
+               }
+       }
+}
+
+static void process_block_or_loop(const block_or_loop_t *block_or_loop)
+{
+       if (block_or_loop->is_loop) {
+               loop_info_t *loop_info = get_loop_info(block_or_loop->v.loop);
+
+               if (loop_info->max_register_pressure > max_register_pressure)
+                       max_register_pressure = loop_info->max_register_pressure;
+
+               /* TODO: push unused livethroughs... */
+               return;
+       }
+       process_block(block_or_loop->v.block, NULL);
+}
+
+static void process_loop(ir_loop *loop)
+{
+       int         n_elements = get_loop_n_elements(loop);
+       int         i, len;
+       loop_info_t *loop_info;
+       loop_edge_t *edge;
+       ir_node     *some_block;
+
+       /* first handle all sub-loops */
+       for (i = 0; i < n_elements; ++i) {
+               loop_element element = get_loop_element(loop, i);
+               if (*element.kind != k_ir_loop)
+                       continue;
+
+               process_loop(element.son);
+       }
+
+       /* create a postorder of the blocks */
+       loop_info = get_loop_info(loop);
+       edge      = loop_info->entry_edges;
+       if (edge != NULL) {
+               some_block = edge->block;
+       } else {
+               assert(loop == get_irg_loop(current_ir_graph));
+               some_block = get_irg_start_block(current_ir_graph);
+       }
+
+       loop_blocks  = NEW_ARR_F(block_or_loop_t,0);
+       current_loop = loop;
+
+       ir_reserve_resources(current_ir_graph, IR_RESOURCE_BLOCK_VISITED);
+       inc_irg_block_visited(current_ir_graph);
+       find_blocks(some_block);
+       /* for endless loops the end-block might be unreachable */
+       if (loop == get_irg_loop(current_ir_graph)) {
+               find_blocks(get_irg_end_block(current_ir_graph));
+       }
+       ir_free_resources(current_ir_graph, IR_RESOURCE_BLOCK_VISITED);
+
+       fprintf(stderr, "Block List for loop %p\n", loop);
+       len = ARR_LEN(loop_blocks);
+       for (i = 0; i < len; ++i) {
+               block_or_loop_t *block_or_loop = &loop_blocks[i];
+               if (block_or_loop->is_loop) {
+                       ir_fprintf(stderr, " L-%p", block_or_loop->v.loop);
+               } else {
+                       ir_fprintf(stderr, " B-%+F", block_or_loop->v.block);
+               }
+       }
+       fprintf(stderr, "\n");
+
+       max_register_pressure = 0;
+
+       /* run1: tentative phase */
+       tentative_mode = true;
+       for (i = len-1; i >= 0; --i) {
+               process_block_or_loop(&loop_blocks[i]);
+       }
+
+       /* run2: tentative phase - should reach fixpoint */
+       tentative_mode = true;
+       for (i = len-1; i >= 0; --i) {
+               process_block_or_loop(&loop_blocks[i]);
+       }
+
+#ifndef NDEBUG
+       /* run3: tentative phase - check fixpoint */
+       tentative_mode               = true;
+       should_have_reached_fixpoint = true;
+       for (i = len-1; i >= 0; --i) {
+               process_block_or_loop(&loop_blocks[i]);
+       }
+       should_have_reached_fixpoint = false;
+#endif
+
+       /* run4: add spills/reloads */
+       tentative_mode = false;
+       for (i = len-1; i >= 0; --i) {
+               process_block_or_loop(&loop_blocks[i]);
+       }
+
+       loop_info->max_register_pressure = max_register_pressure;
+       ir_fprintf(stderr, "Regpressure in loop %p: %u\n", loop,
+                  (unsigned) max_register_pressure);
+
+       DEL_ARR_F(loop_blocks);
+}
+
+static void fix_block_borders(ir_node *block, void *data)
+{
+       block_info_t *block_info     = get_block_info(block);
+       worklist_t   *start_worklist = block_info->start_worklist;
+       int           n_cfgpreds     = get_Block_n_cfgpreds(block);
+       int           i;
+
+       (void) data;
+       assert(start_worklist != NULL);
+
+       for (i = 0; i < n_cfgpreds; ++i) {
+               ir_node      *pred_block      = get_Block_cfgpred_block(block, i);
+               block_info_t *pred_block_info = get_block_info(pred_block);
+               worklist_t   *end_worklist    = pred_block_info->end_worklist;
+               struct list_head *entry;
+
+               assert(end_worklist != NULL);
+
+               /* reload missing values */
+               activate_worklist(end_worklist);
+
+               list_for_each(entry, &start_worklist->live_values) {
+                       worklist_entry_t *wl_entry
+                               = list_entry(entry, worklist_entry_t, head);
+                       ir_node          *value = wl_entry->value;
+
+                       if (is_Phi(value) && get_nodes_block(value) == block) {
+                               value = get_irn_n(value, i);
+
+                               /* we might have unknowns as argument for the phi */
+                               if (!arch_irn_consider_in_reg_alloc(arch_env, cls, value))
+                                       continue;
+                       }
+
+                       if (worklist_contains(value))
+                               continue;
+
+                       be_add_reload_on_edge(senv, value, block, i, cls, 1);
+               }
+
+               deactivate_worklist(end_worklist);
+       }
+}
+
 static void be_spill_belady3(be_irg_t *birg, const arch_register_class_t *ncls)
 {
        ir_graph *irg = be_get_birg_irg(birg);
 
-       cls       = ncls;
-       n_regs    = cls->n_regs - be_put_ignore_regs(birg, cls, NULL);
+       cls    = ncls;
+       n_regs = cls->n_regs - be_put_ignore_regs(birg, cls, NULL);
 
-       if(n_regs == 0)
+       /* shortcut for register classes with ignore regs only */
+       if (n_regs == 0)
                return;
 
-       arch_env       = be_get_birg_arch_env(birg);
-       exec_freq      = be_get_birg_exec_freq(birg);
-       tentative_mode = 0;
+       arch_env  = be_get_birg_arch_env(birg);
+       exec_freq = be_get_birg_exec_freq(birg);
 
        be_clear_links(irg);
-       set_using_irn_link(irg);
-       set_using_irn_visited(irg);
+       ir_reserve_resources(irg, IR_RESOURCE_IRN_VISITED | IR_RESOURCE_IRN_LINK
+                       | IR_RESOURCE_LOOP_LINK);
        inc_irg_visited(irg);
 
        obstack_init(&obst);
        senv = be_new_spill_env(birg);
 
+       assure_cf_loop(irg);
+       clear_loop_info(get_irg_loop(irg));
+       irg_block_walk_graph(irg, construct_loop_edges, NULL, NULL);
+
+       process_loop(get_irg_loop(current_ir_graph));
+
+#if 0
        /* do a post-order walk over the CFG to make sure we have a maximum number
         * of preds processed before entering a block */
+       tentative_mode = 1;
        irg_block_edges_walk(get_irg_start_block(irg), NULL, process_block, NULL);
 
-       clear_using_irn_link(irg);
-       clear_using_irn_visited(irg);
+       tentative_mode = 1;
+       irg_block_edges_walk(get_irg_start_block(irg), NULL, process_block, NULL);
+
+       tentative_mode = 0;
+       irg_block_edges_walk(get_irg_start_block(irg), NULL, process_block, NULL);
+#endif
+
+       irg_block_walk_graph(irg, fix_block_borders, NULL, NULL);
+
+       ir_free_resources(irg, IR_RESOURCE_IRN_VISITED | IR_RESOURCE_IRN_LINK
+                       | IR_RESOURCE_LOOP_LINK);
 
        be_insert_spills_reloads(senv);