The following adds a heuristic to ifcombine that avoids turning
analyzable loop exits into unanalyzable ones.  This allows vectorizing
the testcase in the PR again.  I've refrained from actually
analyzing niters but instead used a cheaper heuristic.  I believe
we'll only ever attempt to combine two ifs if they are in the same
loop and if either both exit the loop or stay within.

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

        PR tree-optimization/107690
        * tree-ssa-ifcombine.cc (ifcombine_ifandif): Do not merge
        possibly analyzable exit conditions.

        * g++.dg/vect/vect-pr107690.cc: New testcase.
---
 gcc/testsuite/g++.dg/vect/vect-pr107690.cc | 16 ++++++++++++++++
 gcc/tree-ssa-ifcombine.cc                  | 16 ++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/vect/vect-pr107690.cc

diff --git a/gcc/testsuite/g++.dg/vect/vect-pr107690.cc 
b/gcc/testsuite/g++.dg/vect/vect-pr107690.cc
new file mode 100644
index 00000000000..847dbca5f62
--- /dev/null
+++ b/gcc/testsuite/g++.dg/vect/vect-pr107690.cc
@@ -0,0 +1,16 @@
+// { dg-do compile }
+// { dg-require-effective-target vect_int }
+// { dg-additional-options "-std=gnu++20" }
+
+#include <algorithm>
+#include <array>
+#include <ranges>
+
+std::array<int, 16> foo(std::array<int, 16> u, std::array<int, 16> const &v)
+{
+  std::ranges::transform(u, v, u.begin(), std::plus<int>());
+  return u;
+}
+
+// { dg-final { scan-tree-dump "optimized: loop vectorized" "vect" } }
+// { dg-final { scan-tree-dump-not "Loop being analyzed as uncounted" "vect" } 
}
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index 7769118b9f6..a60e6914e68 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "bitmap.h"
+#include "cfgloop.h"
 
 #ifndef LOGICAL_OP_NON_SHORT_CIRCUIT
 #define LOGICAL_OP_NON_SHORT_CIRCUIT \
@@ -818,6 +819,21 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool 
inner_inv,
   if (!outer_cond)
     return false;
 
+  /* niter analysis does not cope with boolean typed loop exit conditions.
+     Avoid turning an analyzable exit into an unanalyzable one.  */
+  if (inner_cond_bb->loop_father == outer_cond_bb->loop_father
+      && loop_exits_from_bb_p (inner_cond_bb->loop_father, inner_cond_bb)
+      && loop_exits_from_bb_p (outer_cond_bb->loop_father, outer_cond_bb))
+    {
+      tree outer_type = TREE_TYPE (gimple_cond_lhs (outer_cond));
+      tree inner_type = TREE_TYPE (gimple_cond_lhs (inner_cond));
+      if (TREE_CODE (outer_type) == INTEGER_TYPE
+         || POINTER_TYPE_P (outer_type)
+         || TREE_CODE (inner_type) == INTEGER_TYPE
+         || POINTER_TYPE_P (inner_type))
+       return false;
+    }
+
   /* See if we test a single bit of the same name in both tests.  In
      that case remove the outer test, merging both else edges,
      and change the inner one to test for
-- 
2.51.0

Reply via email to