The PR shows that when using std::clamp from the C++ standard library
and there is surrounding code using exceptions then phiprop can fail
to simplify the code so phiopt can turn the clamping into efficient
min/max operations.

The validation code is needlessly complicated, steming from the
time we had memory-SSA with multiple virtual operands.  The following
simplifies this, thereby fixing this issue.

Bootstrapped and tested on x86_64-unknown-linux-gnu, pushed.

        PR tree-optimization/117965
        * tree-ssa-phiprop.cc (phivn_valid_p): Remove.
        (propagate_with_phi): Pass in virtual PHI node from BB,
        rewrite load motion validity check to require the same
        virtual use along all paths.

        * g++.dg/tree-ssa/pr117965-1.C: New testcase.
        * g++.dg/tree-ssa/pr117965-2.C: Likewise.
---
 gcc/testsuite/g++.dg/tree-ssa/pr117965-1.C | 28 +++++++
 gcc/testsuite/g++.dg/tree-ssa/pr117965-2.C | 19 +++++
 gcc/tree-ssa-phiprop.cc                    | 96 +++++++++++-----------
 3 files changed, 97 insertions(+), 46 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/tree-ssa/pr117965-1.C
 create mode 100644 gcc/testsuite/g++.dg/tree-ssa/pr117965-2.C

diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr117965-1.C 
b/gcc/testsuite/g++.dg/tree-ssa/pr117965-1.C
new file mode 100644
index 00000000000..84f0f2b75df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr117965-1.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-phiopt1" }
+
+void f();
+void f(int&);
+
+static inline const int &
+clamp(const int &v, const int &min, const int &max)
+{
+  const int &t = (v > min) ? v : min;
+  return t > max ? max : t;
+}
+
+void clamp2(int num1) {
+  try {
+  int low = -12, high = 12;
+  f();
+  num1 = clamp(num1, low, high);
+  f(num1);
+  } catch(...)
+  {
+       __builtin_printf("caught.\n");
+        return;
+  }
+}
+
+// { dg-final { scan-tree-dump-times "MAX" 1 "phiopt1" } } */
+// { dg-final { scan-tree-dump-times "MIN" 1 "phiopt1" } } */
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr117965-2.C 
b/gcc/testsuite/g++.dg/tree-ssa/pr117965-2.C
new file mode 100644
index 00000000000..3e94fb3c05c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr117965-2.C
@@ -0,0 +1,19 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-O2 -fdump-tree-phiopt1" }
+
+#include <iostream>
+#include <algorithm>
+
+void clamp2 ()
+{
+  float low = -1.0f, high = 1.0f;
+  float num1, num2, num3;
+  std::cin >> num1;
+  num1 = std::clamp(num1, low, high);
+  std::cout << num1;
+}
+
+// { dg-final { scan-tree-dump-times " < -1.0" 1 "phiopt1" } }
+// { dg-final { scan-tree-dump-times " \\\? -1.0e\\\+0 : " 1 "phiopt1" } }
+// { dg-final { scan-tree-dump-times " > 1.0" 1 "phiopt1" } }
+// { dg-final { scan-tree-dump-times " \\\? 1.0e\\\+0 : " 1 "phiopt1" } }
diff --git a/gcc/tree-ssa-phiprop.cc b/gcc/tree-ssa-phiprop.cc
index a2e1fb16a30..897bd583ea7 100644
--- a/gcc/tree-ssa-phiprop.cc
+++ b/gcc/tree-ssa-phiprop.cc
@@ -99,35 +99,6 @@ struct phiprop_d
   tree vuse;
 };
 
-/* Verify if the value recorded for NAME in PHIVN is still valid at
-   the start of basic block BB.  */
-
-static bool
-phivn_valid_p (struct phiprop_d *phivn, tree name, basic_block bb)
-{
-  tree vuse = phivn[SSA_NAME_VERSION (name)].vuse;
-  gimple *use_stmt;
-  imm_use_iterator ui2;
-  bool ok = true;
-
-  /* The def stmts of the virtual uses need to be dominated by bb.  */
-  gcc_assert (vuse != NULL_TREE);
-
-  FOR_EACH_IMM_USE_STMT (use_stmt, ui2, vuse)
-    {
-      /* If BB does not dominate a VDEF, the value is invalid.  */
-      if ((gimple_vdef (use_stmt) != NULL_TREE
-          || gimple_code (use_stmt) == GIMPLE_PHI)
-         && !dominated_by_p (CDI_DOMINATORS, gimple_bb (use_stmt), bb))
-       {
-         ok = false;
-         break;
-       }
-    }
-
-  return ok;
-}
-
 /* Insert a new phi node for the dereference of PHI at basic_block
    BB with the virtual operands from USE_STMT.  */
 
@@ -275,12 +246,13 @@ chk_uses (tree, tree *idx, void *data)
       <Lx>:;
    Returns true if a transformation was done and edge insertions
    need to be committed.  Global data PHIVN and N is used to track
