- revert big benefice for irg_inline_forced methods,
[libfirm] / ir / opt / gvn_pre.c
index eadf005..bd2c660 100644 (file)
@@ -39,6 +39,8 @@
 #include "valueset.h"
 #include "irnodemap.h"
 #include "irnodeset.h"
+#include "iredges.h"
+#include "iropt_dbg.h"
 #include "debug.h"
 
 #include "irgraph_t.h"
@@ -65,6 +67,7 @@ typedef struct elim_pair {
        ir_node *old_node;      /**< The old node that will be replaced. */
        ir_node *new_node;      /**< The new node. */
        struct elim_pair *next; /**< Links all entries in a list. */
+       int     reason;         /**< The reason for the replacement. */
 } elim_pair;
 
 /** The environment for the GVN-PRE algorithm */
@@ -72,9 +75,9 @@ typedef struct pre_env {
        struct obstack *obst;   /**< The obstack to allocate on. */
        ir_node *start_block;   /**< The start block of the current graph. */
        ir_node *end_block;     /**< The end block of the current graph */
-       pset *trans_set;        /**< The set of all translated values. */
        block_info *list;       /**< Links all block info entires for easier recovery. */
        elim_pair *pairs;       /**< A list of node pairs that must be eliminated. */
+       unsigned last_idx;      /**< last node index of "old" nodes, all higher indexes are newly created once. */
        char changes;           /**< Non-zero, if calculation of Antic_in has changed. */
        char first_iter;        /**< non-zero for first iteration */
 } pre_env;
@@ -102,17 +105,36 @@ static void value_union(ir_valueset_t *dst, ir_valueset_t *src)
 /* ----------  Functions for Values ---------- */
 
 /**
- * Add a node node representing the value value to the set.
+ * Add a node e representing the value v to the set.
+ *
+ * @param e  a node representing an expression
+ * @param v  a node representing a value
+ *
+ * @return the final value for the expression e
  */
 static ir_node *add(ir_node *e, ir_node *v)
 {
+       if (is_Proj(v)) {
+               ir_node *pred = get_Proj_pred(v);
+               ir_node *v_pred = identify_remember(value_table, pred);
+
+               if (v_pred != pred) {
+                       /* must create a new value here */
+                       v = new_r_Proj(current_ir_graph, get_nodes_block(v_pred), v_pred, get_irn_mode(v), get_Proj_proj(v));
+               }
+       }
        v = identify_remember(value_table, v);
        ir_nodemap_insert(&value_map, e, v);
        return v;
-}
+}  /* add */
 
 /**
  * Lookup a value in a value set.
+ *
+ * @param e  a node representing an expression
+ *
+ * @return a node representing the value or NULL if
+ *         the given expression is not available
  */
 static ir_node *lookup(ir_node *e)
 {
@@ -120,32 +142,22 @@ static ir_node *lookup(ir_node *e)
        if (value != NULL)
                return identify_remember(value_table, value);
        return NULL;
-}
-
-/**
- * Add or replace a value in a set by an node computing the same
- * value in a dominator block.
- */
-static ir_node *lookup_or_add(ir_node *e)
-{
-       ir_node *x = lookup(e);
-
-       if (x == NULL) {
-               x = add(e, e);
-       }
-       return x;
-}
-
+}  /* lookup */
 
 /**
- * Return the block info of a block
+ * Return the block info of a block.
+ *
+ * @param block  the block
  */
 static block_info *get_block_info(ir_node *block) {
        return get_irn_link(block);
-}
+}  /* get_block_info */
 
 /**
- * allocate a block info
+ * Allocate a block info for a block.
+ *
+ * @param block   the block
+ * @param env     the environment
  */
 static void alloc_blk_info(ir_node *block, pre_env *env) {
        block_info *info = obstack_alloc(env->obst, sizeof(*info));
@@ -159,49 +171,37 @@ static void alloc_blk_info(ir_node *block, pre_env *env) {
        info->not_found = 0;
        info->next      = env->list;
        env->list       = info;
-}
+}  /* alloc_blk_info */
 
 /**
  * Returns non-zero if a node is movable and a possible candidate for PRE.
+ *
+ * @param n  the node
  */
 static int is_nice_value(ir_node *n) {
        ir_mode *mode;
 
        while (is_Proj(n))
                n = get_Proj_pred(n);
-       mode = get_irn_mode(n);
-       /*
-        * FIXME: For now, we cannot handle Div/even if it's movable.
-        * That should be fixed.
-        */
-       if (!mode_is_data(mode))
+       if (get_irn_pinned(n) == op_pin_state_pinned)
                return 0;
-       return (get_irn_pinned(n) != op_pin_state_pinned);
+       mode = get_irn_mode(n);
+       if (!mode_is_data(mode)) {
+               if (! is_Div(n) && ! is_Mod(n) && ! is_DivMod(n))
+                       return 0;
+               if (! is_NoMem(get_fragile_op_mem(n)))
+                       return 0;
+       }
+       return 1;
 }  /* is_nice_value */
 
 #ifdef DEBUG_libfirm
