Undo r16291.
[libfirm] / ir / opt / opt_osr.c
index 0570032..27d34e9 100644 (file)
@@ -1,27 +1,37 @@
+/*
+ * Copyright (C) 1995-2007 University of Karlsruhe.  All right reserved.
+ *
+ * This file is part of libFirm.
+ *
+ * This file may be distributed and/or modified under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation and appearing in the file LICENSE.GPL included in the
+ * packaging of this file.
+ *
+ * Licensees holding valid libFirm Professional Edition licenses may use
+ * this file in accordance with the libFirm Commercial License.
+ * Agreement provided with the Software.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
 /**
- * Project:     libFIRM
- * File name:   ir/opt/opt_osr.
- * Purpose:     Operator Strength Reduction,
- *              Keith D. Cooper, L. Taylor Simpson, Christopher A. Vick
- * Author:      Michael Beck
- * Modified by:
- * Created:     12.5.2006
- * CVS-ID:      $Id$
- * Copyright:   (c) 2006 Universität Karlsruhe
- * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
+ * @file
+ * @brief   Operator Strength Reduction.
+ * @date    12.5.2006
+ * @author  Michael Beck
+ * @version $Id$
+ * @summary
+ *  Implementation of the Operator Strength Reduction algorithm
+ *  by Keith D. Cooper, L. Taylor Simpson, Christopher A. Vick.
  */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
-#include "opt_osr.h"
+#include "iroptimize.h"
 #include "irgraph.h"
 #include "ircons.h"
 #include "irop_t.h"
 #include "tv.h"
 #include "hashptr.h"
 #include "irtools.h"
+#include "irloop_t.h"
 #include "array.h"
 #include "firmstat.h"
+#include "xmalloc.h"
 
 /** The debug handle. */
 DEBUG_ONLY(static firm_dbg_module_t *dbg;)
@@ -71,27 +83,29 @@ typedef struct iv_env {
        unsigned replaced;      /**< number of replaced ops */
        unsigned lftr_replaced; /**< number of applied linear function test replacements */
        unsigned flags;         /**< additional flags */
+       /** Function called to process a SCC. */
+       void (*process_scc)(scc *pscc, struct iv_env *env);
 } iv_env;
 
 /**
  * An entry in the (op, node, node) -> node map.
  */
-typedef struct quad_t {
-       opcode  code;  /**< the opcode of the reduced operation */
-       ir_node *op1;  /**< the first operand the reduced operation */
-       ir_node *op2;  /**< the second operand of the reduced operation */
+typedef struct quadruple_t {
+       ir_opcode code;  /**< the opcode of the reduced operation */
+       ir_node   *op1;  /**< the first operand the reduced operation */
+       ir_node   *op2;  /**< the second operand of the reduced operation */
 
-       ir_node *res; /**< the reduced operation */
-} quad_t;
+       ir_node   *res; /**< the reduced operation */
+} quadruple_t;
 
 /**
  * A LFTR edge.
  */
 typedef struct LFTR_edge {
-       ir_node *src;   /**< the source node */
-       ir_node *dst;   /**< the destination node */
-       opcode  code;   /**< the opcode that must be applied */
-       ir_node *rc;    /**< the region const that must be applied */
+       ir_node   *src;   /**< the source node */
+       ir_node   *dst;   /**< the destination node */
+       ir_opcode code;   /**< the opcode that must be applied */
+       ir_node   *rc;    /**< the region const that must be applied */
 } LFTR_edge;
 
 /* forward */
@@ -103,10 +117,12 @@ static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env);
 static int LFTR_cmp(const void *e1, const void *e2, size_t size) {
        const LFTR_edge *l1 = e1;
        const LFTR_edge *l2 = e2;
+       (void) size;
 
        return l1->src != l2->src;
 }
 
+#if 0
 /**
  * Find a LFTR edge.
  */
@@ -117,11 +133,12 @@ static LFTR_edge *LFTR_find(ir_node *src, iv_env *env) {
 
        return set_find(env->lftr_edges, &key, sizeof(key), HASH_PTR(src));
 }
