match.pd: Add new pattern: (cond (cmp (convert?
authorBin Cheng <bin.cheng@arm.com>
Fri, 2 Dec 2016 14:13:11 +0000 (14:13 +0000)
committerBin Cheng <amker@gcc.gnu.org>
Fri, 2 Dec 2016 14:13:11 +0000 (14:13 +0000)
* match.pd: Add new pattern:
(cond (cmp (convert? x) c1) (op x c2) c3) -> (op (minmax x c1) c2).
gcc/testsuite
* gcc.dg/fold-bopcond-1.c: New test.
* gcc.dg/fold-bopcond-2.c: New test.

From-SVN: r243180

gcc/ChangeLog
gcc/match.pd
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/fold-bopcond-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-bopcond-2.c [new file with mode: 0644]

index 7da0acad77a69b23846fcaf99f6e2f08b7f3bd78..85b4bdb0ae0fbd76cd45772e67f907781c9bd9aa 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-02  Bin Cheng  <bin.cheng@arm.com>
+
+       * match.pd: Add new pattern:
+       (cond (cmp (convert? x) c1) (op x c2) c3) -> (op (minmax x c1) c2).
+
 2016-12-02  Nathan Sidwell  <nathan@acm.org>
 
        * diagnostic.c (diagnostic_report_diagnostic): Remove extraneous
index bc8a5e74d2d2116c7abc95378a0ad57e34fec2a4..dbb91034e95a3b71af5b717cec86a0025a0c8cf6 100644 (file)
@@ -2038,6 +2038,106 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (convert (cond (eq @1 (convert @3))
                     (convert:from_type @3) (convert:from_type @2)))))))))
 
