fold-const.c (fold_cond_expr_with_comparison): Move simplification for A cmp C1 ...
authorBin Cheng <bin.cheng@arm.com>
Wed, 23 Nov 2016 12:44:08 +0000 (12:44 +0000)
committerBin Cheng <amker@gcc.gnu.org>
Wed, 23 Nov 2016 12:44:08 +0000 (12:44 +0000)
* fold-const.c (fold_cond_expr_with_comparison): Move simplification
for A cmp C1 ? A : C2 to below, also simplify remaining code.
* match.pd: Move and extend simplification from above to here:
(cond (cmp (convert1? x) c1) (convert2? x) c2) -> (minmax (x c)).
* tree-if-conv.c (ifcvt_follow_ssa_use_edges): New func.
(predicate_scalar_phi): Call fold_stmt using the new valueize func.

gcc/testsuite
* gcc.dg/fold-cond_expr-1.c: New test.
* gcc.dg/fold-condcmpconv-1.c: New test.
* gcc.dg/fold-condcmpconv-2.c: New test.

From-SVN: r242750

gcc/ChangeLog
gcc/fold-const.c
gcc/match.pd
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/fold-cond_expr-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-condcmpconv-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-condcmpconv-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr66726.c
gcc/tree-if-conv.c

index 0bafde2..a330d7a 100644 (file)
@@ -1,3 +1,12 @@
+2016-11-23  Bin Cheng  <bin.cheng@arm.com>
+
+       * fold-const.c (fold_cond_expr_with_comparison): Move simplification
+       for A cmp C1 ? A : C2 to below, also simplify remaining code.
+       * match.pd: Move and extend simplification from above to here:
+       (cond (cmp (convert1? x) c1) (convert2? x) c2) -> (minmax (x c)).
+       * tree-if-conv.c (ifcvt_follow_ssa_use_edges): New func.
+       (predicate_scalar_phi): Call fold_stmt using the new valueize func.
+
 2016-11-23  Martin Liska  <mliska@suse.cz>
             Martin Jambor  <mjambor@suse.cz>
 
index a0055c4..cbfbc24 100644 (file)
@@ -5210,95 +5210,18 @@ fold_cond_expr_with_comparison (location_t loc, tree type,
        }
     }
 
-  /* If this is A op C1 ? A : C2 with C1 and C2 constant integers,
-     we might still be able to simplify this.  For example,
-     if C1 is one less or one more than C2, this might have started
-     out as a MIN or MAX and been transformed by this function.
-     Only good for INTEGER_TYPEs, because we need TYPE_MAX_VALUE.  */
+  /* If this is A == C1 ? A : C2 with C1 and C2 constant integers,
+     we simplify it into A == C1 ? C1 : C2.  */
 
