- BugFix: kill partitions with 0 blocks either
[libfirm] / ir / opt / opt_inline.c
index b6e368f..bf36024 100644 (file)
@@ -23,9 +23,7 @@
  * @author   Michael Beck, Goetz Lindenmaier
  * @version  $Id$
  */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "config.h"
 
 #include <limits.h>
 #include <assert.h>
 #include "irgmod.h"
 #include "irgwalk.h"
 
-#include "adt/array.h"
-#include "adt/pset.h"
-#include "adt/pmap.h"
-#include "adt/pdeq.h"
-#include "adt/xmalloc.h"
+#include "array_t.h"
+#include "list.h"
+#include "pset.h"
+#include "pmap.h"
+#include "pdeq.h"
+#include "xmalloc.h"
+#include "pqueue.h"
 
 #include "irouts.h"
 #include "irloop_t.h"
@@ -92,7 +92,7 @@ DEBUG_ONLY(static firm_dbg_module_t *dbg;)
  * accesses.  This function is called for all Phi and Block nodes
  * in a Block.
  */
-static INLINE int
+static inline int
 compute_new_arity(ir_node *b) {
        int i, res, irn_arity;
        int irg_v, block_v;
@@ -470,7 +470,7 @@ void dead_node_elimination(ir_graph *irg) {
        graveyard_obst = irg->obst;
 
        /* A new obstack, where the reachable nodes will be copied to. */
-       rebirth_obst = xmalloc(sizeof(*rebirth_obst));
+       rebirth_obst = XMALLOC(struct obstack);
        irg->obst = rebirth_obst;
        obstack_init(irg->obst);
        irg->last_node_idx = 0;
@@ -661,7 +661,7 @@ static void dead_node_subst_hook(void *context, ir_graph *irg, ir_node *old, ir_
  * Make a new Survive DCE environment.
  */
 survive_dce_t *new_survive_dce(void) {
-       survive_dce_t *res = xmalloc(sizeof(res[0]));
+       survive_dce_t *res = XMALLOC(survive_dce_t);
        obstack_init(&res->obst);
        res->places     = pmap_create();
        res->new_places = NULL;
@@ -674,10 +674,6 @@ survive_dce_t *new_survive_dce(void) {
        res->dead_node_elim_subst.context = res;
        res->dead_node_elim_subst.next    = NULL;
 
-#ifndef FIRM_ENABLE_HOOKS
-       assert(0 && "need hooks enabled");
-#endif
-
        register_hook(hook_dead_node_elim, &res->dead_node_elim);
        register_hook(hook_dead_node_elim_subst, &res->dead_node_elim_subst);
        return res;
@@ -782,7 +778,7 @@ static void find_addr(ir_node *node, void *env) {
                 * using alloca is called in loop.  In GCC present in SPEC2000 inlining
                 * into schedule_block cause it to require 2GB of ram instead of 256MB.
                 *
-                * Sorryly this is true with our implementation also.
+                * Sorrily this is true with our implementation also.
                 * Moreover, we cannot differentiate between alloca() and VLA yet, so this
                 * disables inlining of functions using VLA (with are completely save).
                 *
@@ -1021,6 +1017,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
 
        /* -- Replicate local entities of the called_graph -- */
        /* copy the entities. */
+       irp_reserve_resources(irp, IR_RESOURCE_ENTITY_LINK);
        called_frame = get_irg_frame_type(called_graph);
        curr_frame   = get_irg_frame_type(irg);
        for (i = 0, n = get_class_n_members(called_frame); i < n; ++i) {
@@ -1041,6 +1038,8 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds_inline,
                 get_irg_frame_type(called_graph));
 
+       irp_free_resources(irp, IR_RESOURCE_ENTITY_LINK);
+
        /* Repair called_graph */
        set_irg_visited(called_graph, get_irg_visited(irg));
        set_irg_block_visited(called_graph, get_irg_block_visited(irg));
@@ -1066,8 +1065,8 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
        arity = get_Block_n_cfgpreds(end_bl);    /* arity = n_exc + n_ret  */
        n_res = get_method_n_ress(get_Call_type(call));
 
-       res_pred = xmalloc(n_res * sizeof(*res_pred));
-       cf_pred  = xmalloc(arity * sizeof(*res_pred));
+       res_pred = XMALLOCN(ir_node*, n_res);
+       cf_pred  = XMALLOCN(ir_node*, arity);
 
        set_irg_current_block(irg, post_bl); /* just to make sure */
 
@@ -1207,9 +1206,9 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
                                n_exc++;
                        }
                }
-               main_end_bl = get_irg_end_block(irg);
+               main_end_bl       = get_irg_end_block(irg);
                main_end_bl_arity = get_irn_arity(main_end_bl);
-               end_preds =  xmalloc((n_exc + main_end_bl_arity) * sizeof(*end_preds));
+               end_preds         = XMALLOCN(ir_node*, n_exc + main_end_bl_arity);
 
                for (i = 0; i < main_end_bl_arity; ++i)
                        end_preds[i] = get_irn_n(main_end_bl, i);
@@ -1231,27 +1230,28 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
 }
 
 /********************************************************************/
-/* Apply inlineing to small methods.                                */
+/* Apply inlining to small methods.                                 */
 /********************************************************************/
 
 static struct obstack  temp_obst;
 
 /** Represents a possible inlinable call in a graph. */
-typedef struct _call_entry call_entry;
-struct _call_entry {
-       ir_node    *call;      /**< the Call node */
-       ir_graph   *callee;    /**< the callee IR-graph called here */
-       call_entry *next;      /**< for linking the next one */
-       int        loop_depth; /**< the loop depth of this call */
-};
+typedef struct _call_entry {
+       ir_node    *call;       /**< The Call node. */
+       ir_graph   *callee;     /**< The callee IR-graph. */
+       list_head  list;        /**< List head for linking the next one. */
+       int        loop_depth;  /**< The loop depth of this call. */
+       int        benefice;    /**< The calculated benefice of this call. */
+       unsigned   local_adr:1; /**< Set if this call gets an address of a local variable. */
+       unsigned   all_const:1; /**< Set if this call has only constant parameters. */
+} call_entry;
 
 /**
  * environment for inlining small irgs
  */
 typedef struct _inline_env_t {
-       struct obstack obst;  /**< an obstack where call_entries are allocated on. */
-       call_entry *head;     /**< the head of the call entry list */
-       call_entry *tail;     /**< the tail of the call entry list */
+       struct obstack obst;  /**< An obstack where call_entries are allocated on. */
+       list_head      calls; /**< The call entry list. */
 } inline_env_t;
 
 /**
@@ -1276,6 +1276,7 @@ static ir_graph *get_call_called_irg(ir_node *call) {
  * Walker: Collect all calls to known graphs inside a graph.
  */
 static void collect_calls(ir_node *call, void *env) {
+       (void) env;
        if (is_Call(call)) {
                ir_graph *called_irg = get_call_called_irg(call);
 
@@ -1285,14 +1286,12 @@ static void collect_calls(ir_node *call, void *env) {
                        call_entry   *entry = obstack_alloc(&ienv->obst, sizeof(*entry));
                        entry->call       = call;
                        entry->callee     = called_irg;
-                       entry->next       = NULL;
                        entry->loop_depth = 0;
+                       entry->benefice   = 0;
+                       entry->local_adr  = 0;
+                       entry->all_const  = 0;
 
-                       if (ienv->tail == NULL)
-                               ienv->head = entry;
-                       else
-                               ienv->tail->next = entry;
-                       ienv->tail = entry;
+                       list_add_tail(&entry->list, &ienv->calls);
                }
        }
 }
@@ -1316,19 +1315,19 @@ void inline_small_irgs(ir_graph *irg, int size) {
        free_callee_info(irg);
 
        /* Find Call nodes to inline.
-          (We can not inline during a walk of the graph, as inlineing the same
+          (We can not inline during a walk of the graph, as inlining the same
           method several times changes the visited flag of the walked graph:
-          after the first inlineing visited of the callee equals visited of
-          the caller.  With the next inlineing both are increased.) */
+          after the first inlining visited of the callee equals visited of
+          the caller.  With the next inlining both are increased.) */
        obstack_init(&env.obst);
-       env.head = env.tail = NULL;
+       INIT_LIST_HEAD(&env.calls);
        irg_walk_graph(irg, NULL, collect_calls, &env);
 
-       if (env.head != NULL) {
+       if (! list_empty(&env.calls)) {
                /* There are calls to inline */
                collect_phiprojs(irg);
 
-               for (entry = env.head; entry != NULL; entry = entry->next) {
+               list_for_each_entry(call_entry, entry, &env.calls, list) {
                        ir_graph            *callee = entry->callee;
                        irg_inline_property prop    = get_irg_inline_property(callee);
 
@@ -1351,19 +1350,18 @@ void inline_small_irgs(ir_graph *irg, int size) {
  * Environment for inlining irgs.
  */
 typedef struct {
-       int n_nodes;             /**< Number of nodes in graph except Id, Tuple, Proj, Start, End. */
-       int n_blocks;            /**< Number of Blocks in graph without Start and End block. */
-       int n_nodes_orig;        /**< for statistics */
-       int n_call_nodes;        /**< Number of Call nodes in the graph. */
-       int n_call_nodes_orig;   /**< for statistics */
-       int n_callers;           /**< Number of known graphs that call this graphs. */
-       int n_callers_orig;      /**< for statistics */
-       unsigned got_inline:1;   /**< Set, if at least one call inside this graph was inlined. */
-       unsigned local_vars:1;   /**< Set, if a inlined function gets the address of an inlined variable. */
-       unsigned recursive:1;    /**< Set, if this function is self recursive. */
-       call_entry *call_head;   /**< The head of the list of all call nodes in this graph. */
-       call_entry *call_tail;   /**< The tail of the list of all call nodes in this graph .*/
-       unsigned *local_weights; /**< Once allocated, the beneficial weight for transmitting local addresses. */
+       list_head calls;             /**< List of of all call nodes in this graph. */
+       unsigned  *local_weights;    /**< Once allocated, the beneficial weight for transmitting local addresses. */
+       unsigned  n_nodes;           /**< Number of nodes in graph except Id, Tuple, Proj, Start, End. */
+       unsigned  n_blocks;          /**< Number of Blocks in graph without Start and End block. */
+       unsigned  n_nodes_orig;      /**< for statistics */
+       unsigned  n_call_nodes;      /**< Number of Call nodes in the graph. */
+       unsigned  n_call_nodes_orig; /**< for statistics */
+       unsigned  n_callers;         /**< Number of known graphs that call this graphs. */
+       unsigned  n_callers_orig;    /**< for statistics */
+       unsigned  got_inline:1;      /**< Set, if at least one call inside this graph was inlined. */
+       unsigned  local_vars:1;      /**< Set, if an inlined function got the address of a local variable. */
+       unsigned  recursive:1;       /**< Set, if this function is self recursive. */
 } inline_irg_env;
 
 /**
@@ -1371,11 +1369,11 @@ typedef struct {
  */
 static inline_irg_env *alloc_inline_irg_env(void) {
        inline_irg_env *env    = obstack_alloc(&temp_obst, sizeof(*env));
+       INIT_LIST_HEAD(&env->calls);
+       env->local_weights     = NULL;
        env->n_nodes           = -2; /* do not count count Start, End */
        env->n_blocks          = -2; /* do not count count Start, End Block */
        env->n_nodes_orig      = -2; /* do not count Start, End */
-       env->call_head         = NULL;
-       env->call_tail         = NULL;
        env->n_call_nodes      = 0;
        env->n_call_nodes_orig = 0;
        env->n_callers         = 0;
@@ -1383,13 +1381,11 @@ static inline_irg_env *alloc_inline_irg_env(void) {
        env->got_inline        = 0;
        env->local_vars        = 0;
        env->recursive         = 0;
-       env->local_weights     = NULL;
        return env;
 }
 
 typedef struct walker_env {
        inline_irg_env *x;     /**< the inline environment */
-       call_entry *last_call; /**< points to the last inserted call */
        char ignore_runtime;   /**< the ignore runtime flag */
        char ignore_callers;   /**< if set, do change callers data */
 } wenv_t;
@@ -1448,37 +1444,12 @@ static void collect_calls2(ir_node *call, void *ctx) {
                entry = obstack_alloc(&temp_obst, sizeof(*entry));
                entry->call       = call;
                entry->callee     = callee;
-               entry->next       = NULL;
                entry->loop_depth = get_irn_loop(get_nodes_block(call))->depth;
+               entry->benefice   = 0;
+               entry->local_adr  = 0;
+               entry->all_const  = 0;
 
-               /* note: we use call_tail here as a pointer to the last inserted */
-               if (x->call_head == NULL) {
-                       x->call_head = entry;
-               } else {
-                       if (entry->loop_depth == env->last_call->loop_depth) {
-                               /* same depth as the last one, enqueue after it */
-                               entry->next          = env->last_call->next;
-                               env->last_call->next = entry;
-                       } else if (entry->loop_depth > x->call_head->loop_depth) {
-                               /* put first */
-                               entry->next  = x->call_head;
-                               x->call_head = entry;
-                       } else {
-                               /* search the insertion point */
-                               call_entry *p;
-
-                               for (p = x->call_head; p->next != NULL; p = p->next)
-                                       if (entry->loop_depth > p->next->loop_depth)
-                                               break;
-                               entry->next = p->next;
-                               p->next     = entry;
-                       }
-               }
-               env->last_call = entry;
-               if (entry->next == NULL) {
-                       /* keep tail up to date */
-                       x->call_tail = entry;
-               }
+               list_add_tail(&entry->list, &x->calls);
        }
 }
 
@@ -1486,7 +1457,7 @@ static void collect_calls2(ir_node *call, void *ctx) {
  * Returns TRUE if the number of callers is 0 in the irg's environment,
  * hence this irg is a leave.
  */
-INLINE static int is_leave(ir_graph *irg) {
+inline static int is_leave(ir_graph *irg) {
        inline_irg_env *env = get_irg_link(irg);
        return env->n_call_nodes == 0;
 }
@@ -1495,60 +1466,52 @@ INLINE static int is_leave(ir_graph *irg) {
  * Returns TRUE if the number of nodes in the callee is
  * smaller then size in the irg's environment.
  */
-INLINE static int is_smaller(ir_graph *callee, int size) {
+inline static int is_smaller(ir_graph *callee, unsigned size) {
        inline_irg_env *env = get_irg_link(callee);
        return env->n_nodes < size;
 }
 
 /**
- * Append the nodes of the list src to the nodes of the list in environment dst.
+ * Duplicate a call entry.
+ *
+ * @param entry     the original entry to duplicate
+ * @param new_call  the new call node
+ * @param loop_depth_delta
+ *                  delta value for the loop depth
  */
-static void append_call_list(inline_irg_env *dst, call_entry *src) {
-       call_entry *entry, *nentry;
-
-       /* Note that the src list points to Call nodes in the inlined graph, but
-          we need Call nodes in our graph. Luckily the inliner leaves this information
-          in the link field. */
-       for (entry = src; entry != NULL; entry = entry->next) {
-               nentry = obstack_alloc(&temp_obst, sizeof(*nentry));
-               nentry->call         = get_irn_link(entry->call);
-               nentry->callee       = entry->callee;
-               nentry->next         = NULL;
-               nentry->loop_depth   = entry->loop_depth;
-               dst->call_tail->next = nentry;
-               dst->call_tail       = nentry;
-       }
+static call_entry *duplicate_call_entry(const call_entry *entry,
+                                        ir_node *new_call, int loop_depth_delta) {
+       call_entry *nentry = obstack_alloc(&temp_obst, sizeof(*nentry));
+       nentry->call       = new_call;
+       nentry->callee     = entry->callee;
+       nentry->benefice   = entry->benefice;
+       nentry->loop_depth = entry->loop_depth + loop_depth_delta;
+       nentry->local_adr  = entry->local_adr;
+       nentry->all_const  = entry->all_const;
+
+       return nentry;
 }
 
 /**
- * Add the nodes of the list src in front to the nodes of the list dst.
+ * Append all call nodes of the source environment to the nodes of in the destination
+ * environment.
+ *
+ * @param dst         destination environment
+ * @param src         source environment
+ * @param loop_depth  the loop depth of the call that is replaced by the src list
  */
-static call_entry *replace_entry_by_call_list(call_entry *dst, call_entry *src) {
-       call_entry *entry, *nentry, *head, *tail;
+static void append_call_list(inline_irg_env *dst, inline_irg_env *src, int loop_depth) {
+       call_entry *entry, *nentry;
 
        /* Note that the src list points to Call nodes in the inlined graph, but
           we need Call nodes in our graph. Luckily the inliner leaves this information
           in the link field. */
-       head = tail = NULL;
-       for (entry = src; entry != NULL; entry = entry->next) {
-               nentry = obstack_alloc(&temp_obst, sizeof(*nentry));
-               nentry->call         = get_irn_link(entry->call);
-               nentry->callee       = entry->callee;
-               nentry->next         = NULL;
-               nentry->loop_depth   = entry->loop_depth + dst->loop_depth;
-               if (head == NULL)
-                       head = nentry;
-               else
-                       tail->next = nentry;
-               tail = nentry;
+       list_for_each_entry(call_entry, entry, &src->calls, list) {
+               nentry = duplicate_call_entry(entry, get_irn_link(entry->call), loop_depth);
+               list_add_tail(&nentry->list, &dst->calls);
        }
-       /* skip the head of dst */
-       if (head != NULL) {
-               tail->next = dst->next;
-       } else {
-               head = dst->next;
-       }
-       return head;
+       dst->n_call_nodes += src->n_call_nodes;
+       dst->n_nodes      += src->n_nodes;
 }
 
 /*
@@ -1559,14 +1522,16 @@ static call_entry *replace_entry_by_call_list(call_entry *dst, call_entry *src)
  * Methods where the obstack containing the firm graph is smaller than
  * size are inlined.
  */
-void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_runtime) {
+void inline_leave_functions(unsigned maxsize, unsigned leavesize,
+                            unsigned size, int ignore_runtime)
+{
        inline_irg_env   *env;
        ir_graph         *irg;
        int              i, n_irgs;
        ir_graph         *rem;
        int              did_inline;
        wenv_t           wenv;
-       call_entry       *entry, *tail;
+       call_entry       *entry, *next;
        const call_entry *centry;
        pmap             *copied_graphs;
        pmap_entry       *pm_entry;
@@ -1582,7 +1547,7 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
        for (i = 0; i < n_irgs; ++i)
                set_irg_link(get_irp_irg(i), alloc_inline_irg_env());
 
-       /* Precompute information in temporary data structure. */
+       /* Pre-compute information in temporary data structure. */
        wenv.ignore_runtime = ignore_runtime;
        wenv.ignore_callers = 0;
        for (i = 0; i < n_irgs; ++i) {
@@ -1607,12 +1572,11 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                        int phiproj_computed = 0;
 
                        current_ir_graph = get_irp_irg(i);
-                       env = (inline_irg_env *)get_irg_link(current_ir_graph);
+                       env              = get_irg_link(current_ir_graph);
 
-                       tail = NULL;
-                       for (entry = env->call_head; entry != NULL; entry = entry->next) {
+                       list_for_each_entry_safe(call_entry, entry, next, &env->calls, list) {
                                ir_graph            *callee;
-                               irg_inline_property prop;
+                               irg_inline_property  prop;
 
                                if (env->n_nodes > maxsize)
                                        break;
@@ -1635,9 +1599,9 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                                        did_inline = inline_method(call, callee);
 
                                        if (did_inline) {
-                                               inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
+                                               inline_irg_env *callee_env = get_irg_link(callee);
 
-                                               /* was inlined, must be recomputed */
+                                               /* call was inlined, Phi/Projs for current graph must be recomputed */
                                                phiproj_computed = 0;
 
                                                /* Do some statistics */
@@ -1647,16 +1611,11 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                                                --callee_env->n_callers;
 
                                                /* remove this call from the list */
-                                               if (tail != NULL)
-                                                       tail->next = entry->next;
-                                               else
-                                                       env->call_head = entry->next;
+                                               list_del(&entry->list);
                                                continue;
                                        }
                                }
-                               tail = entry;
                        }
-                       env->call_tail = tail;
                }
        } while (did_inline);
 
@@ -1666,11 +1625,10 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                int phiproj_computed = 0;
 
                current_ir_graph = get_irp_irg(i);
-               env = (inline_irg_env *)get_irg_link(current_ir_graph);
+               env              = get_irg_link(current_ir_graph);
 
                /* note that the list of possible calls is updated during the process */
-               tail = NULL;
-               for (entry = env->call_head; entry != NULL; entry = entry->next) {
+               list_for_each_entry_safe(call_entry, entry, next, &env->calls, list) {
                        irg_inline_property prop;
                        ir_graph            *callee;
                        pmap_entry          *e;
@@ -1746,40 +1704,33 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run
                                if (did_inline) {
                                        inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
 
-                                       /* was inlined, must be recomputed */
+                                       /* call was inlined, Phi/Projs for current graph must be recomputed */
                                        phiproj_computed = 0;
 
                                        /* callee was inline. Append it's call list. */
                                        env->got_inline = 1;
                                        --env->n_call_nodes;
-                                       append_call_list(env, callee_env->call_head);
-                                       env->n_call_nodes += callee_env->n_call_nodes;
-                                       env->n_nodes += callee_env->n_nodes;
+                                       append_call_list(env, callee_env, entry->loop_depth);
                                        --callee_env->n_callers;
 
                                        /* after we have inlined callee, all called methods inside callee
                                           are now called once more */
-                                       for (centry = callee_env->call_head; centry != NULL; centry = centry->next) {
+                                       list_for_each_entry(call_entry, centry, &callee_env->calls, list) {
                                                inline_irg_env *penv = get_irg_link(centry->callee);
                                                ++penv->n_callers;
                                        }
 
                                        /* remove this call from the list */
-                                       if (tail != NULL)
-                                               tail->next = entry->next;
-                                       else
-                                               env->call_head = entry->next;
+                                       list_del(&entry->list);
                                        continue;
                                }
                        }
-                       tail = entry;
                }
-               env->call_tail = tail;
        }
 
        for (i = 0; i < n_irgs; ++i) {
                irg = get_irp_irg(i);
-               env = (inline_irg_env *)get_irg_link(irg);
+               env = get_irg_link(irg);
 
                if (env->got_inline) {
                        optimize_graph_df(irg);
@@ -1925,23 +1876,31 @@ static unsigned get_method_local_adress_weight(ir_graph *callee, int pos) {
  *
  * @param call       the call node we have to inspect
  * @param callee     the called graph
- * @param local_adr  set after return if an address of a local variable is
- *                   transmitted as a parameter
  */
-static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local_adr) {
-       ir_entity *ent = get_irg_entity(callee);
+static int calc_inline_benefice(call_entry *entry, ir_graph *callee)
+{
+       ir_node   *call = entry->call;
+       ir_entity *ent  = get_irg_entity(callee);
        ir_node   *frame_ptr;
        ir_type   *mtp;
        int       weight = 0;
        int       i, n_params, all_const;
        unsigned  cc, v;
+       irg_inline_property prop;
+
+       inline_irg_env *callee_env;
 
-       inline_irg_env *curr_env, *callee_env;
+       prop = get_irg_inline_property(callee);
+       if (prop == irg_inline_forbidden) {
+               DB((dbg, LEVEL_2, "In %+F Call to %+F: inlining forbidden\n",
+                   call, callee));
+               return entry->benefice = INT_MIN;
+       }
 
-       if (get_entity_additional_properties(ent) &
-                       (mtp_property_noreturn|mtp_property_weak)) {
-               /* do NOT inline noreturn or weak calls */
-               return INT_MIN;
+       if (get_irg_additional_properties(callee) & (mtp_property_noreturn | mtp_property_weak)) {
+               DB((dbg, LEVEL_2, "In %+F Call to %+F: not inlining noreturn or weak\n",
+                   call, callee));
+               return entry->benefice = INT_MIN;
        }
 
        /* costs for every passed parameter */
@@ -1975,34 +1934,24 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local
                                weight += get_method_param_weight(ent, i);
                        else if (is_Sel(param) && get_Sel_ptr(param) == frame_ptr) {
                                /*
-                                * An address of a local variable is transmitted. After inlining,
-                                * scalar_replacement might be able to remove the local variable,
-                                * so honor this.
+                                * An address of a local variable is transmitted. After
+                                * inlining, scalar_replacement might be able to remove the
+                                * local variable, so honor this.
                                 */
                                v = get_method_local_adress_weight(callee, i);
                                weight += v;
                                if (v > 0)
-                                       *local_adr = 1;
+                                       entry->local_adr = 1;
                        }
                }
        }
+       entry->all_const = all_const;
 
        callee_env = get_irg_link(callee);
-       if (callee_env->n_callers == 1
-                       && callee != current_ir_graph
-                       && get_entity_visibility(ent) == visibility_local) {
-               weight += 5000;
-       }
-
-       /* reduce the benefice if the current function is already big */
-       curr_env = get_irg_link(current_ir_graph);
-
-       /* inlining is usually a good idea unless the resulting function is
-        * becoming too big */
-       if (curr_env->n_nodes + callee_env->n_nodes > 500) {
-               weight -= curr_env->n_nodes / 20;
-               /* do not inline big functions */
-               weight -= callee_env->n_nodes / 4;
+       if (callee_env->n_callers == 1 &&
+           callee != current_ir_graph &&
+               get_entity_visibility(ent) == visibility_local) {
+               weight += 700;
        }
 
        /* give a bonus for functions with one block */
@@ -2011,40 +1960,45 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local
 
        /* and one for small non-recursive functions: we want them to be inlined in mostly every case */
        if (callee_env->n_nodes < 30 && !callee_env->recursive)
-               weight += 5000;
+               weight += 2000;
 
        /* and finally for leaves: they do not increase the register pressure
           because of callee safe registers */
        if (callee_env->n_call_nodes == 0)
                weight += 400;
 
-       /*
-        * Reduce the weight for recursive function IFF not all arguments are const.
-        * inlining recursive functions is rarely good.
-        */
-       if (callee_env->recursive && !all_const)
-               weight -= 1000;
+       /** it's important to inline inner loops first */
+       if (entry->loop_depth > 30)
+               weight += 30 * 1024;
+       else
+               weight += entry->loop_depth * 1024;
 
        /*
         * All arguments constant is probably a good sign, give an extra bonus
         */
        if (all_const)
-               weight += 1308;
+               weight += 1024;
 
-       return weight;
+       return entry->benefice = weight;
 }
 
 static ir_graph **irgs;
 static int      last_irg;
 
-static void callgraph_walker(ir_graph *irg, void *data)
-{
+/**
+ * Callgraph walker, collect all visited graphs.
+ */
+static void callgraph_walker(ir_graph *irg, void *data) {
        (void) data;
        irgs[last_irg++] = irg;
 }
 
-static ir_graph **create_irg_list(void)
-{
+/**
+ * Creates an inline order for all graphs.
+ *
+ * @return the list of graphs.
+ */
+static ir_graph **create_irg_list(void) {
        ir_entity **free_methods;
        int       arr_len;
        int       n_irgs = get_irp_n_irgs();
@@ -2055,8 +2009,7 @@ static ir_graph **create_irg_list(void)
        compute_callgraph();
 
        last_irg = 0;
-       irgs     = xmalloc(n_irgs * sizeof(*irgs));
-       memset(irgs, 0, sizeof(n_irgs * sizeof(*irgs)));
+       irgs     = XMALLOCNZ(ir_graph*, n_irgs);
 
        callgraph_walk(NULL, callgraph_walker, NULL);
        assert(n_irgs == last_irg);
@@ -2065,17 +2018,215 @@ static ir_graph **create_irg_list(void)
 }
 
 /**
+ * Push a call onto the priority list if its benefice is big enough.
+ *
+ * @param pqueue   the priority queue of calls
+ * @param call     the call entry
+ * @param inlien_threshold
+ *                 the threshold value
+ */
+static void maybe_push_call(pqueue_t *pqueue, call_entry *call,
+                            int inline_threshold)
+{
+       ir_graph            *callee  = call->callee;
+       irg_inline_property prop     = get_irg_inline_property(callee);
+       int                 benefice = calc_inline_benefice(call, callee);
+
+       DB((dbg, LEVEL_2, "In %+F Call %+F to %+F has benefice %d\n",
+           get_irn_irg(call->call), call->call, callee, benefice));
+
+       if (prop < irg_inline_forced && benefice < inline_threshold) {
+               return;
+       }
+
+       pqueue_put(pqueue, call, benefice);
+}
+
+/**
+ * Try to inline calls into a graph.
+ *
+ * @param irg      the graph into which we inline
+ * @param maxsize  do NOT inline if the size of irg gets
+ *                 bigger than this amount
+ * @param inline_threshold
+ *                 threshold value for inline decision
+ * @param copied_graphs
+ *                 map containing copied of recursive graphs
+ */
+static void inline_into(ir_graph *irg, unsigned maxsize,
+                        int inline_threshold, pmap *copied_graphs)
+{
+       int            phiproj_computed = 0;
+       inline_irg_env *env = get_irg_link(irg);
+       call_entry     *curr_call;
+       wenv_t         wenv;
+       pqueue_t       *pqueue;
+
+       if (env->n_call_nodes == 0)
+               return;
+
+       if (env->n_nodes > maxsize) {
+               DB((dbg, LEVEL_2, "%+F: too big (%d)\n", irg, env->n_nodes));
+               return;
+       }
+
+       current_ir_graph = irg;
+
+       /* put irgs into the pqueue */
+       pqueue = new_pqueue();
+
+       list_for_each_entry(call_entry, curr_call, &env->calls, list) {
+               assert(is_Call(curr_call->call));
+               maybe_push_call(pqueue, curr_call, inline_threshold);
+       }
+
+       /* note that the list of possible calls is updated during the process */
+       while (!pqueue_empty(pqueue)) {
+               int                 did_inline;
+               call_entry          *curr_call  = pqueue_pop_front(pqueue);
+               ir_graph            *callee     = curr_call->callee;
+               ir_node             *call_node  = curr_call->call;
+               inline_irg_env      *callee_env = get_irg_link(callee);
+               irg_inline_property prop        = get_irg_inline_property(callee);
+               int                 loop_depth;
+               const call_entry    *centry;
+               pmap_entry          *e;
+
+               if ((prop < irg_inline_forced) && env->n_nodes + callee_env->n_nodes > maxsize) {
+                       DB((dbg, LEVEL_2, "%+F: too big (%d) + %+F (%d)\n", irg,
+                                               env->n_nodes, callee, callee_env->n_nodes));
+                       continue;
+               }
+
+               e = pmap_find(copied_graphs, callee);
+               if (e != NULL) {
+                       int benefice = curr_call->benefice;
+                       /*
+                        * Reduce the weight for recursive function IFF not all arguments are const.
+                        * inlining recursive functions is rarely good.
+                        */
+                       if (!curr_call->all_const)
+                               benefice -= 2000;
+                       if (benefice < inline_threshold)
+                               continue;
+
+                       /*
+                        * Remap callee if we have a copy.
+                        */
+                       callee     = e->value;
+                       callee_env = get_irg_link(callee);
+               }
+
+               if (current_ir_graph == callee) {
+                       /*
+                        * Recursive call: we cannot directly inline because we cannot
+                        * walk the graph and change it. So we have to make a copy of
+                        * the graph first.
+                        */
+                       int benefice = curr_call->benefice;
+                       ir_graph *copy;
+
+                       /*
+                        * Reduce the weight for recursive function IFF not all arguments are const.
+                        * inlining recursive functions is rarely good.
+                        */
+                       if (!curr_call->all_const)
+                               benefice -= 2000;
+                       if (benefice < inline_threshold)
+                               continue;
+
+                       /*
+                        * No copy yet, create one.
+                        * Note that recursive methods are never leaves, so it is
+                        * sufficient to test this condition here.
+                        */
+                       copy = create_irg_copy(callee);
+
+                       /* create_irg_copy() destroys the Proj links, recompute them */
+                       phiproj_computed = 0;
+
+                       /* allocate a new environment */
+                       callee_env = alloc_inline_irg_env();
+                       set_irg_link(copy, callee_env);
+
+                       assure_cf_loop(copy);
+                       wenv.x              = callee_env;
+                       wenv.ignore_callers = 1;
+                       irg_walk_graph(copy, NULL, collect_calls2, &wenv);
+
+                       /*
+                        * Enter the entity of the original graph. This is needed
+                        * for inline_method(). However, note that ent->irg still points
+                        * to callee, NOT to copy.
+                        */
+                       set_irg_entity(copy, get_irg_entity(callee));
+
+                       pmap_insert(copied_graphs, callee, copy);
+                       callee = copy;
+
+                       /* we have only one caller: the original graph */
+                       callee_env->n_callers      = 1;
+                       callee_env->n_callers_orig = 1;
+               }
+               if (! phiproj_computed) {
+                       phiproj_computed = 1;
+                       collect_phiprojs(current_ir_graph);
+               }
+               did_inline = inline_method(call_node, callee);
+               if (!did_inline)
+                       continue;
+
+               /* call was inlined, Phi/Projs for current graph must be recomputed */
+               phiproj_computed = 0;
+
+               /* remove it from the caller list */
+               list_del(&curr_call->list);
+
+               /* callee was inline. Append it's call list. */
+               env->got_inline = 1;
+               if (curr_call->local_adr)
+                       env->local_vars = 1;
+               --env->n_call_nodes;
+
+               /* we just generate a bunch of new calls */
+               loop_depth = curr_call->loop_depth;
+               list_for_each_entry(call_entry, centry, &callee_env->calls, list) {
+                       inline_irg_env *penv = get_irg_link(centry->callee);
+                       ir_node        *new_call;
+                       call_entry     *new_entry;
+
+                       /* after we have inlined callee, all called methods inside
+                        * callee are now called once more */
+                       ++penv->n_callers;
+
+                       /* Note that the src list points to Call nodes in the inlined graph,
+                        * but we need Call nodes in our graph. Luckily the inliner leaves
+                        * this information in the link field. */
+                       new_call = get_irn_link(centry->call);
+                       assert(is_Call(new_call));
+
+                       new_entry = duplicate_call_entry(centry, new_call, loop_depth);
+                       list_add_tail(&new_entry->list, &env->calls);
+                       maybe_push_call(pqueue, new_entry, inline_threshold);
+               }
+
+               env->n_call_nodes += callee_env->n_call_nodes;
+               env->n_nodes += callee_env->n_nodes;
+               --callee_env->n_callers;
+       }
+
+       del_pqueue(pqueue);
+}
+
+/*
  * Heuristic inliner. Calculates a benefice value for every call and inlines
  * those calls with a value higher than the threshold.
  */
-void inline_functions(int maxsize, int inline_threshold) {
+void inline_functions(unsigned maxsize, int inline_threshold) {
        inline_irg_env   *env;
        int              i, n_irgs;
        ir_graph         *rem;
-       int              did_inline;
        wenv_t           wenv;
-       call_entry       *curr_call, **last_call;
-       const call_entry *centry;
        pmap             *copied_graphs;
        pmap_entry       *pm_entry;
        ir_graph         **irgs;
@@ -2093,7 +2244,7 @@ void inline_functions(int maxsize, int inline_threshold) {
        for (i = 0; i < n_irgs; ++i)
                set_irg_link(irgs[i], alloc_inline_irg_env());
 
-       /* Precompute information in temporary data structure. */
+       /* Pre-compute information in temporary data structure. */
        wenv.ignore_runtime = 0;
        wenv.ignore_callers = 0;
        for (i = 0; i < n_irgs; ++i) {
@@ -2101,136 +2252,16 @@ void inline_functions(int maxsize, int inline_threshold) {
 
                free_callee_info(irg);
 
-               wenv.x         = get_irg_link(irg);
-               wenv.last_call = NULL;
+               wenv.x = get_irg_link(irg);
                assure_cf_loop(irg);
                irg_walk_graph(irg, NULL, collect_calls2, &wenv);
        }
 
        /* -- and now inline. -- */
        for (i = 0; i < n_irgs; ++i) {
-               int      phiproj_computed = 0;
-               ir_node  *call;
                ir_graph *irg = irgs[i];
 
-               current_ir_graph = irg;
-               env = get_irg_link(irg);
-
-               /* note that the list of possible calls is updated during the process */
-               last_call = &env->call_head;
-               for (curr_call = env->call_head; curr_call != NULL;) {
-                       irg_inline_property prop;
-                       ir_graph            *callee;
-                       pmap_entry          *e;
-                       int                 benefice;
-                       unsigned            local_adr;
-
-                       if (env->n_nodes > maxsize) break;
-
-                       call   = curr_call->call;
-                       callee = curr_call->callee;
-
-                       prop = get_irg_inline_property(callee);
-                       if (prop == irg_inline_forbidden || get_irg_additional_properties(callee) & mtp_property_weak) {
-                               /* do not inline forbidden / weak graphs */
-                               continue;
-                       }
-
-                       e = pmap_find(copied_graphs, callee);
-                       if (e != NULL) {
-                               /*
-                               * Remap callee if we have a copy.
-                               * FIXME: Should we do this only for recursive Calls ?
-                               */
-                               callee = e->value;
-                       }
-
-                       /* calculate the benefice on the original call to prevent excessive inlining */
-                       local_adr = 0;
-                       benefice = calc_inline_benefice(call, callee, &local_adr);
-                       DB((dbg, LEVEL_2, "In %+F Call %+F has benefice %d\n", irg, callee, benefice));
-
-                       if (benefice > -inline_threshold || prop >= irg_inline_forced) {
-                               if (current_ir_graph == callee) {
-                                       /*
-                                        * Recursive call: we cannot directly inline because we cannot walk
-                                        * the graph and change it. So we have to make a copy of the graph
-                                        * first.
-                                        */
-
-                                       inline_irg_env *callee_env;
-                                       ir_graph       *copy;
-
-                                       /*
-                                        * No copy yet, create one.
-                                        * Note that recursive methods are never leaves, so it is sufficient
-                                        * to test this condition here.
-                                        */
-                                       copy = create_irg_copy(callee);
-
-                                       /* create_irg_copy() destroys the Proj links, recompute them */
-                                       phiproj_computed = 0;
-
-                                       /* allocate new environment */
-                                       callee_env = alloc_inline_irg_env();
-                                       set_irg_link(copy, callee_env);
-
-                                       assure_cf_loop(copy);
-                                       wenv.x              = callee_env;
-                                       wenv.ignore_callers = 1;
-                                       irg_walk_graph(copy, NULL, collect_calls2, &wenv);
-
-                                       /*
-                                        * Enter the entity of the original graph. This is needed
-                                        * for inline_method(). However, note that ent->irg still points
-                                        * to callee, NOT to copy.
-                                        */
-                                       set_irg_entity(copy, get_irg_entity(callee));
-
-                                       pmap_insert(copied_graphs, callee, copy);
-                                       callee = copy;
-
-                                       /* we have only one caller: the original graph */
-                                       callee_env->n_callers      = 1;
-                                       callee_env->n_callers_orig = 1;
-                               }
-                               if (! phiproj_computed) {
-                                       phiproj_computed = 1;
-                                       collect_phiprojs(current_ir_graph);
-                               }
-                               did_inline = inline_method(call, callee);
-                               if (did_inline) {
-                                       inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
-
-                                       /* was inlined, must be recomputed */
-                                       phiproj_computed = 0;
-
-                                       /* after we have inlined callee, all called methods inside callee
-                                       are now called once more */
-                                       for (centry = callee_env->call_head; centry != NULL; centry = centry->next) {
-                                               inline_irg_env *penv = get_irg_link(centry->callee);
-                                               ++penv->n_callers;
-                                       }
-
-                                       /* callee was inline. Append it's call list. */
-                                       env->got_inline = 1;
-                                       if (local_adr)
-                                               env->local_vars = 1;
-                                       --env->n_call_nodes;
-                                       curr_call = replace_entry_by_call_list(curr_call, callee_env->call_head);
-                                       env->n_call_nodes += callee_env->n_call_nodes;
-                                       env->n_nodes += callee_env->n_nodes;
-                                       --callee_env->n_callers;
-
-                                       /* remove the current call entry from the list */
-                                       *last_call = curr_call;
-                                       continue;
-                               }
-                       }
-                       last_call = &curr_call->next;
-                       curr_call = curr_call->next;
-               }
-
+               inline_into(irg, maxsize, inline_threshold, copied_graphs);
        }
 
        for (i = 0; i < n_irgs; ++i) {
@@ -2239,22 +2270,18 @@ void inline_functions(int maxsize, int inline_threshold) {
                env = get_irg_link(irg);
                if (env->got_inline) {
                        /* this irg got calls inlined: optimize it */
-
-                       if (0) {
-                               /* scalar replacement does not work well with Tuple nodes, so optimize them away */
-                               optimize_graph_df(irg);
-
+                       if (get_opt_combo()) {
+                               if (env->local_vars) {
+                                       scalar_replacement_opt(irg);
+                               }
+                               combo(irg);
+                       } else {
                                if (env->local_vars) {
                                        if (scalar_replacement_opt(irg)) {
                                                optimize_graph_df(irg);
                                        }
                                }
                                optimize_cf(irg);
-                       } else {
-                               if (env->local_vars) {
-                                       scalar_replacement_opt(irg);
-                               }
-                               combo(irg);
                        }
                }
                if (env->got_inline || (env->n_callers_orig != env->n_callers)) {