Fixed and simplified rot matcher
[libfirm] / ir / ir / iropt.c
index ec69799..8241810 100644 (file)
 /* Make types visible to allow most efficient access */
 #include "entity_t.h"
 
+/**
+ * Returns the tarval of a Const node or tarval_bad for all other nodes.
+ */
+static tarval *default_value_of(const ir_node *n) {
+       if (is_Const(n))
+               return get_Const_tarval(n); /* might return tarval_bad */
+       else
+               return tarval_bad;
+}
+
+value_of_func value_of_ptr = default_value_of;
+
+void set_value_of_func(value_of_func func) {
+       if (func != NULL)
+               value_of_ptr = func;
+       else
+               value_of_ptr = default_value_of;
+}
+
 /**
  * Return the value of a Constant.
  */
@@ -4527,7 +4546,7 @@ static ir_node *transform_node_Or_bf_store(ir_node *or) {
 static ir_node *transform_node_Or_Rotl(ir_node *or) {
        ir_mode *mode = get_irn_mode(or);
        ir_node *shl, *shr, *block;
-       ir_node *irn, *x, *c1, *c2, *v, *sub, *n;
+       ir_node *irn, *x, *c1, *c2, *v, *sub, *n, *rotval;
        tarval *tv1, *tv2;
 
        if (! mode_is_int(mode))
@@ -4568,64 +4587,45 @@ static ir_node *transform_node_Or_Rotl(ir_node *or) {
                        return or;
 
                /* yet, condition met */
-               block = get_irn_n(or, -1);
+               block = get_nodes_block(or);
 
                n = new_r_Rotl(current_ir_graph, block, x, c1, mode);
 
                DBG_OPT_ALGSIM1(or, shl, shr, n, FS_OPT_OR_SHFT_TO_ROTL);
                return n;
-       } else if (is_Sub(c1)) {
-               v   = c2;
-               sub = c1;
-
-               if (get_Sub_right(sub) != v)
-                       return or;
-
-               c1 = get_Sub_left(sub);
-               if (!is_Const(c1))
-                       return or;
-
-               tv1 = get_Const_tarval(c1);
-               if (! tarval_is_long(tv1))
-                       return or;
-
-               if (get_tarval_long(tv1) != (int) get_mode_size_bits(mode))
-                       return or;
-
-               /* yet, condition met */
-               block = get_nodes_block(or);
-
-               /* a Rot right is not supported, so use a rot left */
-               n =  new_r_Rotl(current_ir_graph, block, x, sub, mode);
+       }
 
-               DBG_OPT_ALGSIM0(or, n, FS_OPT_OR_SHFT_TO_ROTL);
-               return n;
-       } else if (is_Sub(c2)) {
-               v   = c1;
-               sub = c2;
+    if (is_Sub(c1)) {
+           v      = c2;
+               sub    = c1;
+        rotval = sub; /* a Rot right is not supported, so use a rot left */
+    } else if (is_Sub(c2)) {
+               v      = c1;
+       sub    = c2;
+        rotval = v;
+    } else return or;
 
-               c1 = get_Sub_left(sub);
-               if (!is_Const(c1))
-                       return or;
+       if (get_Sub_right(sub) != v)
+               return or;
 
-               tv1 = get_Const_tarval(c1);
-               if (! tarval_is_long(tv1))
-                       return or;
+       c1 = get_Sub_left(sub);
+       if (!is_Const(c1))
+               return or;
 
-               if (get_tarval_long(tv1) != (int) get_mode_size_bits(mode))
-                       return or;
+       tv1 = get_Const_tarval(c1);
+       if (! tarval_is_long(tv1))
+               return or;
 
-               /* yet, condition met */
-               block = get_irn_n(or, -1);
+       if (get_tarval_long(tv1) != (int) get_mode_size_bits(mode))
+               return or;
 
-               /* a Rot Left */
-               n = new_r_Rotl(current_ir_graph, block, x, v, mode);
+       /* yet, condition met */
+       block = get_nodes_block(or);
 
-               DBG_OPT_ALGSIM0(or, n, FS_OPT_OR_SHFT_TO_ROTL);
-               return n;
-       }
+       n = new_r_Rotl(current_ir_graph, block, x, rotval, mode);
 
-       return or;
+       DBG_OPT_ALGSIM0(or, n, FS_OPT_OR_SHFT_TO_ROTL);
+       return n;
 }  /* transform_node_Or_Rotl */
 
 /**
@@ -4691,9 +4691,9 @@ static ir_node *transform_node(ir_node *n);
  */
 static ir_node *transform_node_shift(ir_node *n) {
        ir_node *left, *right;
-       tarval *tv1, *tv2, *res;
        ir_mode *mode;
-       int modulo_shf, flag;
+       tarval *tv1, *tv2, *res;
+       ir_node *in[2], *irn, *block;
 
        left = get_binop_left(n);
 
@@ -4710,49 +4710,249 @@ static ir_node *transform_node_shift(ir_node *n) {
        if (tv2 == tarval_bad)
                return n;
 
-       res = tarval_add(tv1, tv2);
+       res  = tarval_add(tv1, tv2);
+       mode = get_irn_mode(n);
 
        /* beware: a simple replacement works only, if res < modulo shift */
+       if (!is_Rotl(n)) {
+               int modulo_shf = get_mode_modulo_shift(mode);
+               assert(modulo_shf >= (int) get_mode_size_bits(mode));
+               if (modulo_shf > 0) {
+                       tarval *modulo = new_tarval_from_long(modulo_shf,
+                                                             get_tarval_mode(res));
+
+                       /* shifting too much */
+                       if (!(tarval_cmp(res, modulo) & pn_Cmp_Lt)) {
+                               if (is_Shrs(n)) {
+                                       ir_graph *irg   = get_irn_irg(n);
+                                       ir_node  *block = get_nodes_block(n);
+                                       dbg_info *dbgi  = get_irn_dbg_info(n);
+                                       ir_node  *cnst  = new_Const(mode_Iu, new_tarval_from_long(get_mode_size_bits(mode)-1, mode_Iu));
+                                       return new_rd_Shrs(dbgi, irg, block, get_binop_left(left),
+                                                          cnst, mode);
+                               }
+
+                               return new_Const(mode, get_mode_null(mode));
+                       }
+               }
+       } else {
+               res = tarval_mod(res, new_tarval_from_long(get_mode_size_bits(mode), get_tarval_mode(res)));
+       }
+
+       /* ok, we can replace it */
+       block = get_nodes_block(n);
+
+       in[0] = get_binop_left(left);
+       in[1] = new_r_Const(current_ir_graph, block, get_tarval_mode(res), res);
+
+       irn = new_ir_node(NULL, current_ir_graph, block, get_irn_op(n), mode, 2, in);
+
+       DBG_OPT_ALGSIM0(n, irn, FS_OPT_REASSOC_SHIFT);
+
+       return transform_node(irn);
+}  /* transform_node_shift */
+
+/**
+ * normalisation: (x & c1) >> c2   to   (x >> c2) & (c1 >> c2)
+ *  (we can use:
+ *    - and, or, xor          instead of &
+ *    - Shl, Shr, Shrs, rotl  instead of >>
+ *    (with a special case for Or/Xor + Shrs)
+ */
+static ir_node *transform_node_bitop_shift(ir_node *n) {
+       ir_node  *left;
+       ir_node  *right = get_binop_right(n);
+       ir_mode  *mode  = get_irn_mode(n);
+       ir_node  *bitop_left;
+       ir_node  *bitop_right;
+       ir_op    *op_left;
+       ir_graph *irg;
+       ir_node  *block;
+       dbg_info *dbgi;
+       ir_node  *new_shift;
+       ir_node  *new_bitop;
+       ir_node  *new_const;
+       tarval   *tv1;
+       tarval   *tv2;
+       tarval   *tv_shift;
+
+       assert(is_Shrs(n) || is_Shr(n) || is_Shl(n) || is_Rotl(n));
+
+       if (!is_Const(right))
+               return n;
+
+       left    = get_binop_left(n);
+       op_left = get_irn_op(left);
+       if (op_left != op_And && op_left != op_Or && op_left != op_Eor)
+               return n;
+
+       /* doing it with Shrs is not legal if the Or/Eor affects the topmost bit */
+       if (is_Shrs(n) && (op_left == op_Or || op_left == op_Eor)) {
+               /* TODO: test if sign bit is affectes */
+               return n;
+       }
+
+       bitop_right = get_binop_right(left);
+       if (!is_Const(bitop_right))
+               return n;
+
+       bitop_left = get_binop_left(left);
+
+       irg   = get_irn_irg(n);
+       block = get_nodes_block(n);
+       dbgi  = get_irn_dbg_info(n);
+       tv1   = get_Const_tarval(bitop_right);
+       tv2   = get_Const_tarval(right);
+
+       assert(get_tarval_mode(tv1) == mode);
+
+       if (is_Shl(n)) {
+               new_shift = new_rd_Shl(dbgi, irg, block, bitop_left, right, mode);
+               tv_shift  = tarval_shl(tv1, tv2);
+       } else if(is_Shr(n)) {
+               new_shift = new_rd_Shr(dbgi, irg, block, bitop_left, right, mode);
+               tv_shift  = tarval_shr(tv1, tv2);
+       } else if(is_Shrs(n)) {
+               new_shift = new_rd_Shrs(dbgi, irg, block, bitop_left, right, mode);
+               tv_shift  = tarval_shrs(tv1, tv2);
+       } else {
+               assert(is_Rotl(n));
+               new_shift = new_rd_Rotl(dbgi, irg, block, bitop_left, right, mode);
+               tv_shift  = tarval_rotl(tv1, tv2);
+       }
+
+       assert(get_tarval_mode(tv_shift) == mode);
+       new_const = new_Const(mode, tv_shift);
+
+       if (op_left == op_And) {
+               new_bitop = new_rd_And(dbgi, irg, block, new_shift, new_const, mode);
+       } else if(op_left == op_Or) {
+               new_bitop = new_rd_Or(dbgi, irg, block, new_shift, new_const, mode);
+       } else {
+               assert(op_left == op_Eor);
+               new_bitop = new_rd_Eor(dbgi, irg, block, new_shift, new_const, mode);
+       }
+
+       return new_bitop;
+}
+
+/**
+ * normalisation:
+ *    (x << c1) >> c2  <=>  x OP (c2-c1) & ((-1 << c1) >> c2)
+ *    also:
+ *    (x >> c1) << c2  <=>  x OP (c2-c1) & ((-1 >> c1) << c2)
+ *      (also with x >>s c1  when c1>=c2)
+ */
+static ir_node *transform_node_shl_shr(ir_node *n) {
+       ir_node  *left;
+       ir_node  *right = get_binop_right(n);
+       ir_node  *x;
+       ir_graph *irg;
+       ir_node  *block;
+       ir_mode  *mode;
+       dbg_info *dbgi;
+       ir_node  *new_const;
+       ir_node  *new_shift;
+       ir_node  *new_and;
+       tarval   *tv_shl;
+       tarval   *tv_shr;
+       tarval   *tv_shift;
+       tarval   *tv_mask;
+       pn_Cmp    pnc;
+       int       need_shrs = 0;
+
+       assert(is_Shl(n) || is_Shr(n) || is_Shrs(n));
+
+       if (!is_Const(right))
+               return n;
+
+       left = get_binop_left(n);
        mode = get_irn_mode(n);
+       if (is_Shl(n) && (is_Shr(left) || is_Shrs(left))) {
+               ir_node *shr_right = get_binop_right(left);
 
-       flag = 0;
+               if (!is_Const(shr_right))
+                       return n;
 
-       modulo_shf = get_mode_modulo_shift(mode);
-       if (modulo_shf > 0) {
-               tarval *modulo = new_tarval_from_long(modulo_shf, get_tarval_mode(res));
+               x      = get_binop_left(left);
+               tv_shr = get_Const_tarval(shr_right);
+               tv_shl = get_Const_tarval(right);
 
-               if (tarval_cmp(res, modulo) & pn_Cmp_Lt)
-                       flag = 1;
-       } else
-               flag = 1;
+               if (is_Shrs(left)) {
+                       /* shrs variant only allowed if c1 >= c2 */
+                       if (! (tarval_cmp(tv_shl, tv_shr) & pn_Cmp_Ge))
+                               return n;
 
-       if (flag) {
-               /* ok, we can replace it */
-               ir_node *in[2], *irn, *block = get_irn_n(n, -1);
+                       tv_mask = tarval_shrs(get_mode_all_one(mode), tv_shr);
+                       need_shrs = 1;
+               } else {
+                       tv_mask = tarval_shr(get_mode_all_one(mode), tv_shr);
+               }
+               tv_mask = tarval_shl(tv_mask, tv_shl);
+       } else if(is_Shr(n) && is_Shl(left)) {
+               ir_node *shl_right = get_Shl_right(left);
+
+               if (!is_Const(shl_right))
+                       return n;
 
-               in[0] = get_binop_left(left);
-               in[1] = new_r_Const(current_ir_graph, block, get_tarval_mode(res), res);
+               x      = get_Shl_left(left);
+               tv_shr = get_Const_tarval(right);
+               tv_shl = get_Const_tarval(shl_right);
 
-               irn = new_ir_node(NULL, current_ir_graph, block, get_irn_op(n), mode, 2, in);
+               tv_mask = tarval_shl(get_mode_all_one(mode), tv_shl);
+               tv_mask = tarval_shr(tv_mask, tv_shr);
+       } else {
+               return n;
+       }
 
-               DBG_OPT_ALGSIM0(n, irn, FS_OPT_REASSOC_SHIFT);
+       assert(get_tarval_mode(tv_shl) == get_tarval_mode(tv_shr));
+       assert(tv_mask != tarval_bad);
+       assert(get_tarval_mode(tv_mask) == mode);
 
-               return transform_node(irn);
+       irg   = get_irn_irg(n);
+       block = get_nodes_block(n);
+       dbgi  = get_irn_dbg_info(n);
+
+       pnc = tarval_cmp(tv_shl, tv_shr);
+       if (pnc == pn_Cmp_Lt || pnc == pn_Cmp_Eq) {
+               tv_shift  = tarval_sub(tv_shr, tv_shl);
+               new_const = new_Const(get_tarval_mode(tv_shift), tv_shift);
+               if (need_shrs) {
+                       new_shift = new_rd_Shrs(dbgi, irg, block, x, new_const, mode);
+               } else {
+                       new_shift = new_rd_Shr(dbgi, irg, block, x, new_const, mode);
+               }
+       } else {
+               assert(pnc == pn_Cmp_Gt);
+               tv_shift  = tarval_sub(tv_shl, tv_shr);
+               new_const = new_Const(get_tarval_mode(tv_shift), tv_shift);
+               new_shift = new_rd_Shl(dbgi, irg, block, x, new_const, mode);
        }
-       return n;
-}  /* transform_node_shift */
+
+       new_const = new_Const(mode, tv_mask);
+       new_and   = new_rd_And(dbgi, irg, block, new_shift, new_const, mode);
+
+       return new_and;
+}
 
 /**
  * Transform a Shr.
  */
 static ir_node *transform_node_Shr(ir_node *n) {
        ir_node *c, *oldn = n;
-       ir_node *a    = get_Shr_left(n);
-       ir_node *b    = get_Shr_right(n);
-       ir_mode *mode = get_irn_mode(n);
+       ir_node *left  = get_Shr_left(n);
+       ir_node *right = get_Shr_right(n);
+       ir_mode *mode  = get_irn_mode(n);
+
+       HANDLE_BINOP_PHI(tarval_shr, left, right, c, mode);
+       n = transform_node_shift(n);
+
+       if (is_Shr(n))
+               n = transform_node_shl_shr(n);
+       if (is_Shr(n))
+               n = transform_node_bitop_shift(n);
 
-       HANDLE_BINOP_PHI(tarval_shr, a, b, c, mode);
-       return transform_node_shift(n);
+       return n;
 }  /* transform_node_Shr */
 
 /**
@@ -4765,7 +4965,12 @@ static ir_node *transform_node_Shrs(ir_node *n) {
        ir_mode *mode = get_irn_mode(n);
 
        HANDLE_BINOP_PHI(tarval_shrs, a, b, c, mode);
-       return transform_node_shift(n);
+       n = transform_node_shift(n);
+
+       if (is_Shrs(n))
+               n = transform_node_bitop_shift(n);
+
+       return n;
 }  /* transform_node_Shrs */
 
 /**
@@ -4778,7 +4983,14 @@ static ir_node *transform_node_Shl(ir_node *n) {
        ir_mode *mode = get_irn_mode(n);
 
        HANDLE_BINOP_PHI(tarval_shl, a, b, c, mode);
-       return transform_node_shift(n);
+       n = transform_node_shift(n);
+
+       if (is_Shl(n))
+               n = transform_node_shl_shr(n);
+       if (is_Shl(n))
+               n = transform_node_bitop_shift(n);
+
+       return n;
 }  /* transform_node_Shl */
 
 /**
@@ -4791,7 +5003,12 @@ static ir_node *transform_node_Rotl(ir_node *n) {
        ir_mode *mode = get_irn_mode(n);
 
        HANDLE_BINOP_PHI(tarval_rotl, a, b, c, mode);
-       return transform_node_shift(n);
+       n = transform_node_shift(n);
+
+       if (is_Rotl(n))
+               n = transform_node_bitop_shift(n);
+
+       return n;
 }  /* transform_node_Rotl */
 
 /**