-  if (INTEGRAL_TYPE_P (type)
+  if (comp_code == EQ_EXPR
+      && INTEGRAL_TYPE_P (type)
       && TREE_CODE (arg01) == INTEGER_CST
+      && TREE_CODE (arg1) != INTEGER_CST
       && TREE_CODE (arg2) == INTEGER_CST)
-    switch (comp_code)
-      {
-      case EQ_EXPR:
-       if (TREE_CODE (arg1) == INTEGER_CST)
-         break;
-       /* We can replace A with C1 in this case.  */
-       arg1 = fold_convert_loc (loc, type, arg01);
-       return fold_build3_loc (loc, COND_EXPR, type, arg0, arg1, arg2);
-
-      case LT_EXPR:
-       /* If C1 is C2 + 1, this is min(A, C2), but use ARG00's type for
-          MIN_EXPR, to preserve the signedness of the comparison.  */
-       if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type),
-                              OEP_ONLY_CONST)
-           && operand_equal_p (arg01,
-                               const_binop (PLUS_EXPR, arg2,
-                                            build_int_cst (type, 1)),
-                               OEP_ONLY_CONST))
-         {
-           tem = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (arg00), arg00,
-                                  fold_convert_loc (loc, TREE_TYPE (arg00),
-                                                    arg2));
-           return fold_convert_loc (loc, type, tem);
-         }
-       break;
-
-      case LE_EXPR:
-       /* If C1 is C2 - 1, this is min(A, C2), with the same care
-          as above.  */
-       if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type),
-                              OEP_ONLY_CONST)
-           && operand_equal_p (arg01,
-                               const_binop (MINUS_EXPR, arg2,
-                                            build_int_cst (type, 1)),
-                               OEP_ONLY_CONST))
-         {
-           tem = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (arg00), arg00,
-                                  fold_convert_loc (loc, TREE_TYPE (arg00),
-                                                    arg2));
-           return fold_convert_loc (loc, type, tem);
-         }
-       break;
-
-      case GT_EXPR:
-       /* If C1 is C2 - 1, this is max(A, C2), but use ARG00's type for
-          MAX_EXPR, to preserve the signedness of the comparison.  */
-       if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type),
-                              OEP_ONLY_CONST)
-           && operand_equal_p (arg01,
-                               const_binop (MINUS_EXPR, arg2,
-                                            build_int_cst (type, 1)),
-                               OEP_ONLY_CONST))
-         {
-           tem = fold_build2_loc (loc, MAX_EXPR, TREE_TYPE (arg00), arg00,
-                                  fold_convert_loc (loc, TREE_TYPE (arg00),
-                                                    arg2));
-           return fold_convert_loc (loc, type, tem);
-         }
-       break;
-
-      case GE_EXPR:
-       /* If C1 is C2 + 1, this is max(A, C2), with the same care as above.  */
-       if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type),
-                              OEP_ONLY_CONST)
-           && operand_equal_p (arg01,
-                               const_binop (PLUS_EXPR, arg2,
-                                            build_int_cst (type, 1)),
-                               OEP_ONLY_CONST))
-         {
-           tem = fold_build2_loc (loc, MAX_EXPR, TREE_TYPE (arg00), arg00,
-                                  fold_convert_loc (loc, TREE_TYPE (arg00),
-                                                    arg2));
-           return fold_convert_loc (loc, type, tem);
-         }
-       break;
-      case NE_EXPR:
-       break;
-      default:
-       gcc_unreachable ();
-      }
+    {
+      arg1 = fold_convert_loc (loc, type, arg01);
+      return fold_build3_loc (loc, COND_EXPR, type, arg0, arg1, arg2);
+    }
 
   return NULL_TREE;
 }
index 6665412..2599d27 100644 (file)
@@ -1953,6 +1953,67 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (integer_zerop (@0))
    @2)))
 
