https://gcc.gnu.org/g:44c0f794dd4129f279ca9010955095307b981b55

commit r17-5766-g44c0f794dd4129f279ca9010955095307b981b55
Author: Richard Biener <[email protected]>
Date:   Fri Oct 30 15:12:31 2026 +0100

    tree-optimization/128267 - fix IV update of vect_step_op_sh{lr}
    
    The IV update for the scalar epilog is prone to overflow since
    the validity check is performed in the IVs type.  Perform the
    check with widest_int instead.  Disallow the early-exit case
    which has non-constant skip_niters, but add (LLM generated)
    test coverage to all relevant cases.
    
    Assisted-By: Claude Opus 5.5
    
            PR tree-optimization/128267
            * tree-vect-loop.cc (vect_peel_nonlinear_iv_init): For
            vect_step_op_sh{lr} check whether the update shift is >= prec
            using widest_int.
            * tree-vect-loop-manip.cc (vect_can_peel_nonlinear_iv_p): Disallow
            early break for vect_step_op_sh{lr} as well.
    
            * gcc.dg/vect/vect-pr128267-1.c: New testcase.
            * gcc.dg/vect/vect-pr128267-2.c: Likewise.
            * gcc.dg/vect/vect-early-break_147-pr128267.c: Likewise.
            * gcc.dg/vect/vect-early-break_148-pr128267.c: Likewise.

Diff:
---
 .../gcc.dg/vect/vect-early-break_147-pr128267.c    | 71 ++++++++++++++++++++
 .../gcc.dg/vect/vect-early-break_148-pr128267.c    | 76 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/vect/vect-pr128267-1.c        | 22 +++++++
 gcc/testsuite/gcc.dg/vect/vect-pr128267-2.c        | 22 +++++++
 gcc/tree-vect-loop-manip.cc                        | 10 ++-
 gcc/tree-vect-loop.cc                              | 20 +++---
 6 files changed, 210 insertions(+), 11 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_147-pr128267.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_147-pr128267.c