+#endif
 
 /**
  * Add a LFTR edge.
  */
-static void LFTR_add(ir_node *src, ir_node *dst, opcode code, ir_node *rc, iv_env *env) {
+static void LFTR_add(ir_node *src, ir_node *dst, ir_opcode code, ir_node *rc, iv_env *env) {
        LFTR_edge key;
 
        key.src  = src;
@@ -180,8 +197,9 @@ static int is_rc(ir_node *irn, ir_node *header_block) {
  * Set compare function for the quad set.
  */
 static int quad_cmp(const void *e1, const void *e2, size_t size) {
-       const quad_t *c1 = e1;
-       const quad_t *c2 = e2;
+       const quadruple_t *c1 = e1;
+       const quadruple_t *c2 = e2;
+       (void) size;
 
        return c1->code != c2->code || c1->op1 != c2->op1 || c1->op2 != c2->op2;
 }
@@ -196,8 +214,8 @@ static int quad_cmp(const void *e1, const void *e2, size_t size) {
  *
  * @return the already reduced node or NULL if this operation is not yet reduced
  */
-static ir_node *search(opcode code, ir_node *op1, ir_node *op2, iv_env *env) {
-       quad_t key, *entry;
+static ir_node *search(ir_opcode code, ir_node *op1, ir_node *op2, iv_env *env) {
+       quadruple_t key, *entry;
 
        key.code = code;
        key.op1 = op1;
@@ -219,8 +237,8 @@ static ir_node *search(opcode code, ir_node *op1, ir_node *op2, iv_env *env) {
  * @param result  the result of the reduced operation
  * @param env     the environment
  */
-static void add(opcode code, ir_node *op1, ir_node *op2, ir_node *result, iv_env *env) {
-       quad_t key;
+static void add(ir_opcode code, ir_node *op1, ir_node *op2, ir_node *result, iv_env *env) {
+       quadruple_t key;
 
        key.code = code;
        key.op1  = op1;
@@ -260,7 +278,7 @@ static ir_node *find_location(ir_node *block1, ir_node *block2) {
  *
  * @return the newly created node
  */
-static ir_node *do_apply(opcode code, dbg_info *db, ir_node *op1, ir_node *op2, ir_mode *mode) {
+static ir_node *do_apply(ir_opcode code, dbg_info *db, ir_node *op1, ir_node *op2, ir_mode *mode) {
        ir_graph *irg = current_ir_graph;
        ir_node *result;
        ir_node *block = find_location(get_nodes_block(op1), get_nodes_block(op2));
@@ -289,12 +307,12 @@ static ir_node *do_apply(opcode code, dbg_info *db, ir_node *op1, ir_node *op2,
  *               the opcode, debug-info and mode of a newly created one
  * @param op1    the first operand
  * @param op2    the second operand
- * @param env     the environment
+ * @param env    the environment
  *
  * @return the newly created node
  */
 static ir_node *apply(ir_node *orig, ir_node *op1, ir_node *op2, iv_env *env) {
-       opcode code = get_irn_opcode(orig);
+       ir_opcode code = get_irn_opcode(orig);
        ir_node *result = search(code, op1, op2, env);
 
        if (! result) {
@@ -310,8 +328,7 @@ static ir_node *apply(ir_node *orig, ir_node *op1, ir_node *op2, iv_env *env) {
                }
                else {
                        result = do_apply(code, db, op1, op2, get_irn_mode(orig));
-                       get_irn_ne(result, env)->header = NULL;
-               }
+                       get_irn_ne(result, env)->header = NULL;         }
        }
        return result;
 }
@@ -328,7 +345,7 @@ static ir_node *apply(ir_node *orig, ir_node *op1, ir_node *op2, iv_env *env) {
  * @return the reduced node
  */
 static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env) {
-       opcode code = get_irn_opcode(orig);
+       ir_opcode code = get_irn_opcode(orig);
        ir_node *result = search(code, iv, rc, env);
 
        if (! result) {
@@ -337,11 +354,12 @@ static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env) {
                ir_mode *mode = get_irn_mode(orig);
 
                result = exact_copy(iv);
-               if (mode_is_reference(mode)) {
-                       /* bad case: we replace a reference mode calculation.
-                          assure that the new IV will be a reference one */
-                       set_irn_mode(result, mode);
-               }
+
+               /* Beware: we must always create a new induction variable with the same mode
+                  as the node we are replacing. Especially this means the mode might be changed
+                  from P to I and back. This is always possible, because we have only Phi, Add
+                  and Sub nodes. */
+               set_irn_mode(result, mode);
                add(code, iv, rc, result, env);
                DB((dbg, LEVEL_3, "   Created new %+F for %+F (%s %+F)\n", result, iv,
                        get_irn_opname(orig), rc));
@@ -363,11 +381,8 @@ static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env) {
                        else if (is_Phi(result))
                                o = apply(orig, o, rc, env);
                        else {
-                               switch (code) {
-                               case iro_Mul:
+                               if (code == iro_Mul)
                                        o = apply(orig, o, rc, env);
-                                       break;
-                               }
                        }
                        set_irn_n(result, i, o);
                }
@@ -387,21 +402,28 @@ static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env) {
  * @param rc    the region constant
  * @param env   the environment
  */
-static void replace(ir_node *irn, ir_node *iv, ir_node *rc, iv_env *env) {
+static int replace(ir_node *irn, ir_node *iv, ir_node *rc, iv_env *env) {
        ir_node *result;
-
-       DB((dbg, LEVEL_2, "  Replacing %+F\n", irn));
-
-       result = reduce(irn, iv, rc, env);
-       if (result != irn) {
-               node_entry *e, *iv_e;
-
-               hook_strength_red(current_ir_graph, irn);
-               exchange(irn, result);
-               e = get_irn_ne(result, env);
-               iv_e = get_irn_ne(iv, env);
-               e->header = iv_e->header;
+       ir_loop *iv_loop  = get_irn_loop(get_nodes_block(iv));
+       ir_loop *irn_loop = get_irn_loop(get_nodes_block(irn));
+
+       /* only replace nodes that are in the same (or deeper loops) */
+       if (get_loop_depth(irn_loop) >= get_loop_depth(iv_loop)) {
+               DB((dbg, LEVEL_2, "  Replacing %+F\n", irn));
+
+               result = reduce(irn, iv, rc, env);
+               if (result != irn) {
+                       node_entry *e, *iv_e;
+
+                       hook_strength_red(current_ir_graph, irn);
+                       exchange(irn, result);
+                       e = get_irn_ne(result, env);
+                       iv_e = get_irn_ne(iv, env);
+                       e->header = iv_e->header;
+               }
+               return 1;
        }
+       return 0;
 }
 
 /**
@@ -413,10 +435,10 @@ static void replace(ir_node *irn, ir_node *iv, ir_node *rc, iv_env *env) {
  * @return non-zero if irn should be Replace'd
  */
 static int check_replace(ir_node *irn, iv_env *env) {
-       ir_node *left, *right, *iv, *rc;
-       ir_op   *op  = get_irn_op(irn);
-       opcode  code = get_op_code(op);
-       ir_node *liv, *riv;
+       ir_node   *left, *right, *iv, *rc;
+       ir_op     *op  = get_irn_op(irn);
+       ir_opcode code = get_op_code(op);
+       ir_node   *liv, *riv;
 
        switch (code) {
        case iro_Mul:
@@ -438,11 +460,26 @@ static int check_replace(ir_node *irn, iv_env *env) {
                }
 
                if (iv) {
-                       replace(irn, iv, rc, env);
-                       ++env->replaced;
-                       return 1;
+                       if (code == iro_Mul && env->flags & osr_flag_ignore_x86_shift) {
+                               if (is_Const(rc)) {
+                                       tarval *tv = get_Const_tarval(rc);
+
+                                       if (tarval_is_long(tv)) {
+                                               long value = get_tarval_long(tv);
+
+                                               if (value == 2 || value == 4 || value == 8) {
+                                                       /* do not reduce multiplications by 2, 4, 8 */
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+
+                       return replace(irn, iv, rc, env);
                }
                break;
+       default:
+               break;
        }
        return 0;
 }
@@ -455,8 +492,9 @@ static int check_replace(ir_node *irn, iv_env *env) {
  */
 static void classify_iv(scc *pscc, iv_env *env) {
        ir_node *irn, *next, *header = NULL;
-       node_entry *h, *b;
-       int j;
+       node_entry *b, *h = NULL;
+       int j, only_phi, num_outside;
+       ir_node *out_rc;
 
        /* find the header block for this scc */
        for (irn = pscc->head; irn; irn = next) {
@@ -479,6 +517,9 @@ static void classify_iv(scc *pscc, iv_env *env) {
        }
 
        /* check if this scc contains only Phi, Add or Sub nodes */
+       only_phi    = 1;
+       num_outside = 0;
+       out_rc      = NULL;
        for (irn = pscc->head; irn; irn = next) {
                node_entry *e = get_irn_ne(irn, env);
 
@@ -486,6 +527,8 @@ static void classify_iv(scc *pscc, iv_env *env) {
                switch (get_irn_opcode(irn)) {
                case iro_Add:
                case iro_Sub:
+                       only_phi = 0;
+                       /* fall through */
                case iro_Phi:
                        for (j = get_irn_arity(irn) - 1; j >= 0; --j) {
                                ir_node *pred  = get_irn_n(irn, j);
@@ -497,6 +540,12 @@ static void classify_iv(scc *pscc, iv_env *env) {
                                                /* not an induction variable */
                                                goto fail;
                                        }
+                                       if (! out_rc) {
+                                               out_rc = pred;
+                                               ++num_outside;
+                                       } else if (out_rc != pred) {
+                                               ++num_outside;
+                                       }
                                }
                        }
                        break;
@@ -507,6 +556,19 @@ static void classify_iv(scc *pscc, iv_env *env) {
        }
        /* found an induction variable */
        DB((dbg, LEVEL_2, "  Found an induction variable:\n  "));
+       if (only_phi && num_outside == 1) {
+               /* a phi cycle with only one real predecessor can be collapsed */
+               DB((dbg, LEVEL_2, "  Found an USELESS Phi cycle:\n  "));
+
+               for (irn = pscc->head; irn; irn = next) {
+                       node_entry *e = get_irn_ne(irn, env);
+                       next = e->next;
+                       e->header = NULL;
+                       exchange(irn, out_rc);
+               }
+               ++env->replaced;
+               return;
+       }
 
        /* set the header for every node in this scc */
        for (irn = pscc->head; irn; irn = next) {
@@ -529,7 +591,7 @@ fail:
 }
 
 /**
- * Process a SCC.
+ * Process a SCC for the operator strength reduction.
  *
  * @param pscc  the SCC
  * @param env   the environment
@@ -557,12 +619,85 @@ static void process_scc(scc *pscc, iv_env *env) {
        if (e->next == NULL) {
                /* this SCC has only a single member */
                check_replace(head, env);
-       }
-       else {
+       } else {
                classify_iv(pscc, env);
        }
 }
 
+/**
+ * If an SCC is a Phi only cycle, remove it.
+ */
+static void remove_phi_cycle(scc *pscc, iv_env *env) {
+       ir_node *irn, *next;
+       int j;
+       ir_node *out_rc;
+
+       /* check if this scc contains only Phi, Add or Sub nodes */
+       out_rc      = NULL;
+       for (irn = pscc->head; irn; irn = next) {
+               node_entry *e = get_irn_ne(irn, env);
+
+               next = e->next;
+               if (! is_Phi(irn))
+                       return;
+
+               for (j = get_irn_arity(irn) - 1; j >= 0; --j) {
+                       ir_node *pred  = get_irn_n(irn, j);
+                       node_entry *pe = get_irn_ne(pred, env);
+
+                       if (pe->pscc != e->pscc) {
+                               /* not in the same SCC, must be the only input */
+                               if (! out_rc) {
+                                       out_rc = pred;
+                               } else if (out_rc != pred) {
+                                       return;
+                               }
+                       }
+               }
+       }
+       /* found a Phi cycle */
+       DB((dbg, LEVEL_2, "  Found an USELESS Phi cycle:\n  "));
+
+       for (irn = pscc->head; irn; irn = next) {
+               node_entry *e = get_irn_ne(irn, env);
+               next = e->next;
+               e->header = NULL;
+               exchange(irn, out_rc);
+       }
+       ++env->replaced;
+}
+
+/**
+ * Process a SCC for the Phi cycle removement.
+ *
+ * @param pscc  the SCC
+ * @param env   the environment
+ */
+static void process_phi_only_scc(scc *pscc, iv_env *env) {
+       ir_node *head = pscc->head;
+       node_entry *e = get_irn_link(head);
+
+#ifdef DEBUG_libfirm
+       {
+               ir_node *irn, *next;
+
+               DB((dbg, LEVEL_4, " SCC at %p:\n ", pscc));
+               for (irn = pscc->head; irn; irn = next) {
+                       node_entry *e = get_irn_link(irn);
+
+                       next = e->next;
+
+                       DB((dbg, LEVEL_4, " %+F,", irn));
+               }
+               DB((dbg, LEVEL_4, "\n"));
+       }
+#endif
+
+       if (e->next != NULL)
+               remove_phi_cycle(pscc, env);
+}
+
+
 /**
  * Push a node onto the stack.
  *
@@ -590,11 +725,11 @@ static void push(iv_env *env, ir_node *n) {
  */
 static ir_node *pop(iv_env *env)
 {
-  ir_node *n = env->stack[--env->tos];
-  node_entry *e = get_irn_ne(n, env);
+       ir_node *n = env->stack[--env->tos];
+       node_entry *e = get_irn_ne(n, env);
 
-  e->in_stack = 0;
-  return n;
+       e->in_stack = 0;
+       return n;
 }
 
 /**
@@ -658,7 +793,7 @@ static void dfs(ir_node *irn, iv_env *env)
                                pscc->head = x;
                        } while (x != irn);
 
-                       process_scc(pscc, env);
+                       env->process_scc(pscc, env);
                }
        }
 }
@@ -702,6 +837,7 @@ static void assign_po(ir_node *block, void *ctx) {
        e->POnum = env->POnum++;
 }
 
+#if 0
 /**
  * Follows the LFTR edges and return the last node in the chain.
  *
@@ -843,7 +979,7 @@ static void do_lftr(ir_node *cmp, void *ctx) {
                iv = left; rc = right;
 
                nright = applyEdges(iv, rc, env);
-               if (nright) {
+               if (nright && nright != rc) {
                        nleft = followEdges(iv, env);
                }
        }
@@ -851,7 +987,7 @@ static void do_lftr(ir_node *cmp, void *ctx) {
                iv = right; rc = left;
 
                nleft = applyEdges(iv, rc, env);
-               if (nleft) {
+               if (nleft && nleft != rc) {
                        nright = followEdges(iv, env);
                }
        }
@@ -873,6 +1009,7 @@ static void do_lftr(ir_node *cmp, void *ctx) {
 static void lftr(ir_graph *irg, iv_env *env) {
        irg_walk_graph(irg, NULL, do_lftr, env);
 }
+#endif
 
 /**
  * Pre-walker: set all node links to NULL and fix the
@@ -880,23 +1017,30 @@ static void lftr(ir_graph *irg, iv_env *env) {
  */
 static void clear_and_fix(ir_node *irn, void *env)
 {
+       (void) env;
        set_irn_link(irn, NULL);
 
        if (is_Proj(irn)) {
                ir_node *pred = get_Proj_pred(irn);
-               set_irn_n(irn, -1, get_irn_n(pred, -1));
+               set_nodes_block(irn, get_nodes_block(pred));
        }
 }
 
 /* Performs Operator Strength Reduction for the passed graph. */
 void opt_osr(ir_graph *irg, unsigned flags) {
-       iv_env env;
+       iv_env   env;
+       ir_graph *rem;
 
-       if (! get_opt_strength_red())
+       if (! get_opt_strength_red()) {
+               /* only kill Phi cycles  */
+               remove_phi_cycles(irg);
                return;
+       }
+
+       rem = current_ir_graph;
+       current_ir_graph = irg;
 
        FIRM_DBG_REGISTER(dbg, "firm.opt.osr");
-//     firm_dbg_set_mask(dbg, SET_LEVEL_3);
 
        DB((dbg, LEVEL_1, "Doing Operator Strength Reduction for %+F\n", irg));
 
@@ -910,6 +1054,7 @@ void opt_osr(ir_graph *irg, unsigned flags) {
        env.replaced      = 0;
        env.lftr_replaced = 0;
        env.flags         = flags;
+       env.process_scc   = process_scc;
 
        /* Clear all links and move Proj nodes into the
           the same block as it's predecessors.
@@ -929,11 +1074,9 @@ void opt_osr(ir_graph *irg, unsigned flags) {
 
        if (env.replaced) {
                /* try linear function test replacements */
-               lftr(irg, &env);
+               //lftr(irg, &env);
 
                set_irg_outs_inconsistent(irg);
-               set_irg_loopinfo_inconsistent(irg);
-
                DB((dbg, LEVEL_1, "Replacements: %u + %u (lftr)\n\n", env.replaced, env.lftr_replaced));
        }
 
@@ -941,4 +1084,56 @@ void opt_osr(ir_graph *irg, unsigned flags) {
        del_set(env.quad_map);
        DEL_ARR_F(env.stack);
        obstack_free(&env.obst, NULL);
+
+       current_ir_graph = rem;
+}
+
+/* Remove any Phi cycles with only one real input. */
+void remove_phi_cycles(ir_graph *irg) {
+       iv_env   env;
+       ir_graph *rem;
+
+       rem = current_ir_graph;
+       current_ir_graph = irg;
+
+       FIRM_DBG_REGISTER(dbg, "firm.opt.remove_phi");
+
+       DB((dbg, LEVEL_1, "Doing Phi cycle removement for %+F\n", irg));
+
+       obstack_init(&env.obst);
+       env.stack         = NEW_ARR_F(ir_node *, 128);
+       env.tos           = 0;
+       env.nextDFSnum    = 0;
+       env.POnum         = 0;
+       env.quad_map      = NULL;
+       env.lftr_edges    = NULL;
+       env.replaced      = 0;
+       env.lftr_replaced = 0;
+       env.flags         = 0;
+       env.process_scc   = process_phi_only_scc;
+
+       /* Clear all links and move Proj nodes into the
+          the same block as it's predecessors.
+          This can improve the placement of new nodes.
+        */
+       irg_walk_graph(irg, NULL, clear_and_fix, NULL);
+
+       /* we need dominance */
+       assure_irg_outs(irg);
+
+       /* calculate the post order number for blocks. */
+       irg_out_block_walk(get_irg_start_block(irg), NULL, assign_po, &env);
+
+       /* calculate the SCC's and drive OSR. */
+       do_dfs(irg, &env);
+
+       if (env.replaced) {
+               set_irg_outs_inconsistent(irg);
+                DB((dbg, LEVEL_1, "remove_phi_cycles: %u Cycles removed\n\n", env.replaced));
+       }
+
+       DEL_ARR_F(env.stack);
+       obstack_free(&env.obst, NULL);
+
+       current_ir_graph = rem;
 }