+/* Simplification moved from fold_cond_expr_with_comparison.  It may also
+   be extended.  */
+/* (cond (cmp (convert1? x) c1) (convert2? x) c2) -> (minmax (x c)) if:
+     1) Conversions are type widening from smaller type.
+     2) Const c1 equals to c2 after canonicalizing comparison.
+     3) Comparison has tree code LT, LE, GT or GE.
+   This specific pattern is needed when (cmp (convert x) c) may not
+   be simplified by comparison patterns because of multiple uses of
+   x.  It also makes sense here because simplifying across multiple
+   referred var is always benefitial for complicated cases.  */
+(for cmp (lt le gt ge)
+ (simplify
+  (cond (cmp@0 (convert1? @1) INTEGER_CST@3) (convert2? @1) INTEGER_CST@2)
+  (with
+   {
+     tree from_type = TREE_TYPE (@1);
+     tree c1_type = TREE_TYPE (@3), c2_type = TREE_TYPE (@2);
+     enum tree_code code = TREE_CODE (@0), cmp_code = TREE_CODE (@0);
+
+     if (int_fits_type_p (@2, from_type)
+        && (types_match (c1_type, from_type)
+            || (TYPE_PRECISION (c1_type) > TYPE_PRECISION (from_type)
+                && (TYPE_UNSIGNED (from_type)
+                    || TYPE_SIGN (c1_type) == TYPE_SIGN (from_type))))
+        && (types_match (c2_type, from_type)
+            || (TYPE_PRECISION (c2_type) > TYPE_PRECISION (from_type)
+                && (TYPE_UNSIGNED (from_type)
+                    || TYPE_SIGN (c2_type) == TYPE_SIGN (from_type)))))
+       {
+        if (wi::to_widest (@3) == (wi::to_widest (@2) - 1))
+          {
+            /* 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::to_widest (@3) == (wi::to_widest (@2) + 1))
+          {
+            /* 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::to_widest (@2) == wi::to_widest (@3))
+          {
+            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)
+    (convert (max @1 (convert:from_type @2)))
+    (if (code == MIN_EXPR)
+     (convert (min @1 (convert:from_type @2))))))))
+
 (for cnd (cond vec_cond)
  /* A ? B : (A ? X : C) -> A ? B : C.  */
  (simplify
index 64fbed7..604c591 100644 (file)
@@ -1,3 +1,9 @@
+2016-11-23  Bin Cheng  <bin.cheng@arm.com>
+
+       * gcc.dg/fold-cond_expr-1.c: New test.
+       * gcc.dg/fold-condcmpconv-1.c: New test.
+       * gcc.dg/fold-condcmpconv-2.c: New test.
+
 2016-11-23  Richard Biener  <rguenther@suse.de>
 
        PR middle-end/71762
diff --git a/gcc/testsuite/gcc.dg/fold-cond_expr-1.c b/gcc/testsuite/gcc.dg/fold-cond_expr-1.c
new file mode 100644 (file)
index 0000000..68ec754
--- /dev/null
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+min1 (signed char op1, signed char op2)
+{
+  return (op1 < 25) ? (int)op1 : 24;
+}
+int
+min2 (signed char op1, signed char op2)
+{
+  return (op1 <= 24) ? (int)op1 : 25;
+}
+int
+min3 (unsigned char op1, unsigned char op2)
+{
+  return (op1 < 25) ? (unsigned int)op1 : 24;
+}
+int
+min4 (unsigned char op1, unsigned char op2)
+{
+  return (op1 <= 24) ? (unsigned int)op1 : 25;
+}
+int
+max1 (signed char op1, signed char op2)
+{
+  return (op1 > 24) ? (int)op1 : 25;
+}
+int
+max2 (signed char op1, signed char op2)
+{
+  return (op1 >= 25) ? (int)op1 : 24;
+}
+int
+max3 (unsigned char op1, unsigned char op2)
+{
+  return (op1 > 24) ? (unsigned int)op1 : 25;
+}
+int
+max4 (unsigned char op1, unsigned char op2)
+{
+  return (op1 >= 25) ? (unsigned int)op1 : 24;
+}
+
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 4 "optimized" } } */
+
diff --git a/gcc/testsuite/gcc.dg/fold-condcmpconv-1.c b/gcc/testsuite/gcc.dg/fold-condcmpconv-1.c
new file mode 100644 (file)
index 0000000..321294f
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo (unsigned short a[], unsigned int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (unsigned short)(x >= 255 ? 255 : x);
+    }  return x;
+}
+
+/* { dg-final { scan-tree-dump " = MIN_EXPR <" "ifcvt" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-condcmpconv-2.c b/gcc/testsuite/gcc.dg/fold-condcmpconv-2.c
new file mode 100644 (file)
index 0000000..5d3ef4a
--- /dev/null
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo (short a[], int x)
+{
+  unsigned int i;
+  for (i = 0; i < 1000; i++)
+    {
+      x = a[i];
+      a[i] = (short)(x <= 0 ? 0 : x);
+    }  return x;
+}
+
+
+/* { dg-final { scan-tree-dump " = MAX_EXPR <" "ifcvt" } } */
index a4c7418..e66d383 100644 (file)
@@ -1,6 +1,5 @@
-
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-phiopt1-details" } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
 
 extern unsigned short mode_size[];
 
@@ -10,6 +9,8 @@ oof (int mode)
   return (64 < mode_size[mode] ? 64 : mode_size[mode]);
 }
 
-/* { dg-final { scan-tree-dump-times "factor conversion out" 1 "phiopt1" } } */
-/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */
+/* With simplifications transforming cond_expr int min/max_expr
+   supported by match.pd patterns, we can optimize this at early
+   stage of compilation, rather than relying on phiopt for that.  */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "gimple" } } */
 
index 13e12c6..5716deb 100644 (file)
@@ -1749,6 +1749,14 @@ gen_phi_arg_condition (gphi *phi, vec<int> *occur,
   return cond;
 }
 
+/* Local valueization callback that follows all-use SSA edges.  */
+
+static tree
+ifcvt_follow_ssa_use_edges (tree val)
+{
+  return val;
+}
+
 /* Replace a scalar PHI node with a COND_EXPR using COND as condition.
    This routine can handle PHI nodes with more than two arguments.
 
@@ -1844,6 +1852,8 @@ predicate_scalar_phi (gphi *phi, gimple_stmt_iterator *gsi)
                                    arg0, arg1);
       new_stmt = gimple_build_assign (res, rhs);
       gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+      gimple_stmt_iterator new_gsi = gsi_for_stmt (new_stmt);
+      fold_stmt (&new_gsi, ifcvt_follow_ssa_use_edges);
       update_stmt (new_stmt);
 
       if (dump_file && (dump_flags & TDF_DETAILS))