phiopt: Optimize (x != cst1 ? x : cst2) != cst3 [PR104639]
authorJakub Jelinek <jakub@redhat.com>
Mon, 11 Apr 2022 08:44:28 +0000 (10:44 +0200)
committerJakub Jelinek <jakub@redhat.com>
Mon, 11 Apr 2022 08:44:28 +0000 (10:44 +0200)
Here is an attempt to resolve a P1 regression, where due to threading
changes we no longer optimize
bool foo(int i) {
    while (i == 4)
        i += 2;
    return i;
}
to just return i != 0; by enhancing the phiopt value_replacement
optimization.  Normally it will optimize x != cst1 ? x : cst1 to x.
Here we extend it to also optimize x != cst1 ? x : cst2 to x if
it (phi result) has a single immediate use which is a comparison
with some INTEGER_CST cst3 and we can prove that we don't care
whether x is cst1 or cst2 because both compare the same against cst3.

2022-04-11  Jakub Jelinek  <jakub@redhat.com>

PR tree-optimization/104639
* tree-ssa-phiopt.cc: Include tree-ssa-propagate.h.
(value_replacement): Optimize (x != cst1 ? x : cst2) != cst3
into x != cst3.

* gcc.dg/tree-ssa/pr104639-1.c: New test.
* gcc.dg/tree-ssa/pr104639-2.c: New test.