-   past transformation results.  We need to be especially careful here
+   past transformation results.  VPHI is the virtual PHI node in BB
+   if there is one.  We need to be especially careful here
    with aliasing issues as we are moving memory reads.  */
 
 static bool
-propagate_with_phi (basic_block bb, gphi *phi, struct phiprop_d *phivn,
-                   size_t n, bitmap dce_ssa_names)
+propagate_with_phi (basic_block bb, gphi *vphi, gphi *phi,
+                   struct phiprop_d *phivn, size_t n, bitmap dce_ssa_names)
 {
   tree ptr = PHI_RESULT (phi);
   gimple *use_stmt;
@@ -298,6 +270,7 @@ propagate_with_phi (basic_block bb, gphi *phi, struct 
phiprop_d *phivn,
          && TYPE_MODE (TREE_TYPE (TREE_TYPE (ptr))) == BLKmode))
     return false;
 
+  tree up_vuse = NULL_TREE;
   /* Check if we can "cheaply" dereference all phi arguments.  */
   FOR_EACH_PHI_ARG (arg_p, phi, i, SSA_OP_USE)
     {
@@ -315,14 +288,28 @@ propagate_with_phi (basic_block bb, gphi *phi, struct 
phiprop_d *phivn,
            return false;
          arg = gimple_assign_rhs1 (def_stmt);
        }
-      if (TREE_CODE (arg) != ADDR_EXPR
-         && !(TREE_CODE (arg) == SSA_NAME
+      if (TREE_CODE (arg) == ADDR_EXPR)
+       ;
+      /* When we have an SSA name see if we previously encountered a
+        dereference of it.  */
+      else if (TREE_CODE (arg) == SSA_NAME
               && SSA_NAME_VERSION (arg) < n
               && phivn[SSA_NAME_VERSION (arg)].value != NULL_TREE
               && (!type
                   || types_compatible_p
-                      (type, TREE_TYPE (phivn[SSA_NAME_VERSION (arg)].value)))
-              && phivn_valid_p (phivn, arg, bb)))
+                      (type, TREE_TYPE (phivn[SSA_NAME_VERSION (arg)].value))))
+       {
+         /* The dereference should be under the VUSE that's active in BB.
+            If the BB has no virtual PHI then record the common "incoming"
+            vuse.  */
+         if (vphi)
+           up_vuse = gimple_phi_arg_def (vphi, phi_arg_index_from_use (arg_p));
+         if (!up_vuse)
+           up_vuse = phivn[SSA_NAME_VERSION (arg)].vuse;
+         else if (up_vuse != phivn[SSA_NAME_VERSION (arg)].vuse)
+           return false;
+       }
+      else
        return false;
       if (!type
          && TREE_CODE (arg) == SSA_NAME)
@@ -372,17 +359,32 @@ propagate_with_phi (basic_block bb, gphi *phi, struct 
phiprop_d *phivn,
            && !gimple_has_volatile_ops (use_stmt)))
        continue;
 
-      /* Check if we can move the loads.  The def stmt of the virtual use
-        needs to be in a different basic block dominating bb.  When the
+      /* Check if we can move the loads.  This is when the virtual use
+        is the same as the one active at the start of BB which we know
+        either from its virtual PHI def or from the common incoming
+        VUSE.  If neither is present make sure the def stmt of the virtual
+        use is in a different basic block dominating BB.  When the
         def is an edge-inserted one we know it dominates us.  */
       vuse = gimple_vuse (use_stmt);
-      def_stmt = SSA_NAME_DEF_STMT (vuse);
-      if (!SSA_NAME_IS_DEFAULT_DEF (vuse)
-         && (gimple_bb (def_stmt) == bb
-             || (gimple_bb (def_stmt)
-                 && !dominated_by_p (CDI_DOMINATORS,
-                                     bb, gimple_bb (def_stmt)))))
-       goto next;
+      if (vphi)
+       {
+         if (vuse != gimple_phi_result (vphi))
+           goto next;
+       }
+      else if (up_vuse)
+       {
+         if (vuse != up_vuse)
+           goto next;
+       }
+      else
+       {
+         def_stmt = SSA_NAME_DEF_STMT (vuse);
+         if (!SSA_NAME_IS_DEFAULT_DEF (vuse)
+             && (gimple_bb (def_stmt) == bb
+                 || !dominated_by_p (CDI_DOMINATORS,
+                                     bb, gimple_bb (def_stmt))))
+           goto next;
+       }
 
       /* Found a proper dereference with an aggregate copy.  Just
          insert aggregate copies on the edges instead.  */
@@ -535,8 +537,10 @@ pass_phiprop::execute (function *fun)
          edges avoid blocks with abnormal predecessors.  */
       if (bb_has_abnormal_pred (bb))
        continue;
+      gphi *vphi = get_virtual_phi (bb);
       for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-       did_something |= propagate_with_phi (bb, gsi.phi (), phivn, n, 
dce_ssa_names);
+       did_something |= propagate_with_phi (bb, vphi, gsi.phi (),
+                                            phivn, n, dce_ssa_names);
     }
 
   if (did_something)
-- 
2.43.0

Reply via email to