new file mode 100644
index 000000000000..a804c549e01b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_147-pr128267.c
@@ -0,0 +1,71 @@
+/* PR tree-optimization/128267 */
+/* { dg-do run } */
+/* { dg-add-options vect_early_break } */
+/* { dg-require-effective-target vect_early_break_hw } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3" } */
+/* { dg-additional-options "-mavx2" { target i?86-*-* x86_64-*-* } } */
+
+/* The value of a shift IV after an early exit has to be computed
+   correctly when the accumulated shift amount reaches the precision
+   of the IV.  This requires different handling for logical right
+   shifts and left shifts (result 0) and arithmetic right shifts
+   (result is the sign of the initial value).  */
+
+#include "tree-vect.h"
+
+#define N 1000
+
+unsigned a[N];
+
+#define TEST(NAME, T, OP)                              \
+T b_##NAME[N];                                         \
+                                                       \
+__attribute__((noipa)) T                               \
+NAME (T x)                                             \
+{                                                      \
+  T v = x;                                             \
+  for (int i = 0; i < N; i++)                          \
+    {                                                  \
+      if (a[i] == 42)                                  \
+       return v;                                       \
+      b_##NAME[i] = v;                                 \
+      v OP 1;                                          \
+    }                                                  \
+  return v;                                            \
+}                                                      \
+                                                       \
+__attribute__((noipa)) T                               \
+NAME##_ref (T x, int k)                                        \
+{                                                      \
+  T v = x;                                             \
+_Pragma ("GCC novector")                               \
+  for (int i = 0; i < k; i++)                          \
+    v OP 1;                                            \
+  return v;                                            \
+}
+
+TEST (lshr, unsigned, >>=)
+TEST (ashr, int, >>=)
+TEST (shl, unsigned, <<=)
+
+int main ()
+{
+  check_vect ();
+
+#pragma GCC novector
+  for (int k = 0; k < N; k++)
+    {
+      a[k] = 42;
+      if (lshr (0xffffffffu) != lshr_ref (0xffffffffu, k))
+       __builtin_abort ();
+      if (ashr (-__INT_MAX__ - 1) != ashr_ref (-__INT_MAX__ - 1, k))
+       __builtin_abort ();
+      if (ashr (__INT_MAX__) != ashr_ref (__INT_MAX__, k))
+       __builtin_abort ();
+      if (shl (0xffffffffu) != shl_ref (0xffffffffu, k))
+       __builtin_abort ();
+      a[k] = 0;
+    }
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break_148-pr128267.c 
b/gcc/testsuite/gcc.dg/vect/vect-early-break_148-pr128267.c
new file mode 100644
index 000000000000..1cf71211eaf9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-early-break_148-pr128267.c
@@ -0,0 +1,76 @@
+/* PR tree-optimization/128267 */
+/* { dg-do run } */
+/* { dg-add-options vect_early_break } */
+/* { dg-require-effective-target vect_early_break_hw } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target i?86-*-* 
x86_64-*-* } } */
+
+/* Like vect-early-break_147-pr128267.c but with 16-bit IVs where in
+   addition the number of iterations does not fit in the IV type and
+   has to be handled before converting it to that type.  */
+
+#include "tree-vect.h"
+
+#define N 0x10040
+
+unsigned short a[N];
+
+#define TEST(NAME, T, OP)                              \
+T b_##NAME[N];                                         \
+                                                       \
+__attribute__((noipa)) T                               \
+NAME (T x)                                             \
+{                                                      \
+  T v = x;                                             \
+  for (int i = 0; i < N; i++)                          \
+    {                                                  \
+      if (a[i] == 42)                                  \
+       return v;                                       \
+      b_##NAME[i] = v;                                 \
+      v OP 1;                                          \
+    }                                                  \
+  return v;                                            \
+}                                                      \
+                                                       \
+__attribute__((noipa)) T                               \
+NAME##_ref (T x, int k)                                        \
+{                                                      \
+  T v = x;                                             \
+_Pragma ("GCC novector")                               \
+  for (int i = 0; i < k; i++)                          \
+    v OP 1;                                            \
+  return v;                                            \
+}
+
+TEST (lshr, unsigned short, >>=)
+TEST (ashr, short, >>=)
+TEST (shl, unsigned short, <<=)
+
+static void
+check (int k)
+{
+  a[k] = 42;
+  if (lshr (0xffff) != lshr_ref (0xffff, k))
+    __builtin_abort ();
+  if (ashr (-0x8000) != ashr_ref (-0x8000, k))
+    __builtin_abort ();
+  if (ashr (0x7fff) != ashr_ref (0x7fff, k))
+    __builtin_abort ();
+  if (shl (0xffff) != shl_ref (0xffff, k))
+    __builtin_abort ();
+  a[k] = 0;
+}
+
+int main ()
+{
+  check_vect ();
+
+#pragma GCC novector
+  for (int k = 0; k < 0x40; k++)
+    check (k);
+#pragma GCC novector
+  for (int k = 0x10000 - 0x10; k < N; k++)
+    check (k);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-pr128267-1.c 
b/gcc/testsuite/gcc.dg/vect/vect-pr128267-1.c
new file mode 100644
index 000000000000..a721df12c2a9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-pr128267-1.c
@@ -0,0 +1,22 @@
+#include "tree-vect.h"
+
+__attribute__((noipa)) void
+foo(signed char *a)
+{
+  signed char b = 64;
+  for (unsigned i = 0; i < 257; ++i)
+    {
+      a[i] = b;
+      b >>= 1;
+    }
+}
+
+int main(void)
+{
+  signed char a[257];
+  check_vect ();
+  foo(a);
+  if (a[256] != 0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-pr128267-2.c 
b/gcc/testsuite/gcc.dg/vect/vect-pr128267-2.c
new file mode 100644
index 000000000000..bcfb9e40c086
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-pr128267-2.c
@@ -0,0 +1,22 @@
+#include "tree-vect.h"
+
+signed short array[0x10001];
+
+__attribute__((noipa)) void
+foo(signed short *a)
+{
+  signed short b = 64;
+  for (unsigned i = 0; i < 0x10001; ++i) {
+    a[i] = b;
+    b >>= 1;
+  }
+}
+
+int main(void)
+{
+  check_vect ();
+  foo(array);
+  if (array[0x10000] != 0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index e1a1c5c1dd1c..6b6f11a25a4d 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -2321,12 +2321,16 @@ vect_can_peel_nonlinear_iv_p (loop_vec_info loop_vinfo,
     }
 
   if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)
-      && induction_type == vect_step_op_mul)
+      && (induction_type == vect_step_op_mul
+         || induction_type == vect_step_op_shl
+         || induction_type == vect_step_op_shr))
     {
       if (dump_enabled_p ())
        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                        "Peeling for is not supported for nonlinear mult"
-                        " induction using partial vectorization.\n");
+                        "Peeling for is not supported for nonlinear %s"
+                        " induction using partial vectorization.\n",
+                        induction_type == vect_step_op_mul
+                        ? "mult" : "shift");
       return false;
     }
 
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index c8662e4b2981..07fef37f320f 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -9156,15 +9156,12 @@ vect_peel_nonlinear_iv_init (gimple_seq* stmts, tree 
init_expr,
 
     case vect_step_op_shr:
     case vect_step_op_shl:
-      skip_niters = fold_build1 (NOP_EXPR, type, skip_niters);
-      step_expr = fold_build1 (NOP_EXPR, type, step_expr);
-      step_expr = fold_build2 (MULT_EXPR, type, step_expr, skip_niters);
-      /* When shift mount >= precision, need to avoid UD.
+      /* When shift count >= precision, need to avoid UD.
         In the original loop, there's no UD, and according to semantic,
         init_expr should be 0 for lshr, ashl, and >>= (prec - 1) for ashr.  */
-      if ((!tree_fits_uhwi_p (step_expr)
-         || tree_to_uhwi (step_expr) >= prec)
-         && !early_exit_p)
+      if (!early_exit_p
+         && wi::mul (wi::to_widest (step_expr),
+                     wi::to_widest (skip_niters)) >= prec)
        {
          if (induction_type == vect_step_op_shl
              || TYPE_UNSIGNED (type))
@@ -9176,8 +9173,15 @@ vect_peel_nonlinear_iv_init (gimple_seq* stmts, tree 
init_expr,
        }
       else
        {
+         /* ???  For not constant skip_niters we'd have to treat the
+            overflow case.  */
+         gcc_assert (!early_exit_p);
+         step_expr = fold_build2 (MULT_EXPR, TREE_TYPE (skip_niters),
+                                  skip_niters,
+                                  fold_convert (TREE_TYPE (skip_niters),
+                                                step_expr));
          init_expr = fold_build2 ((induction_type == vect_step_op_shr
-                                         ? RSHIFT_EXPR : LSHIFT_EXPR),
+                                   ? RSHIFT_EXPR : LSHIFT_EXPR),
                                    type, init_expr, step_expr);
          init_expr = force_gimple_operand (init_expr, stmts, false, NULL);
        }

Reply via email to