- don't dump alignment 0
[libfirm] / ir / opt / reassoc.c
index 8d29853..8cfcd8c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1995-2007 University of Karlsruhe.  All right reserved.
+ * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
  *
  * This file is part of libFirm.
  *
@@ -168,12 +168,13 @@ static int reassoc_Sub(ir_node **in)
                        /* already constant, nothing to do */
                        return 0;
                }
+
                mode = get_irn_mode(n);
                dbi  = get_irn_dbg_info(n);
 
                /* Beware of SubP(P, Is) */
                irn = new_rd_Minus(dbi, current_ir_graph, block, right, rmode);
-               irn = new_rd_Add(dbi, current_ir_graph, block, left, irn, get_irn_mode(n));
+               irn = new_rd_Add(dbi, current_ir_graph, block, left, irn, mode);
 
                DBG((dbg, LEVEL_5, "Applied: %n - %n => %n + (-%n)\n",
                        get_Sub_left(n), right, get_Sub_left(n), right));
@@ -338,7 +339,6 @@ static int reassoc_Mul(ir_node **node)
                        in[0] = new_rd_Mul(NULL, current_ir_graph, block, c, t1, mode);
                        in[1] = new_rd_Mul(NULL, current_ir_graph, block, c, t2, mode);
 
-                       mode  = get_mode_from_ops(in[0], in[1]);
                        irn   = optimize_node(new_ir_node(NULL, current_ir_graph, block, op, mode, 2, in));
 
                        /* In some cases it might happen that the new irn is equal the old one, for
@@ -363,6 +363,40 @@ static int reassoc_Mul(ir_node **node)
        return 0;
 }  /* reassoc_Mul */
 
+/**
+ * Reassociate Shl. We transform Shl(x, const) into Mul's if possible.
+ */
+static int reassoc_Shl(ir_node **node) {
+       ir_node *n = *node;
+       ir_node *c = get_Shl_right(n);
+       ir_node *x, *blk, *irn;
+       ir_mode *mode;
+       tarval *tv;
+
+       if (! is_Const(c))
+               return 0;
+
+       x = get_Shl_left(n);
+       mode = get_irn_mode(x);
+
+       tv = get_mode_one(mode);
+       tv = tarval_shl(tv, get_Const_tarval(c));
+
+       if (tv == tarval_bad)
+               return 0;
+
+       blk = get_nodes_block(n);
+       c   = new_r_Const(current_ir_graph, blk, mode, tv);
+       irn = new_rd_Mul(get_irn_dbg_info(n), current_ir_graph, blk, x, c, mode);
+
+       if (irn != n) {
+               exchange(n, irn);
+               *node = irn;
+               return 1;
+       }
+       return 0;
+}  /* reassoc_Shl */
+
 /**
  * The walker for the reassociation.
  */
@@ -446,61 +480,190 @@ static void do_reassociation(walker_t *wenv)
 
 /**
  * Returns the earliest were a,b are available.
- * Note that we know that we know that a, b both dominate
+ * Note that we know that a, b both dominate
  * the block of the previous operation, so one must dominate the other.
+ *
+ * If the earliest block is the start block, return curr_blk instead
  */
