Undo r16291.
[libfirm] / ir / opt / opt_osr.c
index 6a54ad2..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, based on
- *              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"
@@ -40,6 +50,7 @@
 #include "irloop_t.h"
 #include "array.h"
 #include "firmstat.h"
+#include "xmalloc.h"
 
 /** The debug handle. */
 DEBUG_ONLY(static firm_dbg_module_t *dbg;)
@@ -72,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 */
@@ -104,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.
  */
@@ -118,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;
@@ -181,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;
 }
@@ -197,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;
@@ -220,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;
@@ -261,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));
@@ -290,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) {
@@ -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) {
@@ -338,8 +355,8 @@ static ir_node *reduce(ir_node *orig, ir_node *iv, ir_node *rc, iv_env *env) {
 
                result = exact_copy(iv);
 
-               /* Beware: we must always create a new nduction variable with the same mode
-                  as the node we are replacing. Espicially this means the mode might be changed
+               /* 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);
@@ -404,7 +421,6 @@ static int replace(ir_node *irn, ir_node *iv, ir_node *rc, iv_env *env) {
                        iv_e = get_irn_ne(iv, env);
                        e->header = iv_e->header;
                }
-               ++env->replaced;
                return 1;
        }
        return 0;
@@ -419,10 +435,10 @@ static int 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:
@@ -443,8 +459,24 @@ static int check_replace(ir_node *irn, iv_env *env) {
                        iv = right; rc = left;
                }
 
-               if (iv)
+               if (iv) {
+                       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;
@@ -511,9 +543,9 @@ static void classify_iv(scc *pscc, iv_env *env) {
                                        if (! out_rc) {
                                                out_rc = pred;
                                                ++num_outside;
-                                       }
-                                       else if (out_rc != pred)
+                                       } else if (out_rc != pred) {
                                                ++num_outside;
+                                       }
                                }
                        }
                        break;
@@ -525,7 +557,17 @@ 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 */
@@ -549,7 +591,7 @@ fail:
 }
 
 /**
- * Process a SCC.
+ * Process a SCC for the operator strength reduction.
  *
  * @param pscc  the SCC
  * @param env   the environment
@@ -577,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.
  *
@@ -678,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);
                }
        }
 }
@@ -722,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.
  *
@@ -893,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
@@ -900,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));
 
@@ -930,10 +1054,7 @@ void opt_osr(ir_graph *irg, unsigned flags) {
        env.replaced      = 0;
        env.lftr_replaced = 0;
        env.flags         = flags;
-
-       /* we need control flow loop information to decide whether
-        * we should do a replacement or not. */
-       construct_cf_backedges(irg);
+       env.process_scc   = process_scc;
 
        /* Clear all links and move Proj nodes into the
           the same block as it's predecessors.
@@ -956,8 +1077,6 @@ void opt_osr(ir_graph *irg, unsigned flags) {
                //lftr(irg, &env);
 
                set_irg_outs_inconsistent(irg);
-               /* cfg loop still valid */
-
                DB((dbg, LEVEL_1, "Replacements: %u + %u (lftr)\n\n", env.replaced, env.lftr_replaced));
        }
 
@@ -965,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;
 }