https://gcc.gnu.org/g:9b4df7d052915431725e58156754dfb077b915c1

commit r17-5080-g9b4df7d052915431725e58156754dfb077b915c1
Author: Richard Biener <[email protected]>
Date:   Mon Oct 12 09:17:01 2026 +0200

    tree-optimization/127814 - niter assumptions / may_be_zero and UB
    
    The following avoids UB and following miscompiles via ranger when
    vectorizing loops that have niter assumptions and/or may_be_zero
    conditions.  We end up emitting those in non-short-circuited form
    during versioning and the least intrusive change, suitable for
    backporting, seems to be to make sure to emit the assumptions first
    and rewrite them to defined operations.  The optimization to put
    those into a separate block, short-circuiting other versioning
    conditions should shield those.  There is existing attempts at
    mitigating issues with regard to the may_be_zero condition, but
    that's guarding only against -ftrapv, so there's still a hole
    for that case.
    
            PR tree-optimization/127814
            * tree-vect-loop-manip.cc (vect_loop_versioning): Emit
            versioning for niter assumption conditions first and
            split those to the separate BB together with the cost model
            check after rewriting all of it to unconditional defined operations.
    
            * gcc.dg/vect/vect-pr127814.c: New testcase.

Diff:
---
 gcc/testsuite/gcc.dg/vect/vect-pr127814.c | 33 +++++++++++++++
 gcc/tree-vect-loop-manip.cc               | 69 +++++++++++++------------------
 2 files changed, 62 insertions(+), 40 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/vect/vect-pr127814.c 
b/gcc/testsuite/gcc.dg/vect/vect-pr127814.c
new file mode 100644
index 000000000000..1687800dbf7d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-pr127814.c
@@ -0,0 +1,33 @@
+/* { dg-additional-options "-O3" } */
+
+#include "tree-vect.h"
+
+int bf;
+
+__attribute__((noipa))
+int get_bf () { return 5; }
+
+__attribute__((noipa))
+void test (int x)
+{
+  int cb = -996736171;
+  if (x == 0)
+    cb = 0;
+  bf = get_bf ();
+  while (1)
+    {
+      bf -= 2;
+      if (-520093677 * bf >= cb)
+       break;
+    }
+}
+
+int main ()
+{
+  check_vect ();
+
+  test (1);
+  if (bf != 1)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index 750b369ae6d0..62bd213692f2 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -4147,38 +4147,6 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, 
tree nitersm1,
   return vect_epilogues ? epilog : NULL;
 }
 
-/* Function vect_create_cond_for_niters_checks.
-
-   Create a conditional expression that represents the run-time checks for
-   loop's niter.  The loop is guaranteed to terminate if the run-time
-   checks hold.
-
-   Input:
-   COND_EXPR  - input conditional expression.  New conditions will be chained
-               with logical AND operation.  If it is NULL, then the function
-               is used to return the number of alias checks.
-   LOOP_VINFO - field LOOP_VINFO_MAY_ALIAS_STMTS contains the list of ddrs
-               to be checked.
-
-   Output:
-   COND_EXPR - conditional expression.
-
-   The returned COND_EXPR is the conditional expression to be used in the
-   if statement that controls which version of the loop gets executed at
-   runtime.  */
-
-static void
-vect_create_cond_for_niters_checks (loop_vec_info loop_vinfo, tree *cond_expr)
-{
-  tree part_cond_expr = LOOP_VINFO_NITERS_ASSUMPTIONS (loop_vinfo);
-
-  if (*cond_expr)
-    *cond_expr = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
-                             *cond_expr, part_cond_expr);
-  else
-    *cond_expr = part_cond_expr;
-}
-
 /* Set *COND_EXPR to a tree that is true when both the original *COND_EXPR
    and PART_COND_EXPR are true.  Treat a null *COND_EXPR as "true".  */
 
@@ -4534,7 +4502,6 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
   edge new_exit_e, e;
   gphi *orig_phi, *new_phi;
   tree cond_expr = NULL_TREE;
-  gimple_seq cond_expr_stmt_list = NULL;
   tree arg;
   profile_probability prob = profile_probability::likely ();
   gimple_seq gimplify_stmt_list = NULL;
@@ -4550,11 +4517,27 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
   unsigned th = LOOP_VINFO_COST_MODEL_THRESHOLD (loop_vinfo);
   bool uncounted_p = LOOP_VINFO_NITERS_UNCOUNTED_P (loop_vinfo);
 
-  if (!uncounted_p && vect_apply_runtime_profitability_check_p (loop_vinfo)
+  /* Start with the niter assumptions so they are split out together
+     with the cost model check, short-circuiting all other checks
+     since niter information is not valid when the assumptions do not
+     hold.  */
+  if (version_niter)
+    cond_expr = LOOP_VINFO_NITERS_ASSUMPTIONS (loop_vinfo);
+
+  if (!uncounted_p
+      && vect_apply_runtime_profitability_check_p (loop_vinfo)
       && !ordered_p (th, versioning_threshold))
-    cond_expr = fold_build2 (GE_EXPR, boolean_type_node, scalar_loop_iters,
-                            build_int_cst (TREE_TYPE (scalar_loop_iters),
-                                           th - 1));
+    {
+      tree expr = fold_build2 (GE_EXPR, boolean_type_node, scalar_loop_iters,
+                              build_int_cst (TREE_TYPE (scalar_loop_iters),
+                                             th - 1));
+      if (cond_expr)
+       cond_expr = fold_build2 (BIT_AND_EXPR, boolean_type_node,
+                                expr, cond_expr);
+      else
+       cond_expr = expr;
+    }
+
   if (!uncounted_p && maybe_ne (versioning_threshold, 0U))
     {
       tree expr = fold_build2 (GE_EXPR, boolean_type_node, scalar_loop_iters,
@@ -4568,6 +4551,7 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
     }
 
   tree cost_name = NULL_TREE;
+  gimple_seq cond_expr_stmt_list = NULL;
   profile_probability prob2 = profile_probability::always ();
   if (cond_expr
       && EXPR_P (cond_expr)
@@ -4582,11 +4566,16 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       /* Split prob () into two so that the overall probability of passing
         both the cost-model and versioning checks is the orig prob.  */
       prob2 = prob = prob.sqrt ();
+      /* Rewrite to defined if there was an niter versioning condition.
+        ???  We fail to be more selective with respect to niter may_be_zero
+        and assumptions.  */
+      if (version_niter)
+       for (auto gsi = gsi_start (cond_expr_stmt_list); !gsi_end_p (gsi);
+            gsi_next (&gsi))
+         if (gimple_needing_rewrite_undefined (gsi_stmt (gsi)))
+           rewrite_to_defined_unconditional (&gsi);
     }
 
-  if (version_niter)
-    vect_create_cond_for_niters_checks (loop_vinfo, &cond_expr);
-
   if (cond_expr)
     {
       gimple_seq tem = NULL;

Reply via email to