doxygen docu fixed
[libfirm] / ir / ir / irgopt.c
index 53829b5..27450ba 100644 (file)
@@ -3,14 +3,12 @@
  * File name:   ir/ir/irgopt.c
  * Purpose:     Optimizations for a whole ir graph, i.e., a procedure.
  * Author:      Christian Schaefer, Goetz Lindenmaier
- * Modified by: Sebastian Felis
+ * Modified by: Sebastian Felis, Michael Beck
  * Created:
  * CVS-ID:      $Id$
- * Copyright:   (c) 1998-2003 Universität Karlsruhe
+ * Copyright:   (c) 1998-2006 Universität Karlsruhe
  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
  */
-
-
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
@@ -23,6 +21,7 @@
 
 #include "ircons.h"
 #include "iropt_t.h"
+#include "cfopt.h"
 #include "irgopt.h"
 #include "irgmod.h"
 #include "irgwalk.h"
@@ -30,7 +29,6 @@
 #include "array.h"
 #include "pset.h"
 #include "pmap.h"
-#include "eset.h"
 #include "pdeq.h"       /* Fuer code placement */
 #include "xmalloc.h"
 
@@ -108,8 +106,8 @@ static void kill_dead_blocks(ir_node *block, void *env)
   }
 }
 
-void
-local_optimize_graph (ir_graph *irg) {
+/* Applies local optimizations (see iropt.h) to all nodes reachable from node n. */
+void local_optimize_graph(ir_graph *irg) {
   ir_graph *rem = current_ir_graph;
   current_ir_graph = irg;
 
@@ -121,8 +119,32 @@ local_optimize_graph (ir_graph *irg) {
   current_ir_graph = rem;
 }
 
+/**
+ * Enqueue all users of a node to a wait queue.
+ * Handles mode_T nodes.
+ */
+static void enqueue_users(ir_node *n, pdeq *waitq) {
+  const ir_edge_t *edge;
+
+  foreach_out_edge(n, edge) {
+    ir_node *succ = get_edge_src_irn(edge);
+
+    if (get_irn_link(succ) != waitq) {
+      pdeq_putr(waitq, succ);
+      set_irn_link(succ, waitq);
+    }
+    if (get_irn_mode(succ) == mode_T) {
+      /* A mode_T node has Proj's. Because most optimizations
+         run on the Proj's we have to enqueue them also. */
+      enqueue_users(succ, waitq);
+    }
+  }
+}
+
 /**
  * Data flow optimization walker.
+ * Optimizes all nodes and enqueue it's users
+ * if done.
  */
 static void opt_walker(ir_node *n, void *env) {
   pdeq *waitq = env;
@@ -132,20 +154,12 @@ static void opt_walker(ir_node *n, void *env) {
   set_irn_link(optimized, NULL);
 
   if (optimized != n) {
-    const ir_edge_t *edge;
-
-    foreach_out_edge(n, edge) {
-      ir_node *succ = get_edge_src_irn(edge);
-
-      if (get_irn_link(succ) != waitq) {
-        pdeq_putr(waitq, succ);
-        set_irn_link(succ, waitq);
-      }
-    }
+    enqueue_users(n, waitq);
     exchange(n, optimized);
   }
 }
 
+/* Applies local optimizations to all nodes in the graph until fixpoint. */
 void optimize_graph_df(ir_graph *irg) {
   pdeq     *waitq = new_pdeq();
   int      state = edges_activated(irg);
@@ -163,7 +177,7 @@ void optimize_graph_df(ir_graph *irg) {
   del_identities(irg->value_table);
   irg->value_table = new_identities();
 
-  if (get_irg_dom_state(current_ir_graph) == dom_consistent)
+  if (get_irg_dom_state(irg) == dom_consistent)
     irg_block_walk_graph(irg, NULL, kill_dead_blocks, NULL);
 
   /* invalidate info */
@@ -177,7 +191,8 @@ void optimize_graph_df(ir_graph *irg) {
   /* finish the wait queue */
   while (! pdeq_empty(waitq)) {
     ir_node *n = pdeq_getl(waitq);
-    opt_walker(n, waitq);
+    if (! is_Bad(n))
+      opt_walker(n, waitq);
   }
 
   del_pdeq(waitq);
@@ -256,7 +271,6 @@ static void copy_node(ir_node *n, void *env) {
   ir_node *nn, *block;
   int new_arity;
   ir_op *op = get_irn_op(n);
-  int copy_node_nr = env != NULL;
 
   /* The end node looses it's flexible in array.  This doesn't matter,
      as dead node elimination builds End by hand, inlineing doesn't use
@@ -291,11 +305,14 @@ static void copy_node(ir_node *n, void *env) {
   copy_node_attr(n, nn);
   new_backedge_info(nn);
 
-#if DEBUG_libfirm
+#ifdef DEBUG_libfirm
+  {
+  int copy_node_nr = env != NULL;
   if (copy_node_nr) {
     /* for easier debugging, we want to copy the node numbers too */
     nn->node_nr = n->node_nr;
   }
+  }
 #endif
 
   set_new_node(n, nn);
@@ -307,7 +324,7 @@ static void copy_node(ir_node *n, void *env) {
  * Spare the Bad predecessors of Phi and Block nodes.
  */
 void
-copy_preds (ir_node *n, void *env) {
+copy_preds(ir_node *n, void *env) {
   ir_node *nn, *block;
   int i, j, irn_arity;
 
@@ -351,12 +368,12 @@ copy_preds (ir_node *n, void *env) {
     /* Don't copy node if corresponding predecessor in block is Bad.
        The Block itself should not be Bad. */
     block = get_nodes_block(n);
-    set_irn_n (nn, -1, get_new_node(block));
+    set_irn_n(nn, -1, get_new_node(block));
     j = 0;
     irn_arity = get_irn_arity(n);
     for (i = 0; i < irn_arity; i++)
       if (! is_Bad(get_irn_n(block, i))) {
-        set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
+        set_irn_n(nn, j, get_new_node(get_irn_n(n, i)));
         /*if (is_backedge(n, i)) set_backedge(nn, j);*/
         j++;
       }
@@ -375,7 +392,7 @@ copy_preds (ir_node *n, void *env) {
   /* Now the new node is complete.  We can add it to the hash table for CSE.
      @@@ inlining aborts if we identify End. Why? */
   if (get_irn_op(nn) != op_End)
-    add_identities (current_ir_graph->value_table, nn);
+    add_identities(current_ir_graph->value_table, nn);
 }
 
 /**
@@ -456,22 +473,23 @@ static void copy_graph(ir_graph *irg, int copy_node_nr) {
   /*- ... and now the keep alives. -*/
   /* First pick the not marked block nodes and walk them.  We must pick these
      first as else we will oversee blocks reachable from Phis. */
-  irn_arity = get_irn_arity(oe);
+  irn_arity = get_End_n_keepalives(oe);
   for (i = 0; i < irn_arity; i++) {
-    ka = get_irn_intra_n(oe, i);
-    if (is_Block(ka) &&
-        (get_irn_visited(ka) <= vfl)) {
-      /* We must keep the block alive and copy everything reachable */
-      set_irg_visited(irg, vfl);
-      irg_walk(ka, copy_node, copy_preds, INT_TO_PTR(copy_node_nr));
+    ka = get_End_keepalive(oe, i);
+    if (is_Block(ka)) {
+      if (get_irn_visited(ka) <= vfl) {
+        /* We must keep the block alive and copy everything reachable */
+        set_irg_visited(irg, vfl);
+        irg_walk(ka, copy_node, copy_preds, INT_TO_PTR(copy_node_nr));
+      }
       add_End_keepalive(ne, get_new_node(ka));
     }
   }
 
   /* Now pick other nodes.  Here we will keep all! */
-  irn_arity = get_irn_arity(oe);
+  irn_arity = get_End_n_keepalives(oe);
   for (i = 0; i < irn_arity; i++) {
-    ka = get_irn_intra_n(oe, i);
+    ka = get_End_keepalive(oe, i);
     if (!is_Block(ka)) {
       if (get_irn_visited(ka) <= vfl) {
         /* We didn't copy the node yet.  */
@@ -496,7 +514,7 @@ static void copy_graph(ir_graph *irg, int copy_node_nr) {
  * @param copy_node_nr  If non-zero, the node number will be copied
  */
 static void
-copy_graph_env (int copy_node_nr) {
+copy_graph_env(int copy_node_nr) {
   ir_graph *irg = current_ir_graph;
   ir_node *old_end, *n;
   int i;
@@ -509,10 +527,10 @@ copy_graph_env (int copy_node_nr) {
   /* Not all nodes remembered in irg might be reachable
      from the end node.  Assure their link is set to NULL, so that
      we can test whether new nodes have been computed. */
-  for (i = anchor_max - 1; i >= 0; --i)
+  for (i = anchor_max - 1; i >= 0; --i) {
     if (irg->anchors[i])
       set_new_node(irg->anchors[i], NULL);
-
+  }
   /* we use the block walk flag for removing Bads from Blocks ins. */
   inc_irg_block_visited(irg);
 
@@ -539,12 +557,11 @@ copy_graph_env (int copy_node_nr) {
  */
 void
 dead_node_elimination(ir_graph *irg) {
-  ir_graph *rem;
-  int rem_ipview = get_interprocedural_view();
-  struct obstack *graveyard_obst = NULL;
-  struct obstack *rebirth_obst   = NULL;
-
   if (get_opt_optimize() && get_opt_dead_node_elimination()) {
+    ir_graph *rem;
+    int rem_ipview = get_interprocedural_view();
+    struct obstack *graveyard_obst = NULL;
+    struct obstack *rebirth_obst   = NULL;
     assert(! edges_activated(irg) && "dead node elimination requires disabled edges");
 
     /* inform statistics that we started a dead-node elimination run */
@@ -555,15 +572,15 @@ dead_node_elimination(ir_graph *irg) {
     current_ir_graph = irg;
     set_interprocedural_view(0);
 
-    assert(get_irg_phase_state(current_ir_graph) != phase_building);
+    assert(get_irg_phase_state(irg) != phase_building);
 
     /* Handle graph state */
-    free_callee_info(current_ir_graph);
-    free_irg_outs(current_ir_graph);
+    free_callee_info(irg);
+    free_irg_outs(irg);
     free_trouts();
 
     /* @@@ so far we loose loops when copying */
-    free_loop_information(current_ir_graph);
+    free_loop_information(irg);
 
     set_irg_doms_inconsistent(irg);
 
@@ -573,16 +590,16 @@ dead_node_elimination(ir_graph *irg) {
 
     /* A new obstack, where the reachable nodes will be copied to. */
     rebirth_obst = xmalloc(sizeof(*rebirth_obst));
-    current_ir_graph->obst = rebirth_obst;
-    obstack_init (current_ir_graph->obst);
-    current_ir_graph->last_node_idx = 0;
+    irg->obst = rebirth_obst;
+    obstack_init(irg->obst);
+    irg->last_node_idx = 0;
 
     /* We also need a new value table for CSE */
     del_identities(irg->value_table);
     irg->value_table = new_identities();
 
     /* Copy the graph from the old to the new obstack */
-    copy_graph_env(1);
+    copy_graph_env(/*copy_node_nr=*/1);
 
     /* Free memory from old unoptimized obstack */
     obstack_free(graveyard_obst, 0);  /* First empty the obstack ... */
@@ -851,11 +868,14 @@ copy_node_inline (ir_node *n, void *env) {
   }
 }
 
-static void find_addr(ir_node *node, void *env)
-{
-  if (get_irn_opcode(node) == iro_Proj) {
+/**
+ * Walker: checks if P_value_arg_base is used.
+ */
+static void find_addr(ir_node *node, void *env) {
+  int *allow_inline = env;
+  if (is_Proj(node) && get_irn_op(get_Proj_pred(node)) == op_Start) {
     if (get_Proj_proj(node) == pn_Start_P_value_arg_base)
-      *(int *)env = 0;
+      *allow_inline = 0;
   }
 }
 
@@ -875,7 +895,7 @@ static int can_inline(ir_node *call, ir_graph *called_graph)
   params = get_method_n_params(call_type);
   ress   = get_method_n_ress(call_type);
 
-  /* check params */
+  /* check parameters for compound arguments */
   for (i = 0; i < params; ++i) {
     ir_type *p_type = get_method_param_type(call_type, i);
 
@@ -883,7 +903,7 @@ static int can_inline(ir_node *call, ir_graph *called_graph)
       return 0;
   }
 
-  /* check res */
+  /* check results for compound arguments */
   for (i = 0; i < ress; ++i) {
     ir_type *r_type = get_method_res_type(call_type, i);
 
@@ -897,6 +917,7 @@ static int can_inline(ir_node *call, ir_graph *called_graph)
   return res;
 }
 
+/* Inlines a method at the given call site. */
 int inline_method(ir_node *call, ir_graph *called_graph) {
   ir_node *pre_call;
   ir_node *post_call, *post_bl;
@@ -910,7 +931,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
   ir_type *called_frame;
   irg_inline_property prop = get_irg_inline_property(called_graph);
 
-  if ( (prop != irg_inline_forced) &&
+  if ( (prop < irg_inline_forced) &&
        (!get_opt_optimize() || !get_opt_inline() || (prop == irg_inline_forbidden))) return 0;
 
   /* Do not inline variadic functions. */
@@ -949,7 +970,6 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
      assert(smaller_type(get_entity_type(get_irg_entity(called_graph)),
      get_Call_type(call)));
   */
-  assert(get_type_tpop(get_Call_type(call)) == type_method);
   if (called_graph == current_ir_graph) {
     set_optimize(rem_opt);
     return 0;
@@ -966,8 +986,8 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
      2 Exception handling not represented in Firm. -- */
   {
     ir_node *proj, *Mproj = NULL, *Xproj = NULL;
-    for (proj = (ir_node *)get_irn_link(call); proj; proj = (ir_node *)get_irn_link(proj)) {
-      assert(get_irn_op(proj) == op_Proj);
+    for (proj = get_irn_link(call); proj; proj = get_irn_link(proj)) {
+      assert(is_Proj(proj));
       if (get_Proj_proj(proj) == pn_Call_X_except) Xproj = proj;
       if (get_Proj_proj(proj) == pn_Call_M_except) Mproj = proj;
     }
@@ -1026,7 +1046,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
   /* copy the entities. */
   called_frame = get_irg_frame_type(called_graph);
   for (i = 0; i < get_class_n_members(called_frame); i++) {
-    entity *new_ent, *old_ent;
+    ir_entity *new_ent, *old_ent;
     old_ent = get_class_member(called_frame, i);
     new_ent = copy_entity_own(old_ent, get_cur_frame_type());
     set_entity_link(old_ent, new_ent);
@@ -1215,51 +1235,6 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
   free(res_pred);
   free(cf_pred);
 
-#if 0  /* old. now better, correcter, faster implementation. */
-  if (n_exc > 0) {
-    /* -- If the exception control flow from the inlined Call directly
-       branched to the end block we now have the following control
-       flow predecessor pattern: ProjX -> Tuple -> Jmp.  We must
-       remove the Jmp along with it's empty block and add Jmp's
-       predecessors as predecessors of this end block.  No problem if
-       there is no exception, because then branches Bad to End which
-       is fine. --
-       @@@ can't we know this beforehand: by getting the Proj(1) from
-       the Call link list and checking whether it goes to Proj. */
-    /* find the problematic predecessor of the end block. */
-    end_bl = get_irg_end_block(current_ir_graph);
-    for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
-      cf_op = get_Block_cfgpred(end_bl, i);
-      if (get_irn_op(cf_op) == op_Proj) {
-        cf_op = get_Proj_pred(cf_op);
-        if ((get_irn_op(cf_op) == op_Tuple) && (cf_op == call)) {
-          /*  There are unoptimized tuples from inlineing before when no exc */
-          assert(get_Proj_proj(get_Block_cfgpred(end_bl, i)) == pn_Call_X_except);
-          cf_op = get_Tuple_pred(cf_op, pn_Call_X_except);
-          assert(get_irn_op(cf_op) == op_Jmp);
-          break;
-        }
-      }
-    }
-    /* repair */
-    if (i < get_Block_n_cfgpreds(end_bl)) {
-      bl = get_nodes_block(cf_op);
-      arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
-      cf_pred = xmalloc (arity * sizeof(*cf_pred));
-      for (j = 0; j < i; j++)
-        cf_pred[j] = get_Block_cfgpred(end_bl, j);
-      for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
-        cf_pred[j] = get_Block_cfgpred(bl, j-i);
-      for (j = j; j < arity; j++)
-        cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
-      set_irn_in(end_bl, arity, cf_pred);
-      free(cf_pred);
-      /*  Remove the exception pred from post-call Tuple. */
-      set_Tuple_pred(call, pn_Call_X_except, new_Bad());
-    }
-  }
-#endif
-
   /* --  Turn CSE back on. -- */
   set_optimize(rem_opt);
 
@@ -1270,16 +1245,21 @@ int inline_method(ir_node *call, ir_graph *called_graph) {
 /* Apply inlineing to small methods.                                */
 /********************************************************************/
 
-/* It makes no sense to inline too many calls in one procedure. Anyways,
-   I didn't get a version with NEW_ARR_F to run. */
-#define MAX_INLINE 1024
+/** Represents a possible inlinable call in a graph. */
+typedef struct _call_entry call_entry;
+struct _call_entry {
+  ir_node    *call;   /**< the Call */
+  ir_graph   *callee; /**< the callee called here */
+  call_entry *next;   /**< for linking the next one */
+};
 
 /**
  * environment for inlining small irgs
  */
 typedef struct _inline_env_t {
-  int pos;
-  ir_node *calls[MAX_INLINE];
+  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 */
 } inline_env_t;
 
 /**
@@ -1290,31 +1270,33 @@ static ir_graph *get_call_called_irg(ir_node *call) {
   ir_node *addr;
   ir_graph *called_irg = NULL;
 
-  assert(is_Call(call));
-
   addr = get_Call_ptr(call);
-  if ((get_irn_op(addr) == op_SymConst) && (get_SymConst_kind (addr) == symconst_addr_ent)) {
+  if (is_SymConst(addr) && get_SymConst_kind(addr) == symconst_addr_ent) {
     called_irg = get_entity_irg(get_SymConst_entity(addr));
   }
 
   return called_irg;
 }
 
+/**
+ * Walker: Collect all calls to known graphs inside a graph.
+ */
 static void collect_calls(ir_node *call, void *env) {
-  ir_node *addr;
-
-  if (! is_Call(call)) return;
-
-  addr = get_Call_ptr(call);
-
-  if (get_irn_op(addr) == op_SymConst) {
-    if (get_SymConst_kind(addr) == symconst_addr_ent) {
-      ir_graph *called_irg = get_entity_irg(get_SymConst_entity(addr));
-      inline_env_t *ienv = (inline_env_t *)env;
-      if (called_irg && ienv->pos < MAX_INLINE) {
-        /* The Call node calls a locally defined method.  Remember to inline. */
-        ienv->calls[ienv->pos++] = call;
-      }
+  if (is_Call(call)) {
+    ir_graph *called_irg = get_call_called_irg(call);
+    if (called_irg) {
+      /* The Call node calls a locally defined method.  Remember to inline. */
+      inline_env_t *ienv  = env;
+      call_entry   *entry = obstack_alloc(&ienv->obst, sizeof(*entry));
+      entry->call   = call;
+      entry->callee = called_irg;
+      entry->next   = NULL;
+
+      if (ienv->tail == NULL)
+        ienv->head = entry;
+      else
+        ienv->tail->next = entry;
+      ienv->tail = entry;
     }
   }
 }
@@ -1328,38 +1310,41 @@ static void collect_calls(ir_node *call, void *env) {
  * size are inlined.
  */
 void inline_small_irgs(ir_graph *irg, int size) {
-  int i;
   ir_graph *rem = current_ir_graph;
-  inline_env_t env /* = {0, NULL}*/;
+  inline_env_t env;
+  call_entry *entry;
+  DEBUG_ONLY(firm_dbg_module_t *dbg;)
 
   if (!(get_opt_optimize() && get_opt_inline())) return;
 
+  FIRM_DBG_REGISTER(dbg, "firm.opt.inline");
+
   current_ir_graph = irg;
   /* Handle graph state */
-  assert(get_irg_phase_state(current_ir_graph) != phase_building);
-  free_callee_info(current_ir_graph);
+  assert(get_irg_phase_state(irg) != phase_building);
+  free_callee_info(irg);
 
   /* Find Call nodes to inline.
      (We can not inline during a walk of the graph, as inlineing 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.) */
-  env.pos = 0;
-  irg_walk(get_irg_end(irg), NULL, collect_calls, &env);
+  obstack_init(&env.obst);
+  env.head = env.tail = NULL;
+  irg_walk_graph(irg, NULL, collect_calls, &env);
 
-  if ((env.pos > 0) && (env.pos < MAX_INLINE)) {
+  if (env.head != NULL) {
     /* There are calls to inline */
     collect_phiprojs(irg);
-    for (i = 0; i < env.pos; i++) {
-      ir_graph *callee;
-      callee = get_entity_irg(get_SymConst_entity(get_Call_ptr(env.calls[i])));
+    for (entry = env.head; entry != NULL; entry = entry->next) {
+      ir_graph *callee = entry->callee;
       if (((_obstack_memory_used(callee->obst) - (int)obstack_room(callee->obst)) < size) ||
-        (get_irg_inline_property(callee) == irg_inline_forced)) {
-        inline_method(env.calls[i], callee);
+          (get_irg_inline_property(callee) >= irg_inline_forced)) {
+        inline_method(entry->call, callee);
       }
     }
   }
-
+  obstack_free(&env.obst, NULL);
   current_ir_graph = rem;
 }
 
@@ -1367,66 +1352,92 @@ void inline_small_irgs(ir_graph *irg, int size) {
  * Environment for inlining irgs.
  */
 typedef struct {
-  int n_nodes;       /**< Nodes in graph except Id, Tuple, Proj, Start, End */
-  int n_nodes_orig;  /**< for statistics */
-  eset *call_nodes;  /**< All call nodes in this graph */
-  int n_call_nodes;
-  int n_call_nodes_orig; /**< for statistics */
-  int n_callers;   /**< Number of known graphs that call this graphs. */
-  int n_callers_orig; /**< for statistics */
+  int n_nodes;             /**< Number of nodes in graph except Id, Tuple, Proj, Start, End. */
+  int n_nodes_orig;        /**< for statistics */
+  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 .*/
+  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 */
+  int got_inline;          /**< Set, if at leat one call inside this graph was inlined. */
 } inline_irg_env;
 
 /**
  * Allocate a new environment for inlining.
  */
-static inline_irg_env *new_inline_irg_env(void) {
-  inline_irg_env *env    = xmalloc(sizeof(*env));
+static inline_irg_env *alloc_inline_irg_env(struct obstack *obst) {
+  inline_irg_env *env    = obstack_alloc(obst, sizeof(*env));
   env->n_nodes           = -2; /* do not count count Start, End */
   env->n_nodes_orig      = -2; /* do not count Start, End */
-  env->call_nodes        = eset_create();
+  env->call_head         = NULL;
+  env->call_tail         = NULL;
   env->n_call_nodes      = 0;
   env->n_call_nodes_orig = 0;
   env->n_callers         = 0;
   env->n_callers_orig    = 0;
+  env->got_inline        = 0;
   return env;
 }
 
-/**
- * destroy an environment for inlining.
- */
-static void free_inline_irg_env(inline_irg_env *env) {
-  eset_destroy(env->call_nodes);
-  free(env);
-}
+typedef struct walker_env {
+  struct obstack *obst; /**< the obstack for allocations. */
+  inline_irg_env *x;    /**< the inline environment */
+  int ignore_runtime;   /**< the ignore runtime flag */
+} wenv_t;
 
 /**
  * post-walker: collect all calls in the inline-environment
  * of a graph and sum some statistics.
  */
-static void collect_calls2(ir_node *call, void *env) {
-  inline_irg_env *x = (inline_irg_env *)env;
-  ir_op *op = get_irn_op(call);
-  ir_graph *callee;
+static void collect_calls2(ir_node *call, void *ctx) {
+  wenv_t         *env = ctx;
+  inline_irg_env *x = env->x;
+  ir_op          *op = get_irn_op(call);
+  ir_graph       *callee;
+  call_entry     *entry;
 
   /* count meaningful nodes in irg */
   if (op != op_Proj && op != op_Tuple && op != op_Sync) {
-    x->n_nodes++;
-    x->n_nodes_orig++;
+    ++x->n_nodes;
+    ++x->n_nodes_orig;
   }
 
   if (op != op_Call) return;
 
+  /* check, if it's a runtime call */
+  if (env->ignore_runtime) {
+    ir_node *symc = get_Call_ptr(call);
+
+    if (is_SymConst(symc) && get_SymConst_kind(symc) == symconst_addr_ent) {
+      ir_entity *ent = get_SymConst_entity(symc);
+
+      if (get_entity_additional_properties(ent) & mtp_property_runtime)
+        return;
+    }
+  }
+
   /* collect all call nodes */
-  eset_insert(x->call_nodes, call);
-  x->n_call_nodes++;
-  x->n_call_nodes_orig++;
+  ++x->n_call_nodes;
+  ++x->n_call_nodes_orig;
 
-  /* count all static callers */
   callee = get_call_called_irg(call);
   if (callee) {
     inline_irg_env *callee_env = get_irg_link(callee);
-    callee_env->n_callers++;
-    callee_env->n_callers_orig++;
+    /* count all static callers */
+    ++callee_env->n_callers;
+    ++callee_env->n_callers_orig;
+
+    /* link it in the list of possible inlinable entries */
+    entry = obstack_alloc(env->obst, sizeof(*entry));
+    entry->call   = call;
+    entry->callee = callee;
+    entry->next   = NULL;
+    if (x->call_tail == NULL)
+      x->call_head = entry;
+    else
+      x->call_tail->next = entry;
+    x->call_tail = entry;
   }
 }
 
@@ -1435,16 +1446,36 @@ static void collect_calls2(ir_node *call, void *env) {
  * hence this irg is a leave.
  */
 INLINE static int is_leave(ir_graph *irg) {
-  return (((inline_irg_env *)get_irg_link(irg))->n_call_nodes == 0);
+  inline_irg_env *env = get_irg_link(irg);
+  return env->n_call_nodes == 0;
 }
 
 /**
  * Returns TRUE if the number of callers is smaller size in the irg's environment.
  */
 INLINE static int is_smaller(ir_graph *callee, int size) {
-  return (((inline_irg_env *)get_irg_link(callee))->n_nodes < 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.
+ */
+static void append_call_list(struct obstack *obst, 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(obst, sizeof(*nentry));
+    nentry->call   = get_irn_link(entry->call);
+    nentry->callee = entry->callee;
+    nentry->next   = NULL;
+    dst->call_tail->next = nentry;
+    dst->call_tail       = nentry;
+  }
+}
 
 /*
  * Inlines small leave methods at call sites where the called address comes
@@ -1454,32 +1485,46 @@ INLINE static int is_smaller(ir_graph *callee, int size) {
  * Methods where the obstack containing the firm graph is smaller than
  * size are inlined.
  */
-void inline_leave_functions(int maxsize, int leavesize, int size) {
-  inline_irg_env *env;
-  int i, n_irgs = get_irp_n_irgs();
-  ir_graph *rem = current_ir_graph;
-  int did_inline = 1;
+void inline_leave_functions(int maxsize, int leavesize, int 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;
+  const call_entry *centry;
+  struct obstack   obst;
+  DEBUG_ONLY(firm_dbg_module_t *dbg;)
 
   if (!(get_opt_optimize() && get_opt_inline())) return;
 
+  FIRM_DBG_REGISTER(dbg, "firm.opt.inline");
+  rem = current_ir_graph;
+  obstack_init(&obst);
+
   /* extend all irgs by a temporary data structure for inlining. */
+  n_irgs = get_irp_n_irgs();
   for (i = 0; i < n_irgs; ++i)
-    set_irg_link(get_irp_irg(i), new_inline_irg_env());
+    set_irg_link(get_irp_irg(i), alloc_inline_irg_env(&obst));
 
   /* Precompute information in temporary data structure. */
+  wenv.obst           = &obst;
+  wenv.ignore_runtime = ignore_runtime;
   for (i = 0; i < n_irgs; ++i) {
-    current_ir_graph = get_irp_irg(i);
-    assert(get_irg_phase_state(current_ir_graph) != phase_building);
-    free_callee_info(current_ir_graph);
+    ir_graph *irg = get_irp_irg(i);
 
-    irg_walk(get_irg_end(current_ir_graph), NULL, collect_calls2,
-             get_irg_link(current_ir_graph));
+    assert(get_irg_phase_state(irg) != phase_building);
+    free_callee_info(irg);
+
+    wenv.x = get_irg_link(irg);
+    irg_walk_graph(irg, NULL, collect_calls2, &wenv);
   }
 
   /* -- and now inline. -- */
 
   /* Inline leaves recursively -- we might construct new leaves. */
-  while (did_inline) {
+  do {
     did_inline = 0;
 
     for (i = 0; i < n_irgs; ++i) {
@@ -1489,15 +1534,16 @@ void inline_leave_functions(int maxsize, int leavesize, int size) {
       current_ir_graph = get_irp_irg(i);
       env = (inline_irg_env *)get_irg_link(current_ir_graph);
 
-      for (call = eset_first(env->call_nodes); call; call = eset_next(env->call_nodes)) {
+      tail = NULL;
+      for (entry = env->call_head; entry != NULL; entry = entry->next) {
         ir_graph *callee;
 
-        if (get_irn_op(call) == op_Tuple) continue;   /* We already have inlined this call. */
-        callee = get_call_called_irg(call);
+        if (env->n_nodes > maxsize) break;
 
-        if (env->n_nodes > maxsize) continue; // break;
+        call   = entry->call;
+        callee = entry->callee;
 
-        if (callee && (is_leave(callee) && is_smaller(callee, leavesize))) {
+        if (is_leave(callee) && is_smaller(callee, leavesize)) {
           if (!phiproj_computed) {
             phiproj_computed = 1;
             collect_phiprojs(current_ir_graph);
@@ -1507,70 +1553,99 @@ void inline_leave_functions(int maxsize, int leavesize, int size) {
           if (did_inline) {
             /* Do some statistics */
             inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
-            env->n_call_nodes --;
+
+            env->got_inline = 1;
+            --env->n_call_nodes;
             env->n_nodes += callee_env->n_nodes;
-            callee_env->n_callers--;
+            --callee_env->n_callers;
+
+            /* remove this call from the list */
+            if (tail != NULL)
+              tail->next = entry->next;
+            else
+              env->call_head = entry->next;
+            continue;
           }
         }
+        tail = entry;
       }
+      env->call_tail = tail;
     }
-  }
+  } while (did_inline);
 
   /* inline other small functions. */
   for (i = 0; i < n_irgs; ++i) {
     ir_node *call;
-    eset *walkset;
     int phiproj_computed = 0;
 
     current_ir_graph = get_irp_irg(i);
     env = (inline_irg_env *)get_irg_link(current_ir_graph);
 
-    /* we can not walk and change a set, nor remove from it.
-       So recompute.*/
-    walkset = env->call_nodes;
-    env->call_nodes = eset_create();
-    for (call = eset_first(walkset); call; call = eset_next(walkset)) {
+    /* note that the list of possible calls is updated during the process */
+    tail = NULL;
+    for (entry = env->call_head; entry != NULL; entry = entry->next) {
       ir_graph *callee;
 
-      if (get_irn_op(call) == op_Tuple) continue;   /* We already inlined. */
-      callee = get_call_called_irg(call);
+      call   = entry->call;
+      callee = entry->callee;
 
-      if (callee &&
-          ((is_smaller(callee, size) && (env->n_nodes < maxsize)) ||    /* small function */
-           (get_irg_inline_property(callee) == irg_inline_forced))) {
+      if (((is_smaller(callee, size) && (env->n_nodes < maxsize)) ||    /* small function */
+           (get_irg_inline_property(callee) >= irg_inline_forced))) {
         if (!phiproj_computed) {
             phiproj_computed = 1;
             collect_phiprojs(current_ir_graph);
         }
         if (inline_method(call, callee)) {
           inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
-          env->n_call_nodes--;
-          eset_insert_all(env->call_nodes, callee_env->call_nodes);  /* @@@ ??? This are the wrong nodes !? Not the copied ones. */
+
+          /* callee was inline. Append it's call list. */
+          env->got_inline = 1;
+          --env->n_call_nodes;
+          append_call_list(&obst, env, callee_env->call_head);
           env->n_call_nodes += callee_env->n_call_nodes;
           env->n_nodes += callee_env->n_nodes;
-          callee_env->n_callers--;
+          --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) {
+            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;
+          continue;
         }
-      } else {
-        eset_insert(env->call_nodes, call);
       }
+      tail = entry;
     }
-    eset_destroy(walkset);
+    env->call_tail = tail;
   }
 
   for (i = 0; i < n_irgs; ++i) {
-    current_ir_graph = get_irp_irg(i);
-#if 0
-    env = (inline_irg_env *)get_irg_link(current_ir_graph);
-    if ((env->n_call_nodes_orig != env->n_call_nodes) ||
-        (env->n_callers_orig != env->n_callers))
-      printf("Nodes:%3d ->%3d, calls:%3d ->%3d, callers:%3d ->%3d, -- %s\n",
+    irg = get_irp_irg(i);
+    env = (inline_irg_env *)get_irg_link(irg);
+
+    if (env->got_inline) {
+      /* this irg got calls inlined */
+      set_irg_outs_inconsistent(irg);
+      set_irg_doms_inconsistent(irg);
+
+      optimize_graph_df(irg);
+      optimize_cf(irg);
+    }
+    if (env->got_inline || (env->n_callers_orig != env->n_callers))
+      DB((dbg, SET_LEVEL_1, "Nodes:%3d ->%3d, calls:%3d ->%3d, callers:%3d ->%3d, -- %s\n",
              env->n_nodes_orig, env->n_nodes, env->n_call_nodes_orig, env->n_call_nodes,
              env->n_callers_orig, env->n_callers,
-             get_entity_name(get_irg_entity(current_ir_graph)));
-#endif
-    free_inline_irg_env((inline_irg_env *)get_irg_link(current_ir_graph));
+             get_entity_name(get_irg_entity(irg))));
   }
 
+  obstack_free(&obst, NULL);
   current_ir_graph = rem;
 }
 
@@ -1606,7 +1681,7 @@ is_Block_unreachable(ir_node *block) {
  * the control flow will be dead later.
  */
 static void
-place_floats_early(ir_node *n, pdeq *worklist)
+place_floats_early(ir_node *n, waitq *worklist)
 {
   int i, irn_arity;
 
@@ -1621,12 +1696,9 @@ place_floats_early(ir_node *n, pdeq *worklist)
     int depth           = 0;
     ir_node *b          = NULL;   /* The block to place this node in */
 
-    assert(get_irn_op(n) != op_Block);
+    assert(is_no_Block(n));
 
-    if ((get_irn_op(n) == op_Const) ||
-        (get_irn_op(n) == op_SymConst) ||
-        (is_Bad(n)) ||
-        (get_irn_op(n) == op_Unknown)) {
+    if (is_irn_start_block_placed(n)) {
       /* These nodes will not be placed by the loop below. */
       b = get_irg_start_block(current_ir_graph);
       depth = 1;
@@ -1645,7 +1717,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
          * If the current node is NOT in a dead block, but one of its
          * predecessors is, we must move the predecessor to a live block.
          * Such thing can happen, if global CSE chose a node from a dead block.
-         * We move it simple to our block.
+         * We move it simply to our block.
          * Note that neither Phi nor End nodes are floating, so we don't
          * need to handle them here.
          */
@@ -1698,7 +1770,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
     for (i = -1; i < irn_arity; ++i) {
       ir_node *pred = get_irn_n(n, i);
       if (irn_not_visited(pred))
-        pdeq_putr(worklist, pred);
+        waitq_put(worklist, pred);
     }
   }
   else if (is_Block(n)) {
@@ -1709,7 +1781,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
     for (i = irn_arity - 1; i >= 0; --i) {
       ir_node *pred = get_irn_n(n, i);
       if (irn_not_visited(pred))
-        pdeq_putr(worklist, pred);
+        waitq_put(worklist, pred);
     }
   }
   else if (is_Phi(n)) {
@@ -1723,7 +1795,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
      */
     pred = get_irn_n(n, -1);
     if (irn_not_visited(pred))
-      pdeq_putr(worklist, pred);
+      waitq_put(worklist, pred);
 
     for (i = irn_arity - 1; i >= 0; --i) {
       ir_node *pred = get_irn_n(n, i);
@@ -1734,7 +1806,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
             is_Block_unreachable(get_irn_n(pred, -1))) {
           set_nodes_block(pred, get_Block_cfgpred_block(curr_block, i));
         }
-        pdeq_putr(worklist, pred);
+        waitq_put(worklist, pred);
       }
     }
   }
@@ -1748,7 +1820,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
      */
     pred = get_irn_n(n, -1);
     if (irn_not_visited(pred))
-      pdeq_putr(worklist, pred);
+      waitq_put(worklist, pred);
 
     for (i = irn_arity - 1; i >= 0; --i) {
       ir_node *pred = get_irn_n(n, i);
@@ -1759,7 +1831,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
             is_Block_unreachable(get_irn_n(pred, -1))) {
           set_nodes_block(pred, curr_block);
         }
-        pdeq_putr(worklist, pred);
+        waitq_put(worklist, pred);
       }
     }
   }
@@ -1771,7 +1843,7 @@ place_floats_early(ir_node *n, pdeq *worklist)
  * places all floating nodes reachable from its argument through floating
  * nodes and adds all beginnings at op_pin_state_pinned nodes to the worklist.
  */
-static INLINE void place_early(pdeq *worklist) {
+static INLINE void place_early(waitq *worklist) {
   assert(worklist);
   inc_irg_visited(current_ir_graph);
 
@@ -1779,8 +1851,8 @@ static INLINE void place_early(pdeq *worklist) {
   place_floats_early(get_irg_end(current_ir_graph), worklist);
 
   /* Work the content of the worklist. */
-  while (!pdeq_empty(worklist)) {
-    ir_node *n = pdeq_getl(worklist);
+  while (!waitq_empty(worklist)) {
+    ir_node *n = waitq_get(worklist);
     if (irn_not_visited(n))
       place_floats_early(n, worklist);
   }
@@ -1869,8 +1941,7 @@ static INLINE int get_irn_loop_depth(ir_node *n) {
  * @param n      the node that should be moved
  * @param early  the earliest block we can n move to
  */
-static void
-move_out_of_loops (ir_node *n, ir_node *early)
+static void move_out_of_loops(ir_node *n, ir_node *early)
 {
   ir_node *best, *dca;
   assert(n && early);
@@ -1908,8 +1979,7 @@ move_out_of_loops (ir_node *n, ir_node *early)
  * loops as possible and then makes it as control dependent as
  * possible.
  */
-static void
-place_floats_late(ir_node *n, pdeq *worklist)
+static void place_floats_late(ir_node *n, pdeq *worklist)
 {
   int i;
   ir_node *early_blk;
@@ -1948,12 +2018,15 @@ place_floats_late(ir_node *n, pdeq *worklist)
 
     if (! is_Block_dead(early_blk)) {
       /* do only move things that where not dead */
+      ir_op *op = get_irn_op(n);
 
       /* We have to determine the final block of this node... except for
-         constants. */
+         constants and Projs */
       if ((get_irn_pinned(n) == op_pin_state_floats) &&
-          (get_irn_op(n) != op_Const) &&
-          (get_irn_op(n) != op_SymConst)) {
+          (op != op_Const)    &&
+          (op != op_SymConst) &&
+          (op != op_Proj))
+      {
         ir_node *dca = NULL;  /* deepest common ancestor in the
                      dominator tree of all nodes'
                      blocks depending on us; our final
@@ -1994,7 +2067,7 @@ place_floats_late(ir_node *n, pdeq *worklist)
   }
 }
 
-static INLINE void place_late(pdeq *worklist) {
+static INLINE void place_late(waitq *worklist) {
   assert(worklist);
   inc_irg_visited(current_ir_graph);
 
@@ -2002,15 +2075,15 @@ static INLINE void place_late(pdeq *worklist) {
   place_floats_late(get_irg_start_block(current_ir_graph), worklist);
 
   /* And now empty the worklist again... */
-  while (!pdeq_empty(worklist)) {
-    ir_node *n = pdeq_getl(worklist);
+  while (!waitq_empty(worklist)) {
+    ir_node *n = waitq_get(worklist);
     if (irn_not_visited(n))
       place_floats_late(n, worklist);
   }
 }
 
 void place_code(ir_graph *irg) {
-  pdeq *worklist;
+  waitq *worklist;
   ir_graph *rem = current_ir_graph;
 
   current_ir_graph = irg;
@@ -2028,7 +2101,7 @@ void place_code(ir_graph *irg) {
 
   /* Place all floating nodes as early as possible. This guarantees
      a legal code placement. */
-  worklist = new_pdeq();
+  worklist = new_waitq();
   place_early(worklist);
 
   /* place_early() invalidates the outs, place_late needs them. */
@@ -2040,7 +2113,7 @@ void place_code(ir_graph *irg) {
 
   set_irg_outs_inconsistent(current_ir_graph);
   set_irg_loopinfo_inconsistent(current_ir_graph);
-  del_pdeq(worklist);
+  del_waitq(worklist);
   current_ir_graph = rem;
 }
 
@@ -2057,41 +2130,43 @@ static void walk_critical_cf_edges(ir_node *n, void *env) {
   int arity, i;
   ir_node *pre, *block, *jmp;
   int *changed = env;
+  ir_graph *irg = get_irn_irg(n);
 
   /* Block has multiple predecessors */
-  if (is_Block(n) && (get_irn_arity(n) > 1)) {
-    if (n == get_irg_end_block(current_ir_graph))
+  arity = get_irn_arity(n);
+  if (arity > 1) {
+    if (n == get_irg_end_block(irg))
       return;  /*  No use to add a block here.      */
 
-    arity = get_irn_arity(n);
-    for (i=0; i<arity; i++) {
+    for (i = 0; i < arity; ++i) {
+         const ir_op *cfop;
+
       pre = get_irn_n(n, i);
-      /* Predecessor has multiple successors. Insert new control flow edge. */
-      if (op_Raise != get_irn_op(skip_Proj(pre))) {
+      cfop = get_irn_op(skip_Proj(pre));
+      /* Predecessor has multiple successors. Insert new control flow edge but
+         ignore exception edges. */
+      if (! is_op_fragile(cfop) && is_op_forking(cfop)) {
         /* set predecessor of new block */
-        block = new_Block(1, &pre);
+        block = new_r_Block(irg, 1, &pre);
         /* insert new jmp node to new block */
-        set_cur_block(block);
-        jmp = new_Jmp();
-        set_cur_block(n);
+        jmp = new_r_Jmp(irg, block);
         /* set successor of new block */
         set_irn_n(n, i, jmp);
         *changed = 1;
       } /* predecessor has multiple successors */
     } /* for all predecessors */
-  } /* n is a block */
+  } /* n is a multi-entry block */
 }
 
 void remove_critical_cf_edges(ir_graph *irg) {
   int changed = 0;
-  irg_walk_graph(irg, NULL, walk_critical_cf_edges, &changed);
 
+  irg_block_walk_graph(irg, NULL, walk_critical_cf_edges, &changed);
   if (changed) {
     /* control flow changed */
     set_irg_outs_inconsistent(irg);
     set_irg_extblk_inconsistent(irg);
-    set_irg_doms_inconsistent(current_ir_graph);
-    set_irg_loopinfo_inconsistent(current_ir_graph);
+    set_irg_doms_inconsistent(irg);
+    set_irg_loopinfo_inconsistent(irg);
   }
-
 }