On Wed, 30 Sep 2026, Richard Sandiford wrote:

> Dominic P <[email protected]> writes:
> > Without partial vectors, the vector IV step (VF) and the skip-vector bound
> > (VF - 1, plus 1 when peeling for gaps) are built as constants of the NITERS
> > type.  With an 8-bit loop counter and a VF of [16, 16], a 2048-bit SVE
> > vector makes the VF 256 == 0: the guard NITERSM1 < VF never skips the
> > vector loop and the IV steps by 0, so the loop runs off the end of its
> > arrays.  niters_vf_2.c fails this way at plain -O3 (VF [32, 32] from
> > #pragma GCC unroll); GCC 16 is affected too.  Reject the loop_vinfo when
> > partial vectors are not used and the maximum VF does not fit the NITERS
> > type, bounding a variable VF with targetm.poly_int_indeterminate_bound
> > when the target has it.  On SVE this only affects unmasked loops with an
> > 8-bit counter; they now use a smaller VF.  Targets without the hook keep
> > vect_max_vf; on RISC-V this changes one rvv test, poly_licm-3.c, whose
> > 16-bit-counter loop now uses fixed-length vectors.
> 
> Sorry if this has already been suggested, but: rather than make this a
> reason to fail vectorisation, why not change the type to a pointer-sized
> integer type in cases where we cannot prove that the original type is
> small enough?  Sub-32-bit counters aren't as efficient as 32-bit or
> 64-bit counters on AArch64 in any case.

Yeah, I think we probably should up the IV the vectorizer adds to
either integer_type_node or an integer type corresponding to word_mode.
(I see create_canonical_iv also simply uses the niter type)

But I'll also note that there's code for determining the integer
type to use for the .WHILE_ULT comparison and there we already
widen the scalar iteration counter IIRC.  So I wonder why it still
breaks down.

> Thanks,
> Richard
> 
> 
> > Bootstrapped and regression tested on x86_64-pc-linux-gnu; regression
> > tested on aarch64-linux-gnu (qemu, VL 128 and SVE VL 2048).
> >
> > Assisted-by: Claude Opus 5 (Anthropic)
> >
> > gcc/ChangeLog:
> >
> >     * tree-vect-loop.cc (vect_determine_partial_vectors_and_peeling):
> >     Fail if the maximum VF does not fit the type of NITERS and partial
> >     vectors are not used.
> >
> > gcc/testsuite/ChangeLog:
> >
> >     * gcc.target/aarch64/sve/niters_vf_1.c: New test.
> >     * gcc.target/aarch64/sve/niters_vf_1_run.c: New test.
> >     * gcc.target/aarch64/sve/niters_vf_2.c: New test.
> >
> > Signed-off-by: Dominic P <[email protected]>
> > ---
> > OK for trunk?
> >
> >  .../gcc.target/aarch64/sve/niters_vf_1.c      | 25 +++++++++++++++
> >  .../gcc.target/aarch64/sve/niters_vf_1_run.c  | 26 ++++++++++++++++
> >  .../gcc.target/aarch64/sve/niters_vf_2.c      | 31 +++++++++++++++++++
> >  gcc/tree-vect-loop.cc                         | 22 +++++++++++++
> >  4 files changed, 104 insertions(+)
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1_run.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/niters_vf_2.c
> >
> > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1.c 
> > b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1.c
> > new file mode 100644
> > index 00000000000..ff510ddf192
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O3 -fno-vect-cost-model --param 
> > vect-partial-vector-usage=0 -fdump-tree-vect-details" } */
> > +
> > +#include <stdint.h>
> > +
> > +uint8_t in[1024];
> > +uint8_t out[256];
> > +
> > +uint32_t __attribute__ ((noipa))
> > +f1 (int n)
> > +{
> > +  uint32_t r = 7;
> > +  for (uint8_t i = 0; i < (uint8_t) n; i++)
> > +    r += in[i * 4 + 1];
> > +  return r;
> > +}
> > +
> > +void __attribute__ ((noipa))
> > +f2 (int n)
> > +{
> > +  for (uint8_t i = 0; i < (uint8_t) n; i++)
> > +    out[i] = in[i * 2] + 1;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump "vectorization factor may not fit" "vect" } 
> > } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1_run.c 
> > b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1_run.c
> > new file mode 100644
> > index 00000000000..144be94cf6c
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_1_run.c
> > @@ -0,0 +1,26 @@
> > +/* { dg-do run { target aarch64_sve_hw } } */
> > +/* { dg-options "-O3 -fno-vect-cost-model --param 
> > vect-partial-vector-usage=0" } */
> > +
> > +#include "niters_vf_1.c"
> > +
> > +int __attribute__ ((optimize (1)))
> > +main (void)
> > +{
> > +  for (int i = 0; i < 1024; i++)
> > +    in[i] = i * 7;
> > +  for (int n = 0; n < 256; n++)
> > +    {
> > +      uint32_t r = 7;
> > +      for (int i = 0; i < n; i++)
> > +   r += in[i * 4 + 1];
> > +      if (f1 (n) != r)
> > +   __builtin_abort ();
> > +      for (int i = 0; i < 256; i++)
> > +   out[i] = 0;
> > +      f2 (n);
> > +      for (int i = 0; i < 256; i++)
> > +   if (out[i] != (i < n ? (uint8_t) (in[i * 2] + 1) : 0))
> > +     __builtin_abort ();
> > +    }
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_2.c 
> > b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_2.c
> > new file mode 100644
> > index 00000000000..f5c6dddc726
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/sve/niters_vf_2.c
> > @@ -0,0 +1,31 @@
> > +/* { dg-do run { target aarch64_sve_hw } } */
> > +/* { dg-options "-O3" } */
> > +
> > +#include <stdint.h>
> > +
> > +uint8_t c[512];
> > +
> > +void __attribute__ ((noipa))
> > +f (uint8_t n)
> > +{
> > +  uint8_t i = 0;
> > +#pragma GCC unroll 32
> > +  do
> > +    c[i] += 1;
> > +  while (i++ != n);
> > +}
> > +
> > +int __attribute__ ((optimize (1)))
> > +main (void)
> > +{
> > +  for (int n = 0; n < 256; n++)
> > +    {
> > +      for (int i = 0; i < 512; i++)
> > +   c[i] = 0;
> > +      f (n);
> > +      for (int i = 0; i < 512; i++)
> > +   if (c[i] != (i <= n))
> > +     __builtin_abort ();
> > +    }
> > +  return 0;
> > +}
> > diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
> > index 9b2afad6034..763c5800ef5 100644
> > --- a/gcc/tree-vect-loop.cc
> > +++ b/gcc/tree-vect-loop.cc
> > @@ -2126,6 +2126,28 @@ vect_determine_partial_vectors_and_peeling 
> > (loop_vec_info loop_vinfo,
> >                                "not vectorized: loop needs but cannot "
> >                                "use partial vectors\n");
> >  
> > +  /* Without partial vectors the IV step and the epilogue bound are
> > +     built in the type of NITERS, so they must be able to hold the VF.  */
> > +  if (!LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo)
> > +      && !LOOP_VINFO_NITERS_UNCOUNTED_P (loop_vinfo))
> > +    {
> > +      poly_uint64 vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
> > +      widest_int max_vf = vect_max_vf (loop_vinfo);
> > +      if (targetm.poly_int_indeterminate_bound)
> > +   {
> > +     poly_uint64 bound = targetm.poly_int_indeterminate_bound ();
> > +     max_vf = vf.coeffs[0];
> > +     for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
> > +       max_vf += widest_int (vf.coeffs[i]) * bound.coeffs[i];
> > +   }
> > +      if (wi::min_precision (max_vf, UNSIGNED)
> > +     > TYPE_PRECISION (TREE_TYPE (LOOP_VINFO_NITERS (loop_vinfo))))
> > +   return opt_result::failure_at (vect_location,
> > +                                  "not vectorized: vectorization factor "
> > +                                  "may not fit the type of the iteration "
> > +                                  "count\n");
> > +    }
> > +
> >    if (dump_enabled_p ())
> >      dump_printf_loc (MSG_NOTE, vect_location,
> >                  "operating on %s vectors%s.\n",
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to