Fixed and simplified rot matcher
[libfirm] / ir / ir / iropt.c
index c07e4ff..8241810 100644 (file)
@@ -4546,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))
@@ -4587,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);
+    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;
 
-               DBG_OPT_ALGSIM0(or, n, FS_OPT_OR_SHFT_TO_ROTL);
-               return n;
-       } else if (is_Sub(c2)) {
-               v   = c1;
-               sub = c2;
-
-               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 */
 
 /**
@@ -4771,17 +4752,207 @@ static ir_node *transform_node_shift(ir_node *n) {
        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);
+
+               if (!is_Const(shr_right))
+                       return n;
+
+               x      = get_binop_left(left);
+               tv_shr = get_Const_tarval(shr_right);
+               tv_shl = get_Const_tarval(right);
+
+               if (is_Shrs(left)) {
+                       /* shrs variant only allowed if c1 >= c2 */
+                       if (! (tarval_cmp(tv_shl, tv_shr) & pn_Cmp_Ge))
+                               return n;
+
+                       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;
+
+               x      = get_Shl_left(left);
+               tv_shr = get_Const_tarval(right);
+               tv_shl = get_Const_tarval(shl_right);
+
+               tv_mask = tarval_shl(get_mode_all_one(mode), tv_shl);
+               tv_mask = tarval_shr(tv_mask, tv_shr);
+       } else {
+               return n;
+       }
+
+       assert(get_tarval_mode(tv_shl) == get_tarval_mode(tv_shr));
+       assert(tv_mask != tarval_bad);
+       assert(get_tarval_mode(tv_mask) == mode);
+
+       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);
+       }
+
+       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, a, b, c, mode);
-       return transform_node_shift(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);
+
+       return n;
 }  /* transform_node_Shr */
 
 /**
@@ -4794,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 */
 
 /**
@@ -4807,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 */
 
 /**
@@ -4820,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 */
 
 /**