-static ir_node *earliest_block(ir_node *a, ir_node *b) {
+static ir_node *earliest_block(ir_node *a, ir_node *b, ir_node *curr_blk) {
        ir_node *blk_a = get_nodes_block(a);
        ir_node *blk_b = get_nodes_block(b);
+       ir_node *res;
 
        /* if blk_a != blk_b, one must dominate the other */
        if (block_dominates(blk_a, blk_b))
-               return blk_b;
+               res = blk_b;
        else
-               return blk_a;
-}
+               res = blk_a;
+       if (res == get_irg_start_block(current_ir_graph))
+               return curr_blk;
+       return res;
+}  /* earliest_block */
+
+/**
+ * Checks whether a node is a Constant expression.
+ * The following trees are constant expressions:
+ *
+ * Const, SymConst, Const + SymConst
+ *
+ * Handling SymConsts as const might be not a good idea for all
+ * architectures ...
+ */
+static int is_constant_expr(ir_node *irn) {
+       ir_op *op;
 
+       switch (get_irn_opcode(irn)) {
+       case iro_Const:
+       case iro_SymConst:
+               return 1;
+       case iro_Add:
+               op = get_irn_op(get_Add_left(irn));
+               if (op != op_Const && op != op_SymConst)
+                       return 0;
+               op = get_irn_op(get_Add_right(irn));
+               if (op != op_Const && op != op_SymConst)
+                       return 0;
+               return 1;
+       default:
+               return 0;
+       }
+}  /* is_constant_expr */
+
+/**
+ * Apply distributive Law for Mul and Add/Sub
+ */
+static int reverse_rule_distributive(ir_node **node) {
+       ir_node *n = *node;
+       ir_node *left  = get_binop_left(n);
+       ir_node *right = get_binop_right(n);
+       ir_node *x, *blk, *curr_blk;
+       ir_node *a, *b, *irn;
+       ir_op *op;
+       ir_mode *mode;
+       dbg_info *dbg;
+
+       op = get_irn_op(left);
+       if (op != get_irn_op(right))
+               return 0;
+
+       if (op == op_Shl) {
+               x = get_Shl_right(left);
+
+               if (x == get_Shl_right(right)) {
+                       /* (a << x) +/- (b << x) ==> (a +/- b) << x */
+                       a = get_Shl_left(left);
+                       b = get_Shl_left(right);
+                       goto transform;
+               }
+       } else if (op == op_Mul) {
+               x = get_Mul_left(left);
+
+               if (x == get_Mul_left(right)) {
+                       /* (x * a) +/- (x * b) ==> (a +/- b) * x */
+                       a = get_Mul_right(left);
+                       b = get_Mul_right(right);
+                       goto transform;
+               } else if (x == get_Mul_right(right)) {
+                       /* (x * a) +/- (b * x) ==> (a +/- b) * x */
+                       a = get_Mul_right(left);
+                       b = get_Mul_left(right);
+                       goto transform;
+               }
+
+               x = get_Mul_right(left);
+
+               if (x == get_Mul_right(right)) {
+                       /* (a * x) +/- (b * x) ==> (a +/- b) * x */
+                       a = get_Mul_left(left);
+                       b = get_Mul_left(right);
+                       goto transform;
+               } else if (x == get_Mul_left(right)) {
+                       /* (a * x) +/- (x * b) ==> (a +/- b) * x */
+                       a = get_Mul_left(left);
+                       b = get_Mul_right(right);
+                       goto transform;
+               }
+       }
+       return 0;
+
+transform:
+       curr_blk = get_nodes_block(n);
+
+       blk = earliest_block(a, b, curr_blk);
+
+       dbg  = get_irn_dbg_info(n);
+       mode = get_irn_mode(n);
+
+       if (is_Add(n))
+               irn = new_rd_Add(dbg, current_ir_graph, blk, a, b, mode);
+       else
+               irn = new_rd_Sub(dbg, current_ir_graph, blk, a, b, mode);
+
+       blk  = earliest_block(irn, x, curr_blk);
+
+       if (op == op_Mul)
+               irn = new_rd_Mul(dbg, current_ir_graph, blk, irn, x, mode);
+       else
+               irn = new_rd_Shl(dbg, current_ir_graph, blk, irn, x, mode);
+
+       exchange(n, irn);
+       *node = irn;
+       return 1;
+}  /* reverse_rule_distributive */
 
 /**
  * Move Constants towards the root.
  */
 static int move_consts_up(ir_node **node) {
        ir_node *n = *node;
-       ir_op *op = get_irn_op(n);
+       ir_op *op;
        ir_node *l, *r, *a, *b, *c, *blk, *irn, *in[2];
-       ir_mode *mode;
-       dbg_info *dbg = get_irn_dbg_info(n);
+       ir_mode *mode, *ma, *mb;
+       dbg_info *dbg;
 
        l = get_binop_left(n);
        r = get_binop_right(n);
-       if (get_irn_op(l) == op && !is_irn_constlike(r)) {
+
+       /* check if one is already a constant expression */
+       if (is_constant_expr(l) || is_constant_expr(r))
+               return 0;
+
+       dbg = get_irn_dbg_info(n);
+       op = get_irn_op(n);
+       if (get_irn_op(l) == op) {
+               /* (a .op. b) .op. r */
                a = get_binop_left(l);
                b = get_binop_right(l);
 
-               if (is_irn_constlike(a)) {
+               if (is_constant_expr(a)) {
+                       /* (C .op. b) .op. r ==> (r .op. b) .op. C */
                        c = a;
                        a = r;
                        blk = get_nodes_block(l);
                        dbg = dbg == get_irn_dbg_info(l) ? dbg : NULL;
                        goto transform;
-               } else if (is_irn_constlike(b)) {
+               } else if (is_constant_expr(b)) {
+                       /* (a .op. C) .op. r ==> (a .op. r) .op. C */
                        c = b;
                        b = r;
                        blk = get_nodes_block(l);
                        dbg = dbg == get_irn_dbg_info(l) ? dbg : NULL;
                        goto transform;
                }
-       } else if (get_irn_op(r) == op && !is_irn_constlike(l)) {
+       } else if (get_irn_op(r) == op) {
+               /* l .op. (a .op. b) */
                a = get_binop_left(r);
                b = get_binop_right(r);
 
-               if (is_irn_constlike(a)) {
+               if (is_constant_expr(a)) {
+                       /* l .op. (C .op. b) ==> (l .op. b) .op. C */
                        c = a;
                        a = l;
                        blk = get_nodes_block(r);
                        dbg = dbg == get_irn_dbg_info(r) ? dbg : NULL;
                        goto transform;
-               } else if (is_irn_constlike(b)) {
+               } else if (is_constant_expr(b)) {
+                       /* l .op. (a .op. C) ==> (a .op. l) .op. C */
                        c = b;
                        b = l;
                        blk = get_nodes_block(r);
@@ -511,7 +674,18 @@ static int move_consts_up(ir_node **node) {
        return 0;
 
 transform:
-       /* check if a+b can be calculted in the same block is the old instruction */
+       /* In some cases a and b might be both of different integer mode, and c a SymConst.
+        * in that case we could either
+        * 1.) cast into unsigned mode
+        * 2.) ignore
+        * we implement the second here
+        */
+       ma = get_irn_mode(a);
+       mb = get_irn_mode(b);
+       if (ma != mb && mode_is_int(ma) && mode_is_int(mb))
+               return 0;
+
+       /* check if (a .op. b) can be calculated in the same block is the old instruction */
        if (! block_dominates(get_nodes_block(a), blk))
                return 0;
        if (! block_dominates(get_nodes_block(b), blk))
@@ -520,8 +694,13 @@ transform:
        in[0] = a;
        in[1] = b;
 
-       mode = get_mode_from_ops(in[0], in[1]);
-       in[0] = optimize_node(new_ir_node(dbg, current_ir_graph, blk, op, mode, 2, in));
+       mode = get_mode_from_ops(a, b);
+       in[0] = irn = optimize_node(new_ir_node(dbg, current_ir_graph, blk, op, mode, 2, in));
+
+       /* beware: optimize_node might have changed the opcode, check again */
+       if (is_Add(irn) || is_Sub(irn)) {
+               reverse_rule_distributive(&in[0]);
+       }
        in[1] = c;
 
        mode = get_mode_from_ops(in[0], in[1]);
@@ -530,70 +709,7 @@ transform:
        exchange(n, irn);
        *node = irn;
        return 1;
-}  /* mve_consts_up */
-
-/**
- * Apply distributive Law for Mul and Add/Sub
- */
-static int reverse_rule_distributive(ir_node **node) {
-       ir_node *n = *node;
-       ir_node *left  = get_binop_left(n);
-       ir_node *right = get_binop_right(n);
-       ir_node *x, *blk;
-       ir_node *a, *b, *irn;
-       ir_mode *mode;
-       dbg_info *dbg;
-
-       if (! is_Mul(left) || !is_Mul(right))
-               return 0;
-
-       x = get_Mul_left(left);
-
-       if (x == get_Mul_left(right)) {
-               /* (x * a) +/- (x * b) */
-               a = get_Mul_right(left);
-               b = get_Mul_right(right);
-               goto transform;
-       } else if (x == get_Mul_right(right)) {
-               /* (x * a) +/- (b * x) */
-               a = get_Mul_right(left);
-               b = get_Mul_left(right);
-               goto transform;
-       }
-
-       x = get_Mul_right(left);
-
-       if (x == get_Mul_right(right)) {
-               /* (a * x) +/- (b * x) */
-               a = get_Mul_left(left);
-               b = get_Mul_left(right);
-               goto transform;
-       } else if (x == get_Mul_left(right)) {
-               /* (a * x) +/- (x * b) */
-               a = get_Mul_left(left);
-               b = get_Mul_right(right);
-               goto transform;
-       }
-       return 0;
-
-transform:
-       blk = earliest_block(a, b);
-
-       dbg  = get_irn_dbg_info(n);
-       mode = get_irn_mode(n);
-
-       if (is_Add(n))
-               irn = new_rd_Add(dbg, current_ir_graph, blk, a, b, mode);
-       else
-               irn = new_rd_Sub(dbg, current_ir_graph, blk, a, b, mode);
-
-       blk  = earliest_block(irn, x);
-       irn = new_rd_Mul(dbg, current_ir_graph, blk, irn, x, mode);
-
-       exchange(n, irn);
-       *node = irn;
-       return 1;
-}  /* reverse_rule_distributive */
+}  /* move_consts_up */
 
 /**
  * Apply the rules in reverse order, removing code that was not collapsed
@@ -612,10 +728,10 @@ static void reverse_rules(ir_node *node, void *env) {
 
                res = 0;
                if (is_op_commutative(op)) {
-                       /* no need to recurse here */
-                       wenv->changes |= move_consts_up(&node);
+                       wenv->changes |= res = move_consts_up(&node);
                }
-               if (op == op_Add || op == op_Sub) {
+               /* beware: move_consts_up might have changed the opcode, check again */
+               if (is_Add(node) || is_Sub(node)) {
                        wenv->changes |= res = reverse_rule_distributive(&node);
                }
        } while (res);
@@ -628,14 +744,14 @@ void optimize_reassociation(ir_graph *irg)
 {
        walker_t env;
        irg_loopinfo_state state;
+       ir_graph *rem;
 
        assert(get_irg_phase_state(irg) != phase_building);
        assert(get_irg_pinned(irg) != op_pin_state_floats &&
                "Reassociation needs pinned graph to work properly");
 
-       /* reassociation needs constant folding */
-       if (!get_opt_reassociation() || !get_opt_constant_folding())
-               return;
+       rem = current_ir_graph;
+       current_ir_graph = irg;
 
        /* we use dominance to detect dead blocks */
        assure_doms(irg);
@@ -654,12 +770,17 @@ void optimize_reassociation(ir_graph *irg)
        env.changes = 0;
        env.wq      = new_waitq();
 
-       /* now we have collected enough information, optimize */
-       irg_walk_graph(irg, NULL, wq_walker, &env);
-       do_reassociation(&env);
+       /* disable some optimizations while reassoc is running to prevent endless loops */
+       set_reassoc_running(1);
+       {
+               /* now we have collected enough information, optimize */
+               irg_walk_graph(irg, NULL, wq_walker, &env);
+               do_reassociation(&env);
 
-       /* reverse those rules that do not result in collapsed constants */
-       irg_walk_graph(irg, NULL, reverse_rules, &env);
+               /* reverse those rules that do not result in collapsed constants */
+               irg_walk_graph(irg, NULL, reverse_rules, &env);
+       }
+       set_reassoc_running(0);
 
        /* Handle graph state */
        if (env.changes) {
@@ -668,6 +789,7 @@ void optimize_reassociation(ir_graph *irg)
        }
 
        del_waitq(env.wq);
+       current_ir_graph = rem;
 }  /* optimize_reassociation */
 
 /* Sets the default reassociation operation for an ir_op_ops. */
@@ -682,6 +804,7 @@ ir_op_ops *firm_set_default_reassoc(ir_opcode code, ir_op_ops *ops)
        CASE(And);
        CASE(Or);
        CASE(Eor);
+       CASE(Shl);
        default:
                /* leave NULL */;
        }