+/* (cond (cmp (convert? x) c1) (op x c2) c3) -> (op (minmax x c1) c2) if:
+
+     1) OP is PLUS or MINUS.
+     2) CMP is LT, LE, GT or GE.
+     3) C3 == (C1 op C2), and computation doesn't have undefined behavior.
+
+   This pattern also handles special cases like:
+
+     A) Operand x is a unsigned to signed type conversion and c1 is
+       integer zero.  In this case,
+         (signed type)x  < 0  <=>  x  > MAX_VAL(signed type)
+         (signed type)x >= 0  <=>  x <= MAX_VAL(signed type)
+     B) Const c1 may not equal to (C3 op' C2).  In this case we also
+       check equality for (c1+1) and (c1-1) by adjusting comparison
+       code.
+
+   TODO: Though signed type is handled by this pattern, it cannot be
+   simplified at the moment because C standard requires additional
+   type promotion.  In order to match&simplify it here, the IR needs
+   to be cleaned up by other optimizers, i.e, VRP.  */
+(for op (plus minus)
+ (for cmp (lt le gt ge)
+  (simplify
+   (cond (cmp (convert? @X) INTEGER_CST@1) (op @X INTEGER_CST@2) INTEGER_CST@3)
+   (with { tree from_type = TREE_TYPE (@X), to_type = TREE_TYPE (@1); }
+    (if (types_match (from_type, to_type)
+        /* Check if it is special case A).  */
+        || (TYPE_UNSIGNED (from_type)
+            && !TYPE_UNSIGNED (to_type)
+            && TYPE_PRECISION (from_type) == TYPE_PRECISION (to_type)
+            && integer_zerop (@1)
+            && (cmp == LT_EXPR || cmp == GE_EXPR)))
+     (with
+      {
+       bool overflow = false;
+       enum tree_code code, cmp_code = cmp;
+       wide_int real_c1, c1 = @1, c2 = @2, c3 = @3;
+       signop sgn = TYPE_SIGN (from_type);
+
+       /* Handle special case A), given x of unsigned type:
+           ((signed type)x  < 0) <=> (x  > MAX_VAL(signed type))
+           ((signed type)x >= 0) <=> (x <= MAX_VAL(signed type))  */
+       if (!types_match (from_type, to_type))
+         {
+           if (cmp_code == LT_EXPR)
+             cmp_code = GT_EXPR;
+           if (cmp_code == GE_EXPR)
+             cmp_code = LE_EXPR;
+           c1 = wi::max_value (to_type);
+         }
+       /* To simplify this pattern, we require c3 = (c1 op c2).  Here we
+          compute (c3 op' c2) and check if it equals to c1 with op' being
+          the inverted operator of op.  Make sure overflow doesn't happen
+          if it is undefined.  */
+       if (op == PLUS_EXPR)
+         real_c1 = wi::sub (c3, c2, sgn, &overflow);
+       else
+         real_c1 = wi::add (c3, c2, sgn, &overflow);
+
+       code = cmp_code;
+       if (!overflow || !TYPE_OVERFLOW_UNDEFINED (from_type))
+         {
+           /* Check if c1 equals to real_c1.  Boundary condition is handled
+              by adjusting comparison operation if necessary.  */
+           if (!wi::cmp (wi::sub (real_c1, 1, sgn, &overflow), c1, sgn)
+               && !overflow)
+             {
+               /* X <= Y - 1 equals to X < Y.  */
+               if (cmp_code == LE_EXPR)
+                 code = LT_EXPR;
+               /* X > Y - 1 equals to X >= Y.  */
+               if (cmp_code == GT_EXPR)
+                 code = GE_EXPR;
+             }
+           if (!wi::cmp (wi::add (real_c1, 1, sgn, &overflow), c1, sgn)
+               && !overflow)
+             {
+               /* X < Y + 1 equals to X <= Y.  */
+               if (cmp_code == LT_EXPR)
+                 code = LE_EXPR;
+               /* X >= Y + 1 equals to X > Y.  */
+               if (cmp_code == GE_EXPR)
+                 code = GT_EXPR;
+             }
+           if (code != cmp_code || !wi::cmp (real_c1, c1, sgn))
+             {
+               if (cmp_code == LT_EXPR || cmp_code == LE_EXPR)
+                 code = MIN_EXPR;
+               if (cmp_code == GT_EXPR || cmp_code == GE_EXPR)
+                 code = MAX_EXPR;
+             }
+         }
+      }
+      (if (code == MAX_EXPR)
+       (op (max @X { wide_int_to_tree (from_type, real_c1); })
+          { wide_int_to_tree (from_type, c2); })
+       (if (code == MIN_EXPR)
+       (op (min @X { wide_int_to_tree (from_type, real_c1); })
+           { wide_int_to_tree (from_type, c2); })))))))))
+
 (for cnd (cond vec_cond)
  /* A ? B : (A ? X : C) -> A ? B : C.  */
  (simplify
index c1416d06fc5cc8643d25ebab2d6e618a1c7f244a..143687de60d4643cce58cd42da85eece6bf2f5cb 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-02  Bin Cheng  <bin.cheng@arm.com>
+
+       * gcc.dg/fold-bopcond-1.c: New test.
+       * gcc.dg/fold-bopcond-2.c: New test.
+
 2016-12-02  Dominik Vogt  <vogt@linux.vnet.ibm.com>
 
        * gcc.target/s390/md/setmem_long-1.c: Fix test.
diff --git a/gcc/testsuite/gcc.dg/fold-bopcond-1.c b/gcc/testsuite/gcc.dg/fold-bopcond-1.c
new file mode 100644 (file)
index 0000000..7324c16
--- /dev/null
@@ -0,0 +1,48 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo1 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x <= 32768 ? x + 32768 : 0);
+    }
+  return x;
+}
+
+int foo2 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x < 32768 ? x + 32768 : 0);
+    }
+  return x;
+}
+
+int foo3 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x < 1000 ? x - 1000 : 0);
+    }
+  return x;
+}
+
+int foo4 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x <= 2 ? x + 999 : 1001);
+    }
+  return x;
+}
+
+/* { dg-final { scan-tree-dump-times "MIN_EXPR " 4 "ifcvt" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-bopcond-2.c b/gcc/testsuite/gcc.dg/fold-bopcond-2.c
new file mode 100644 (file)
index 0000000..7a47449
--- /dev/null
@@ -0,0 +1,48 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo1 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x >= 32768 ? x - 32768 : 0);
+    }
+  return x;
+}
+
+int foo2 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x > 32768 ? x - 32768 : 0);
+    }
+  return x;
+}
+
+int foo3 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x > 1000 ? x - 1000 : 0);
+    }
+  return x;
+}
+
+int foo4 (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x >= 2 ? x - 32768 : 32770);
+    }
+  return x;
+}
+
+/* { dg-final { scan-tree-dump-times "MAX_EXPR " 4 "ifcvt" } } */