-/**
- * Dump a ir_nodeset_t set.
- */
-static void dump_node_set(ir_nodeset_t *set, char *txt, ir_node *block)
-{
-       ir_nodeset_iterator_t iter;
-       ir_node *n;
-       int i;
-
-       DB((dbg, LEVEL_2, "%s(%+F) = {\n", txt, block));
-       i = 0;
-       foreach_ir_nodeset(set, n, iter) {
-               if ((i & 3) == 3)
-                       DB((dbg, LEVEL_2, "\n"));
-               DB((dbg, LEVEL_2, " %+F,", n));
-               ++i;
-       }
-       DB((dbg, LEVEL_2, "\n}\n"));
-}  /* dump_node_set */
-
 /**
  * Dump a value set.
+ *
+ * @param set    the set to dump
+ * @param txt    a text to describe the set
+ * @param block  the owner block of the set
  */
 static void dump_value_set(ir_valueset_t *set, char *txt, ir_node *block) {
        ir_valueset_iterator_t iter;
@@ -222,9 +222,7 @@ static void dump_value_set(ir_valueset_t *set, char *txt, ir_node *block) {
        DB((dbg, LEVEL_2, "\n}\n"));
 }  /* dump_value_set */
 
-
 #else
-#define dump_node_set(set, txt, block)
 #define dump_value_set(set, txt, block)
 #endif /* DEBUG_libfirm */
 
@@ -250,6 +248,11 @@ static void topo_walker(ir_node *irn, void *ctx) {
        if (! is_nice_value(irn) || is_irn_constlike(irn))
                return;
 
+       /* Do not put mode_T nodes info the sets, or PhiT will be created
+         (which are not allowed in Firm). Instead, put the Proj's here only. */
+       if (get_irn_mode(irn) == mode_T)
+               return;
+
        /* place this node into the set of possible nodes of its block */
        block = get_nodes_block(irn);
        info  = get_block_info(block);
@@ -266,6 +269,9 @@ static void topo_walker(ir_node *irn, void *ctx) {
  * Precondition:
  *  This function must be called in the top-down dominance order:
  *  Then, it computes Leader(Nodes(block)) instead of Nodes(block) !
+ *
+ * @param block   the block
+ * @param ctx     walker context
  */
 static void compute_avail_top_down(ir_node *block, void *ctx)
 {
@@ -295,10 +301,13 @@ static void compute_avail_top_down(ir_node *block, void *ctx)
        value_union(info->avail_out, info->exp_gen);
 
        dump_value_set(info->avail_out, "Avail_out", block);
-}
+}  /* compute_avail_top_down */
 
 /**
  * check if a node n is clean in block block.
+ *
+ * @param n      the node
+ * @param block  the block
  */
 static int _is_clean(ir_node *n, ir_node *block)
 {
@@ -323,22 +332,31 @@ static int _is_clean(ir_node *n, ir_node *block)
 bad:
        mark_irn_visited(n);
        return 0;
-}
+}  /* _is_clean */
 
 /**
  * check if a node n is clean.
+ *
+ * @param n  the node to check
  */
 static int is_clean(ir_node *n) {
        int res = _is_clean(n, get_nodes_block(n));
        return res;
-}
+}  /* is_clean */
 
 /**
- * Implements phi_translate.
+ * Implements phi_translate. Translate a expression above a Phi.
+ *
+ * @param node   the node
+ * @param block  the block in which the node is translated
+ * @param pos    the input number of the destination block
+ * @param env    the environment
+ *
+ * @return a node representing the translated value
  */
 static ir_node *phi_translate(ir_node *node, ir_node *block, int pos, pre_env *env)
 {
-       ir_node *nn, *res;
+       ir_node *nn;
        int i, arity;
        struct obstack *old;
 
@@ -356,7 +374,6 @@ static ir_node *phi_translate(ir_node *node, ir_node *block, int pos, pre_env *e
        /* check if the node has at least one Phi predecessor */
        for (i = 0; i < arity; ++i) {
                ir_node *pred    = get_irn_intra_n(node, i);
-               ir_node *pred_bl = get_nodes_block(pred);
                ir_node *leader  = lookup(pred);
 
                leader = leader != NULL ? leader : pred;
@@ -388,7 +405,6 @@ static ir_node *phi_translate(ir_node *node, ir_node *block, int pos, pre_env *e
        set_nodes_block(nn, get_nodes_block(node));
        for (i = 0; i < arity; ++i) {
                ir_node *pred    = get_irn_intra_n(node, i);
-               ir_node *pred_bl = get_irn_intra_n(pred, -1);
                ir_node *leader  = lookup(pred);
 
                leader = leader != NULL ? leader : pred;
@@ -398,18 +414,14 @@ static ir_node *phi_translate(ir_node *node, ir_node *block, int pos, pre_env *e
                        set_irn_n(nn, i, leader);
        }
        current_ir_graph->obst = old;
-
-       res = identify_remember(env->trans_set, nn);
-       if (nn != res)
-               obstack_free(env->obst, nn);
-       else {
-               DB((dbg, LEVEL_2, "--> Translate %+F in <%+F,%d> into %+F\n", node, block, pos, res));
-       }
-       return res;
+       return nn;
 }  /* phi_translate */
 
 /**
- * computes Antic_in(block):
+ * Block-walker, computes Antic_in(block).
+ *
+ * @param block  the block
+ * @param ctx    the walker environment
  */
 static void compute_antic(ir_node *block, void *ctx) {
        pre_env    *env = ctx;
@@ -427,8 +439,21 @@ static void compute_antic(ir_node *block, void *ctx) {
 
        /* the end block has no successor */
        if (block != env->end_block) {
-               int n_succ = get_Block_n_cfg_outs(block);
+               int n_succ;
+
+               /*
+                * This step puts all generated expression from the current
+                * current block into Antic_in.
+                * It is enough to do this in the first iteration only, because
+                * the set info->exp_gen is not changed anymore.
+                */
+               if (env->first_iter) {
+                       foreach_valueset(info->exp_gen, value, expr, iter) {
+                               ir_valueset_insert(info->antic_in, value, expr);
+                       }
+               }
 
+               n_succ = get_Block_n_cfg_outs(block);
                if (n_succ == 1) {
                        int i, pos = -1;
 
@@ -458,18 +483,6 @@ static void compute_antic(ir_node *block, void *ctx) {
 
                        assert(n_succ > 1);
 
-                       /*
-                        * This step puts all generated expression from the current
-                        * current block into Antic_in.
-                        * It is enough to do this in the first iteration only, because
-                        * the set info->exp_gen is not changed anymore.
-                        */
-                       if (env->first_iter) {
-                               foreach_valueset(info->exp_gen, value, expr, iter) {
-                                       ir_valueset_insert(info->antic_in, value, expr);
-                               }
-                       }
-
                        /* Select a successor to compute the disjoint of all Nodes
                           sets, it might be useful to select the block with the
                           smallest number of nodes.  For simplicity we choose the
@@ -515,6 +528,9 @@ static void compute_antic(ir_node *block, void *ctx) {
  *     2c. Insert a new Phi merging the values of the predecessors.
  *     2d. Insert the new Phi, and the new expressions, into the
  *         NEW_SETS set.
+ *
+ * @param block  the block
+ * @param ctx    the walker environment
  */
 static void insert_nodes(ir_node *block, void *ctx)
 {
@@ -630,6 +646,22 @@ static void insert_nodes(ir_node *block, void *ctx)
                                        ir_node *e_prime = pred_info->avail;
                                        ir_node *nn;
                                        if (!is_Phi(e_prime)) {
+                                               ir_node *proj_pred = NULL;
+                                               if (is_Proj(e_prime)) {
+                                                       ir_node *pred = get_Proj_pred(e_prime);
+                                                       mode = get_irn_mode(pred);
+                                                       nn = new_ir_node(
+                                                               get_irn_dbg_info(pred),
+                                                               current_ir_graph, pred_blk,
+                                                               get_irn_op(pred),
+                                                               mode,
+                                                               get_irn_arity(pred),
+                                                               get_irn_in(pred) + 1);
+                                                       copy_node_attr(pred, nn);
+
+                                                       DB((dbg, LEVEL_2, "New node %+F in block %+F created\n", nn, pred_blk));
+                                                       proj_pred = nn;
+                                               }
                                                mode = get_irn_mode(e_prime);
                                                nn = new_ir_node(
                                                        get_irn_dbg_info(e_prime),
@@ -639,6 +671,9 @@ static void insert_nodes(ir_node *block, void *ctx)
                                                        get_irn_arity(e_prime),
                                                        get_irn_in(e_prime) + 1);
                                                copy_node_attr(e_prime, nn);
+                                               if (proj_pred != NULL) {
+                                                       set_Proj_pred(nn, proj_pred);
+                                               }
 
                                                DB((dbg, LEVEL_2, "New node %+F in block %+F created\n", nn, pred_blk));
                                                ir_valueset_insert(pred_info->avail_out, add(nn, lookup(expr)), nn);
@@ -653,15 +688,24 @@ static void insert_nodes(ir_node *block, void *ctx)
                        ir_valueset_insert(curr_info->new_set, value, phi);
                        free(in);
                        DB((dbg, LEVEL_2, "New %+F for redundant %+F created\n", phi, expr));
+                       ir_valueset_remove_iterator(curr_info->antic_in, &iter);
                        env->changes |= 1;
                }  /* if */
   }  /* node_set_foreach */
 }  /* insert_nodes */
 
 /**
- * Walker, change nodes by its value if different
+ * Walker, change nodes by its value if different.
+ *
+ * We cannot do the changes right here, as this would change
+ * the hash values of the nodes in the avail_out set!
+ *
+ * @param irn  the node
+ * @param ctx  the walker environment
  */
 static void eliminate(ir_node *irn, void *ctx) {
+       pre_env *env = ctx;
+
        if (is_no_Block(irn)) {
                ir_node *block = get_nodes_block(irn);
                block_info *bl = get_block_info(block);
@@ -671,13 +715,56 @@ static void eliminate(ir_node *irn, void *ctx) {
                        ir_node *expr = ir_valueset_lookup(bl->avail_out, value);
 
                        if (expr != NULL && expr != irn) {
-                               DB((dbg, LEVEL_2, "Replacing %+F by %+F\n", irn, expr));
-                               exchange(irn, expr);
+                               elim_pair *p = obstack_alloc(env->obst, sizeof(*p));
+
+                               p->old_node = irn;
+                               p->new_node = expr;
+                               p->next     = env->pairs;
+                               p->reason   = get_irn_idx(expr) >= env->last_idx ? FS_OPT_GVN_PARTLY : FS_OPT_GVN_FULLY;
+                               env->pairs  = p;
                        }
                }
        }
 }  /* eliminate */
 
+/**
+ * Do all the recorded changes and optimize
+ * newly created Phi's.
+ *
+ * @param pairs  list of elimination pairs
+ */
+static void eliminate_nodes(elim_pair *pairs) {
+       elim_pair *p;
+
+       for (p = pairs; p != NULL; p = p->next) {
+               DB((dbg, LEVEL_2, "Replacing %+F by %+F\n", p->old_node, p->new_node));
+               /*
+                * PRE tends to create Phi(self, self, ... , x, self, self, ...)
+                * which we can optimize here
+                */
+               if (is_Phi(p->new_node)) {
+                       int i;
+                       ir_node *res = NULL;
+
+                       for (i = get_irn_intra_arity(p->new_node) - 1; i >= 0; --i) {
+                               ir_node *pred = get_irn_n(p->new_node, i);
+
+                               if (pred != p->old_node) {
+                                       if (res) {
+                                               res = NULL;
+                                               break;
+                                       }
+                                       res = pred;
+                               }
+                       }
+                       if (res)
+                               p->new_node = res;
+               }
+               DBG_OPT_GVN_PRE(p->old_node, p->new_node, p->reason);
+               exchange(p->old_node, p->new_node);
+       }
+}  /* eliminate_nodes */
+
 /*
  * Argh: Endless loops cause problems, because the
  * insert algorithm did not terminate. We get translated nodes that
@@ -688,15 +775,15 @@ static void eliminate(ir_node *irn, void *ctx) {
  */
 void do_gvn_pre(ir_graph *irg)
 {
-       struct obstack obst;
-       pre_env a_env;
+       struct obstack       obst;
+       pre_env              a_env;
        optimization_state_t state;
-       block_info *bl_info;
-       unsigned antic_iter, insert_iter;
+       block_info           *bl_info;
+       unsigned             antic_iter, insert_iter;
+       ir_node              *value, *expr;
 
        /* register a debug mask */
        FIRM_DBG_REGISTER(dbg, "firm.opt.gvn_pre");
-       firm_dbg_set_mask(dbg, SET_LEVEL_2);
 
        /* edges will crash if enabled due to our allocate on other obstack trick */
        edges_deactivate(irg);
@@ -706,7 +793,6 @@ void do_gvn_pre(ir_graph *irg)
 
        obstack_init(&obst);
        a_env.obst        = &obst;
-       a_env.trans_set   = value_table;
        a_env.list        = NULL;
        a_env.start_block = get_irg_start_block(irg);
        a_env.end_block   = get_irg_end_block(irg);
@@ -737,6 +823,17 @@ void do_gvn_pre(ir_graph *irg)
        /* allocate block info for all blocks */
        irg_walk_blkwise_graph(irg, NULL, topo_walker, &a_env);
 
+       /* clean the exp_gen set. Doing this here saves the cleanup in the iteration. */
+       inc_irg_visited(irg);
+       for (bl_info = a_env.list; bl_info != NULL; bl_info = bl_info->next) {
+               ir_valueset_iterator_t iter;
+
+               foreach_valueset(bl_info->exp_gen, value, expr, iter) {
+                       if (!is_clean(expr))
+                               ir_valueset_remove_iterator(bl_info->exp_gen, &iter);
+               }
+       }
+
        /* compute the available value sets for all blocks */
        dom_tree_walk_irg(irg, compute_avail_top_down, NULL, &a_env);
 
@@ -749,7 +846,6 @@ void do_gvn_pre(ir_graph *irg)
        do {
                DB((dbg, LEVEL_1, "Antic_in Iteration %d starts ...\n", ++antic_iter));
                a_env.changes = 0;
-               //irg_block_walk_graph(irg, compute_antic, NULL, &a_env);
                postdom_tree_walk_irg(irg, compute_antic, NULL, &a_env);
                a_env.first_iter = 0;
                DB((dbg, LEVEL_1, "------------------------\n"));
@@ -757,6 +853,7 @@ void do_gvn_pre(ir_graph *irg)
 
        /* compute redundant expressions */
        insert_iter = 0;
+       a_env.last_idx = get_irg_last_idx(irg);
        do {
                DB((dbg, LEVEL_1, "Insert Iteration %d starts ...\n", ++insert_iter));
                a_env.changes = 0;
@@ -766,8 +863,7 @@ void do_gvn_pre(ir_graph *irg)
 
        /* last step: eliminate nodes */
        irg_walk_graph(irg, NULL, eliminate, &a_env);
-
-       dump_ir_block_graph(irg, "-gvn");
+       eliminate_nodes(a_env.pairs);
 
        /* clean up: delete all sets */
        for (bl_info = a_env.list; bl_info != NULL; bl_info = bl_info->next) {
@@ -777,7 +873,6 @@ void do_gvn_pre(ir_graph *irg)
                if (bl_info->new_set)
                        ir_valueset_del(bl_info->new_set);
        }
-       del_identities(a_env.trans_set);
        del_identities(value_table);
        ir_nodemap_destroy(&value_map);
        obstack_free(&obst, NULL);
@@ -792,5 +887,4 @@ void do_gvn_pre(ir_graph *irg)
                set_irg_loopinfo_inconsistent(irg);
 
        }
-       dump_ir_block_graph(irg, "-gvn");
 }  /* do_gvn_pre */