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.
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",
--
2.55.0