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); }