gcc/testsuite/gcc.dg/tree-ssa/pr104639-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr104639-2.c [new file with mode: 0644]
gcc/tree-ssa-phiopt.cc

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr104639-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr104639-1.c
new file mode 100644 (file)
index 0000000..183fa37
--- /dev/null
@@ -0,0 +1,13 @@
+/* PR tree-optimization/104639 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -g -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-not "PHI <" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "i_\[0-9]*\\\(D\\\) != 0;" 1 "optimized" } } */
+
+_Bool
+foo (int i)
+{
+  while (i == 4)
+    i += 2;
+  return i;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr104639-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr104639-2.c
new file mode 100644 (file)
index 0000000..e251147
--- /dev/null
@@ -0,0 +1,54 @@
+/* PR tree-optimization/104639 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-tree-pre -g -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-not "PHI <" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "x_\[0-9]*\\\(D\\\) != 42;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "y_\[0-9]*\\\(D\\\) > 6;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "z_\[0-9]*\\\(D\\\) > 9;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "u_\[0-9]*\\\(D\\\) <= 7;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "v_\[0-9]*\\\(D\\\) <= 42;" 1 "optimized" } } */
+
+int
+f1 (int x)
+{
+  if (x == 4)
+    x = 6;
+  int xd = x;
+  return x != 42;
+}
+
+int
+f2 (int y)
+{
+  if (y == 4)
+    y = 6;
+  int yd = y;
+  return y > 6;
+}
+
+int
+f3 (int z)
+{
+  if (z == 4)
+    z = 6;
+  int zd = z;
+  return z >= 10;
+}
+
+int
+f4 (int u)
+{
+  if (u == 4)
+    u = 6;
+  int ud = u;
+  return u < 8;
+}
+
+int
+f5 (int v)
+{
+  if (v == 4)
+    v = 6;
+  int vd = v;
+  return v <= 42;
+}
index 4a0c9dd..00c8f39 100644 (file)
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-range.h"
 #include "gimple-match.h"
 #include "dbgcnt.h"
+#include "tree-ssa-propagate.h"
 
 static unsigned int tree_ssa_phiopt_worker (bool, bool, bool);
 static bool two_value_replacement (basic_block, basic_block, edge, gphi *,
@@ -1327,7 +1328,17 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
      We now need to verify that the two arguments in the PHI node match
      the two arguments to the equality comparison.  */
 
-  if (operand_equal_for_value_replacement (arg0, arg1, &code, cond))
+  bool equal_p = operand_equal_for_value_replacement (arg0, arg1, &code, cond);
+  bool maybe_equal_p = false;
+  if (!equal_p
+      && empty_or_with_defined_p
+      && TREE_CODE (gimple_cond_rhs (cond)) == INTEGER_CST
+      && (operand_equal_for_phi_arg_p (gimple_cond_lhs (cond), arg0)
+         ? TREE_CODE (arg1) == INTEGER_CST
+         : (operand_equal_for_phi_arg_p (gimple_cond_lhs (cond), arg1)
+            && TREE_CODE (arg0) == INTEGER_CST)))
+    maybe_equal_p = true;
+  if (equal_p || maybe_equal_p)
     {
       edge e;
       tree arg;
@@ -1358,11 +1369,123 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
          && single_non_singleton_phi_for_edges (phi_nodes (gimple_bb (phi)),
                                                 e0, e1) == phi)
        {
-          replace_phi_edge_with_variable (cond_bb, e1, phi, arg);
-         /* Note that we optimized this PHI.  */
-         return 2;
+         use_operand_p use_p;
+         gimple *use_stmt;
+
+         /* Even if arg0/arg1 isn't equal to second operand of cond, we
+            can optimize away the bb if we can prove it doesn't care whether
+            phi result is arg0/arg1 or second operand of cond.  Consider:
+            <bb 2> [local count: 118111600]:
+            if (i_2(D) == 4)
+              goto <bb 4>; [97.00%]
+            else
+              goto <bb 3>; [3.00%]
+
+            <bb 3> [local count: 3540129]:
+
+            <bb 4> [local count: 118111600]:
+            # i_6 = PHI <i_2(D)(3), 6(2)>
+            _3 = i_6 != 0;
+            Here, carg is 4, oarg is 6, crhs is 0, and because
+            (4 != 0) == (6 != 0), we don't care if i_6 is 4 or 6, both
+            have the same outcome.  So, can can optimize this to:
+            _3 = i_2(D) != 0;
+            If the single imm use of phi result >, >=, < or <=, similarly
+            we can check if both carg and oarg compare the same against
+            crhs using ccode.  */
+         if (maybe_equal_p
+             && TREE_CODE (arg) != INTEGER_CST
+             && single_imm_use (gimple_phi_result (phi), &use_p, &use_stmt))
+           {
+             enum tree_code ccode = ERROR_MARK;
+             tree clhs = NULL_TREE, crhs = NULL_TREE;
+             tree carg = gimple_cond_rhs (cond);
+             tree oarg = e0 == e ? arg1 : arg0;
+             if (is_gimple_assign (use_stmt)
+                 && (TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt))
+                     == tcc_comparison))
+               {
+                 ccode = gimple_assign_rhs_code (use_stmt);
+                 clhs = gimple_assign_rhs1 (use_stmt);
+                 crhs = gimple_assign_rhs2 (use_stmt);
+               }
+             else if (gimple_code (use_stmt) == GIMPLE_COND)
+               {
+                 ccode = gimple_cond_code (use_stmt);
+                 clhs = gimple_cond_lhs (use_stmt);
+                 crhs = gimple_cond_rhs (use_stmt);
+               }
+             if (ccode != ERROR_MARK
+                 && clhs == gimple_phi_result (phi)
+                 && TREE_CODE (crhs) == INTEGER_CST)
+               switch (ccode)
+                 {
+                 case EQ_EXPR:
+                 case NE_EXPR:
+                   if (!tree_int_cst_equal (crhs, carg)
+                       && !tree_int_cst_equal (crhs, oarg))
+                     equal_p = true;
+                   break;
+                 case GT_EXPR:
+                   if (tree_int_cst_lt (crhs, carg)
+                       == tree_int_cst_lt (crhs, oarg))
+                     equal_p = true;
+                   break;
+                 case GE_EXPR:
+                   if (tree_int_cst_le (crhs, carg)
+                       == tree_int_cst_le (crhs, oarg))
+                     equal_p = true;
+                   break;
+                 case LT_EXPR:
+                   if (tree_int_cst_lt (carg, crhs)
+                       == tree_int_cst_lt (oarg, crhs))
+                     equal_p = true;
+                   break;
+                 case LE_EXPR:
+                   if (tree_int_cst_le (carg, crhs)
+                       == tree_int_cst_le (oarg, crhs))
+                     equal_p = true;
+                   break;
+                 default:
+                   break;
+                 }
+             if (equal_p && MAY_HAVE_DEBUG_BIND_STMTS)
+               {
+                 imm_use_iterator imm_iter;
+                 tree phires = gimple_phi_result (phi);
+                 tree temp = NULL_TREE;
+
+                 /* Add # DEBUG D#1 => arg != carg ? arg : oarg.  */
+                 FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, phires)
+                   {
+                     if (!is_gimple_debug (use_stmt))
+                       continue;
+                     if (temp == NULL_TREE)
+                       {
+                         gimple_stmt_iterator gsi
+                           = gsi_after_labels (gimple_bb (phi));
+                         tree type = TREE_TYPE (phires);
+                         temp = build_debug_expr_decl (type);
+                         tree t = build2 (NE_EXPR, boolean_type_node,
+                                          arg, carg);
+                         t = build3 (COND_EXPR, type, t, arg, oarg);
+                         gimple *g = gimple_build_debug_bind (temp, t, phi);
+                         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+                       }
+                     FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter)
+                       replace_exp (use_p, temp);
+                     update_stmt (use_stmt);
+                   }
+               }
+           }
+         if (equal_p)
+           {
+             replace_phi_edge_with_variable (cond_bb, e1, phi, arg);
+             /* Note that we optimized this PHI.  */
+             return 2;
+           }
        }
-      else
+      else if (equal_p)
        {
          if (!single_pred_p (middle_bb))
            return 0;