Consider the following simple loop:
void foo (int *x)
{
for (int i = 0; i < 1000; i++)
x[i] *= 2;
}
compiled at -Ofast -march=armv8-a+sve generates
foo:
mov w1, 0
cntw x3
mov w2, 1000
whilelo p7.s, wzr, w2
.L2:
ld1w z31.s, p7/z, [x0, x1, lsl 2]
add z31.s, z31.s, z31.s
st1w z31.s, p7, [x0, x1, lsl 2]
add x1, x1, x3
whilelo p7.s, w1, w2
b.any .L2
ret
Which is nice, but the whilelo in the pre-header is unneeded. Due to the
architecturally defined minimum and maximum vector lengths[1] we know that at
any vector length the predicate is an all lanes active predicate, i.e. p7 is
always ptrue.
We can use gimple-isel these days while we still have range information on
the operands of .WHILE_ULTs to do this folding.
As such this patch folds whenever possible WHILE_ULTs into ptrue which are
cheaper to execute and so lowers our costs for entering the loops. i.e. the
above generates:
foo:
mov w1, 0
cntw x3
mov w2, 1000
ptrue p7.b, all
.L2:
ld1w z31.s, p7/z, [x0, x1, lsl 2]
add z31.s, z31.s, z31.s
st1w z31.s, p7, [x0, x1, lsl 2]
add x1, x1, x3
whilelo p7.s, w1, w2
b.any .L2
ret
[1] https://developer.arm.com/documentation/102476/0101/Introducing-SVE
Bootstrapped Regtested on aarch64-none-linux-gnu and no issues.
Any feedback? otherwise will commit early next week.
Thanks,
Tamar
gcc/ChangeLog:
* config/aarch64/aarch64.cc (aarch64_fold_while_ult_to_ptrue): New.
(aarch64_instruction_selection): Use it.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/sve/fmaxnm_2.c: Update output.
* gcc.target/aarch64/sve/fmaxnm_3.c: Likewise.
* gcc.target/aarch64/sve/fminnm_2.c: Likewise.
* gcc.target/aarch64/sve/fminnm_3.c: Likewise.
* gcc.target/aarch64/sve/slp_12.c: Likewise.
* gcc.target/aarch64/sve/unpacked_fadd_2.c: Likewise.
* gcc.target/aarch64/sve/unpacked_fmul_2.c: Likewise.
* gcc.target/aarch64/sve/unpacked_fsubr_2.c: Likewise.
* gfortran.dg/pr88833.f90: Likewise.
* gcc.target/aarch64/sve/while_ult_1.c: New test.
* gcc.target/aarch64/sve/while_ult_1_run.c: New test.
---
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index
7a81be8dcbe942ca113f7db2fbab4c357c40f462..630856f95281b4e2c290353b3b491404b5dfe633
100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -4442,15 +4442,87 @@ aarch64_fold_sve_ptrue_vl (tree vectype, unsigned int
vl,
return builder.build ();
}
+/* Fold a WHILE_ULT whose bounds are known enough to be expressed as a PTRUE.
+ Generic folding cannot do this because it cannot assume an architectural
+ maximum for a scalable vector. */
+
+static bool
+aarch64_fold_while_ult_to_ptrue (function *fun, gcall *call,
+ gimple_stmt_iterator *gsi)
+{
+ if (!gimple_call_internal_p (call, IFN_WHILE_ULT))
+ return false;
+
+ tree lhs = gimple_call_lhs (call);
+ if (!lhs)
+ return false;
+
+ tree lhs_type = TREE_TYPE (lhs);
+ if (!VECTOR_BOOLEAN_TYPE_P (lhs_type)
+ || !aarch64_sve_pred_mode_p (TYPE_MODE (lhs_type)))
+ return false;
+
+ int_range_max min, max;
+ range_query *query = get_range_query (fun);
+ if (!query->range_of_expr (min, gimple_call_arg (call, 0), call)
+ || !query->range_of_expr (max, gimple_call_arg (call, 1), call)
+ || min.undefined_p ()
+ || max.undefined_p ()
+ || !min.nonnegative_p ()
+ || !max.nonnegative_p ())
+ return false;
+
+ widest_int min_upper = widest_int::from (min.upper_bound (), UNSIGNED);
+ widest_int min_lower = widest_int::from (min.lower_bound (), UNSIGNED);
+ widest_int max_lower = widest_int::from (max.lower_bound (), UNSIGNED);
+ widest_int max_upper = widest_int::from (max.upper_bound (), UNSIGNED);
+ if (wi::leu_p (max_lower, min_upper))
+ return false;
+
+ unsigned int min_nelts
+ = constant_lower_bound (TYPE_VECTOR_SUBPARTS (lhs_type));
+ widest_int max_sve_nelts = min_nelts * 16;
+ widest_int min_gap = max_lower - min_upper;
+ widest_int max_gap = max_upper - min_lower;
+
+ tree pred_cst = NULL_TREE;
+ unsigned HOST_WIDE_INT gap = 0;
+ if (min_gap == max_gap
+ && wi::fits_uhwi_p (min_gap))
+ {
+ gap = min_gap.to_uhwi ();
+ machine_mode pred_mode = TYPE_MODE (lhs_type);
+ if (gap <= UINT_MAX
+ && (aarch64_svpattern_for_vl (pred_mode, (int) gap)
+ != AARCH64_NUM_SVPATTERNS))
+ pred_cst = aarch64_fold_sve_ptrue_vl (lhs_type,
+ (unsigned int) gap, 1);
+ }
+
+ if (!pred_cst)
+ {
+ if (wi::ltu_p (min_gap, max_sve_nelts))
+ return false;
+ pred_cst = build_all_ones_cst (lhs_type);
+ }
+
+ gassign *assign = gimple_build_assign (lhs, pred_cst);
+ gsi_replace (gsi, assign, false);
+ return true;
+}
+
/* Implement TARGET_INSTRUCTION_SELECTION. The target hook is used to
change generic sequences to a form AArch64 has an easier time expanding
instructions for. It's not supposed to be used for generic rewriting that
all targets would benefit from. */
static bool
-aarch64_instruction_selection (function * /* fun */, gimple_stmt_iterator *gsi)
+aarch64_instruction_selection (function *fun, gimple_stmt_iterator *gsi)
{
auto stmt = gsi_stmt (*gsi);
+ if (gcall *call = dyn_cast<gcall *> (stmt))
+ return aarch64_fold_while_ult_to_ptrue (fun, call, gsi);
+
gassign *assign = dyn_cast<gassign *> (stmt);
if (!assign)
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
index
ee3cdc20f96550ceeb1e6c11dd464c59eb4b3e6f..3f1dfdd60d238945e2c4fbf4676d3e943cfb3e30
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
@@ -16,7 +16,7 @@ f2 (double x, double *ptr)
return x;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.s,.*\tfmaxnm\tz[0-9]+\.s,
\1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.s,.*\tfmaxnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n}
} } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\ts[0-9]+, p[0-7],
z[0-9]+\.s\n} 1 } } */
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d,
\1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n}
} } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\td[0-9]+, p[0-7],
z[0-9]+\.d\n} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
index
a8eee0f4b2698f0425f55ea91c523e462ca134bd..2548e55f2841d370d5d30bce23350d3750f094a9
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
@@ -14,5 +14,5 @@ f (double *restrict res, double *restrict ptr)
res[1] = x1;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d,
\1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n}
} } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\td[0-9]+, p[0-7],
z[0-9]+\.d\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
index
10aced05f1afe69f6e646ac23bac8196c13f8357..ed657d58fdfe663f9ddf5b013aa73e14868dbe03
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
@@ -16,7 +16,7 @@ f2 (double x, double *ptr)
return x;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.s,.*\tfminnm\tz[0-9]+\.s,
\1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.s,.*\tfminnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n}
} } */
/* { dg-final { scan-assembler-times {\tfminnmv\ts[0-9]+, p[0-7],
z[0-9]+\.s\n} 1 } } */
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d,
\1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n}
} } */
/* { dg-final { scan-assembler-times {\tfminnmv\td[0-9]+, p[0-7],
z[0-9]+\.d\n} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
index
80ad01602491d07b338113f34d372b5f3970a3be..1f2fc76640fb6c5389c0185a793ca94446ff0352
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
@@ -14,5 +14,5 @@ f (double *restrict res, double *restrict ptr)
res[1] = x1;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d,
\1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not
{\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n}
} } */
/* { dg-final { scan-assembler-times {\tfminnmv\td[0-9]+, p[0-7],
z[0-9]+\.d\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
index
0b9f8d9bdfaaf868ba0bd73ae83894c68e1613f8..6ba4c9651b67d7e2495b58ffc0ed01f563c428b6
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
@@ -47,9 +47,9 @@ TEST_ALL (VEC_PERM)
/* We should use WHILEs for all accesses. */
/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 30 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 30 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 18 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 27 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 24 } } */
/* 6 for the 8-bit types and 2 for the 16-bit types. */
/* { dg-final { scan-assembler-times {\tuqdecb\t} 8 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
index
7a74efd129cb1c2bab6f60b26f371d9a0deb3b1d..7ebee0fff77602c0d63a43f00c4be958c1fe5036
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 12 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 15 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 11 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 11 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
index
eb05600e98a052fcd21f9c7f0f29502bbb5484a5..98617a90260a5d0508fe7c59a76a4359c9e18283
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 3 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 6 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 5 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 5 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
index
de9325ccacb00a970ecddd90fb25d609467f74c3..286a1282c402b16bc0733b9c5ca9dbe1c1fb8538
100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 6 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 9 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 7 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 7 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c
b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c
new file mode 100644
index
0000000000000000000000000000000000000000..ddc28029117dadac0610da3ba2753bc9cc4cc385
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -ftree-vectorize -fopenmp-simd -march=armv8-a+sve
-msve-vector-bits=scalable --param aarch64-autovec-preference=sve-only
-fdump-tree-vect-details --save-temps" } */
+/* { dg-final { scan-tree-dump-times "LOOP VECTORIZED" 14 "vect" } } */
+
+#include <arm_sve.h>
+#include <stdint.h>
+
+#define NOIPA __attribute__ ((noipa))
+
+#define ADD_LOOP(TYPE, NAME, N) \
+ void NOIPA \
+ NAME (TYPE *__restrict a, const TYPE *__restrict b) \
+ { \
+ for (unsigned int i = 0; i < N; ++i) \
+ a[i] = b[i] + (TYPE) 1; \
+ }
+
+ADD_LOOP (uint8_t, full_b, 300)
+ADD_LOOP (uint16_t, full_h, 1000)
+ADD_LOOP (uint32_t, full_s, 100)
+ADD_LOOP (uint64_t, full_d, 40)
+
+ADD_LOOP (uint8_t, exact_b, 256)
+ADD_LOOP (uint16_t, exact_h, 128)
+ADD_LOOP (uint32_t, exact_s, 64)
+ADD_LOOP (uint64_t, exact_d, 32)
+
+svbool_t NOIPA
+finite_b (void)
+{
+ return svptrue_pat_b8 (SV_VL8);
+}
+
+svbool_t NOIPA
+finite_h (void)
+{
+ return svptrue_pat_b16 (SV_VL4);
+}
+
+svbool_t NOIPA
+finite_s (void)
+{
+ return svptrue_pat_b32 (SV_VL2);
+}
+
+svbool_t NOIPA
+finite_d (void)
+{
+ return svptrue_pat_b64 (SV_VL1);
+}
+
+
+ADD_LOOP (uint8_t, partial_b, 255)
+ADD_LOOP (uint16_t, partial_h, 127)
+ADD_LOOP (uint32_t, partial_s, 63)
+ADD_LOOP (uint64_t, partial_d, 31)
+
+void NOIPA
+full_range_h (uint16_t *__restrict a, const uint16_t *__restrict b,
+ unsigned int n)
+{
+ if (n < 1000)
+ __builtin_unreachable ();
+ for (unsigned int i = 0; i < n; ++i)
+ a[i] = b[i] + (uint16_t) 1;
+}
+
+void NOIPA
+partial_range_h (uint16_t *__restrict a, const uint16_t *__restrict b,
+ unsigned int n)
+{
+ if (n < 100 || n > 127)
+ __builtin_unreachable ();
+ for (unsigned int i = 0; i < n; ++i)
+ a[i] = b[i] + (uint16_t) 1;
+}
+
+/* The first WHILELO in the "full" loops should fold to an all-true
+ predicate. GCC prints all-true predicates as .b, regardless of the
+ element size of the consuming instruction. */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, all\n} 9 } } */
+
+/* The finite loops should fold to finite PTRUE patterns. */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, vl8\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.h, vl4\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.s, vl2\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, vl1\n} 1 } } */
+
+/* The "partial" loops should keep their initial WHILELO. */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b, wzr, w[0-9]+} 1 }
} */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h, wzr, w[0-9]+} 2 }
} */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s, wzr, w[0-9]+} 1 }
} */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d, wzr, w[0-9]+} 1 }
} */
+
+/* All loops still need a latch WHILELO. */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b, [wx][0-9]+,
[wx][0-9]+} 3 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h, [wx][0-9]+,
[wx][0-9]+} 5 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s, [wx][0-9]+,
[wx][0-9]+} 3 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d, [wx][0-9]+,
[wx][0-9]+} 3 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c
b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c
new file mode 100644
index
0000000000000000000000000000000000000000..342b11afce92f567c90ab822f7bdc256c51de7cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c
@@ -0,0 +1,66 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -ftree-vectorize -fopenmp-simd -march=armv8-a+sve
-msve-vector-bits=scalable --param aarch64-autovec-preference=sve-only" } */
+
+#include "while_ult_1.c"
+
+#define CHECK_LOOP(TYPE, NAME, N) \
+ do \
+ { \
+ TYPE a[N], b[N]; \
+ for (unsigned int i = 0; i < N; ++i) \
+ { \
+ a[i] = 0; \
+ b[i] = (TYPE) (i * 3 + i % 7); \
+ } \
+ NAME (a, b); \
+ for (unsigned int i = 0; i < N; ++i) \
+ if (a[i] != (TYPE) (b[i] + (TYPE) 1)) \
+ __builtin_abort (); \
+ } \
+ while (0)
+
+#define CHECK_LOOP_ARG(TYPE, NAME, N) \
+ do \
+ { \
+ TYPE a[N], b[N]; \
+ for (unsigned int i = 0; i < N; ++i) \
+ { \
+ a[i] = 0; \
+ b[i] = (TYPE) (i * 3 + i % 7); \
+ } \
+ NAME (a, b, N); \
+ for (unsigned int i = 0; i < N; ++i) \
+ if (a[i] != (TYPE) (b[i] + (TYPE) 1)) \
+ __builtin_abort (); \
+ } \
+ while (0)
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+ CHECK_LOOP (uint8_t, full_b, 300);
+ CHECK_LOOP (uint16_t, full_h, 1000);
+ CHECK_LOOP (uint32_t, full_s, 100);
+ CHECK_LOOP (uint64_t, full_d, 40);
+
+ CHECK_LOOP (uint8_t, exact_b, 256);
+ CHECK_LOOP (uint16_t, exact_h, 128);
+ CHECK_LOOP (uint32_t, exact_s, 64);
+ CHECK_LOOP (uint64_t, exact_d, 32);
+
+ svbool_t pg = svptrue_b8 ();
+ if (svcntp_b8 (pg, finite_b ()) != 8
+ || svcntp_b16 (pg, finite_h ()) != 4
+ || svcntp_b32 (pg, finite_s ()) != 2
+ || svcntp_b64 (pg, finite_d ()) != 1)
+ __builtin_abort ();
+
+ CHECK_LOOP (uint8_t, partial_b, 255);
+ CHECK_LOOP (uint16_t, partial_h, 127);
+ CHECK_LOOP (uint32_t, partial_s, 63);
+ CHECK_LOOP (uint64_t, partial_d, 31);
+
+ CHECK_LOOP_ARG (uint16_t, full_range_h, 1000);
+ CHECK_LOOP_ARG (uint16_t, partial_range_h, 127);
+ return 0;
+}
diff --git a/gcc/testsuite/gfortran.dg/pr88833.f90
b/gcc/testsuite/gfortran.dg/pr88833.f90
index
224e6ce5f3d53135c588b6ac0ab6ee1733eab852..5959cb1d16bf3da90bbbdedcb57631610cfc17d0
100644
--- a/gcc/testsuite/gfortran.dg/pr88833.f90
+++ b/gcc/testsuite/gfortran.dg/pr88833.f90
@@ -6,4 +6,4 @@ subroutine foo(x)
x = x + 10
end subroutine foo
-! { dg-final { scan-assembler {\twhilelo\tp[0-9]+\.s, wzr,
(w[0-9]+).*\twhilelo\tp[0-9]+\.s, w[0-9]+, \1} } }
+! { dg-final { scan-assembler-not {\twhilelo\tp[0-9]+\.s, wzr,
(w[0-9]+).*\twhilelo\tp[0-9]+\.s, w[0-9]+, \1} } }
--
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 7a81be8dcbe942ca113f7db2fbab4c357c40f462..630856f95281b4e2c290353b3b491404b5dfe633 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -4442,15 +4442,87 @@ aarch64_fold_sve_ptrue_vl (tree vectype, unsigned int vl,
return builder.build ();
}
+/* Fold a WHILE_ULT whose bounds are known enough to be expressed as a PTRUE.
+ Generic folding cannot do this because it cannot assume an architectural
+ maximum for a scalable vector. */
+
+static bool
+aarch64_fold_while_ult_to_ptrue (function *fun, gcall *call,
+ gimple_stmt_iterator *gsi)
+{
+ if (!gimple_call_internal_p (call, IFN_WHILE_ULT))
+ return false;
+
+ tree lhs = gimple_call_lhs (call);
+ if (!lhs)
+ return false;
+
+ tree lhs_type = TREE_TYPE (lhs);
+ if (!VECTOR_BOOLEAN_TYPE_P (lhs_type)
+ || !aarch64_sve_pred_mode_p (TYPE_MODE (lhs_type)))
+ return false;
+
+ int_range_max min, max;
+ range_query *query = get_range_query (fun);
+ if (!query->range_of_expr (min, gimple_call_arg (call, 0), call)
+ || !query->range_of_expr (max, gimple_call_arg (call, 1), call)
+ || min.undefined_p ()
+ || max.undefined_p ()
+ || !min.nonnegative_p ()
+ || !max.nonnegative_p ())
+ return false;
+
+ widest_int min_upper = widest_int::from (min.upper_bound (), UNSIGNED);
+ widest_int min_lower = widest_int::from (min.lower_bound (), UNSIGNED);
+ widest_int max_lower = widest_int::from (max.lower_bound (), UNSIGNED);
+ widest_int max_upper = widest_int::from (max.upper_bound (), UNSIGNED);
+ if (wi::leu_p (max_lower, min_upper))
+ return false;
+
+ unsigned int min_nelts
+ = constant_lower_bound (TYPE_VECTOR_SUBPARTS (lhs_type));
+ widest_int max_sve_nelts = min_nelts * 16;
+ widest_int min_gap = max_lower - min_upper;
+ widest_int max_gap = max_upper - min_lower;
+
+ tree pred_cst = NULL_TREE;
+ unsigned HOST_WIDE_INT gap = 0;
+ if (min_gap == max_gap
+ && wi::fits_uhwi_p (min_gap))
+ {
+ gap = min_gap.to_uhwi ();
+ machine_mode pred_mode = TYPE_MODE (lhs_type);
+ if (gap <= UINT_MAX
+ && (aarch64_svpattern_for_vl (pred_mode, (int) gap)
+ != AARCH64_NUM_SVPATTERNS))
+ pred_cst = aarch64_fold_sve_ptrue_vl (lhs_type,
+ (unsigned int) gap, 1);
+ }
+
+ if (!pred_cst)
+ {
+ if (wi::ltu_p (min_gap, max_sve_nelts))
+ return false;
+ pred_cst = build_all_ones_cst (lhs_type);
+ }
+
+ gassign *assign = gimple_build_assign (lhs, pred_cst);
+ gsi_replace (gsi, assign, false);
+ return true;
+}
+
/* Implement TARGET_INSTRUCTION_SELECTION. The target hook is used to
change generic sequences to a form AArch64 has an easier time expanding
instructions for. It's not supposed to be used for generic rewriting that
all targets would benefit from. */
static bool
-aarch64_instruction_selection (function * /* fun */, gimple_stmt_iterator *gsi)
+aarch64_instruction_selection (function *fun, gimple_stmt_iterator *gsi)
{
auto stmt = gsi_stmt (*gsi);
+ if (gcall *call = dyn_cast<gcall *> (stmt))
+ return aarch64_fold_while_ult_to_ptrue (fun, call, gsi);
+
gassign *assign = dyn_cast<gassign *> (stmt);
if (!assign)
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
index ee3cdc20f96550ceeb1e6c11dd464c59eb4b3e6f..3f1dfdd60d238945e2c4fbf4676d3e943cfb3e30 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_2.c
@@ -16,7 +16,7 @@ f2 (double x, double *ptr)
return x;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.s,.*\tfmaxnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.s,.*\tfmaxnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\ts[0-9]+, p[0-7], z[0-9]+\.s\n} 1 } } */
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\td[0-9]+, p[0-7], z[0-9]+\.d\n} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
index a8eee0f4b2698f0425f55ea91c523e462ca134bd..2548e55f2841d370d5d30bce23350d3750f094a9 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fmaxnm_3.c
@@ -14,5 +14,5 @@ f (double *restrict res, double *restrict ptr)
res[1] = x1;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.d,.*\tfmaxnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
/* { dg-final { scan-assembler-times {\tfmaxnmv\td[0-9]+, p[0-7], z[0-9]+\.d\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
index 10aced05f1afe69f6e646ac23bac8196c13f8357..ed657d58fdfe663f9ddf5b013aa73e14868dbe03 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_2.c
@@ -16,7 +16,7 @@ f2 (double x, double *ptr)
return x;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.s,.*\tfminnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.s,.*\tfminnm\tz[0-9]+\.s, \1/m, z[0-9]+\.s, z[0-9]+\.s\n} } } */
/* { dg-final { scan-assembler-times {\tfminnmv\ts[0-9]+, p[0-7], z[0-9]+\.s\n} 1 } } */
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
/* { dg-final { scan-assembler-times {\tfminnmv\td[0-9]+, p[0-7], z[0-9]+\.d\n} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
index 80ad01602491d07b338113f34d372b5f3970a3be..1f2fc76640fb6c5389c0185a793ca94446ff0352 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/fminnm_3.c
@@ -14,5 +14,5 @@ f (double *restrict res, double *restrict ptr)
res[1] = x1;
}
-/* { dg-final { scan-assembler {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
+/* { dg-final { scan-assembler-not {\twhilelo\t(p[0-7])\.d,.*\tfminnm\tz[0-9]+\.d, \1/m, z[0-9]+\.d, z[0-9]+\.d\n} } } */
/* { dg-final { scan-assembler-times {\tfminnmv\td[0-9]+, p[0-7], z[0-9]+\.d\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
index 0b9f8d9bdfaaf868ba0bd73ae83894c68e1613f8..6ba4c9651b67d7e2495b58ffc0ed01f563c428b6 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
@@ -47,9 +47,9 @@ TEST_ALL (VEC_PERM)
/* We should use WHILEs for all accesses. */
/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 30 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 30 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 18 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 27 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 24 } } */
/* 6 for the 8-bit types and 2 for the 16-bit types. */
/* { dg-final { scan-assembler-times {\tuqdecb\t} 8 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
index 7a74efd129cb1c2bab6f60b26f371d9a0deb3b1d..7ebee0fff77602c0d63a43f00c4be958c1fe5036 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fadd_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 12 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 15 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 11 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 11 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
index eb05600e98a052fcd21f9c7f0f29502bbb5484a5..98617a90260a5d0508fe7c59a76a4359c9e18283 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fmul_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 3 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 6 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 5 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 5 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
index de9325ccacb00a970ecddd90fb25d609467f74c3..286a1282c402b16bc0733b9c5ca9dbe1c1fb8538 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/unpacked_fsubr_2.c
@@ -5,7 +5,7 @@
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.s} } } */
/* { dg-final { scan-assembler-not {\tptrue\tp[0-7]\.d} } } */
-/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 6 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b} 9 } } */
/* { dg-final { scan-assembler-times {\tld1w\tz[0-9]+\.d} 7 } } */
/* { dg-final { scan-assembler-times {\tld1h\tz[0-9]+\.s} 7 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c
new file mode 100644
index 0000000000000000000000000000000000000000..ddc28029117dadac0610da3ba2753bc9cc4cc385
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -ftree-vectorize -fopenmp-simd -march=armv8-a+sve -msve-vector-bits=scalable --param aarch64-autovec-preference=sve-only -fdump-tree-vect-details --save-temps" } */
+/* { dg-final { scan-tree-dump-times "LOOP VECTORIZED" 14 "vect" } } */
+
+#include <arm_sve.h>
+#include <stdint.h>
+
+#define NOIPA __attribute__ ((noipa))
+
+#define ADD_LOOP(TYPE, NAME, N) \
+ void NOIPA \
+ NAME (TYPE *__restrict a, const TYPE *__restrict b) \
+ { \
+ for (unsigned int i = 0; i < N; ++i) \
+ a[i] = b[i] + (TYPE) 1; \
+ }
+
+ADD_LOOP (uint8_t, full_b, 300)
+ADD_LOOP (uint16_t, full_h, 1000)
+ADD_LOOP (uint32_t, full_s, 100)
+ADD_LOOP (uint64_t, full_d, 40)
+
+ADD_LOOP (uint8_t, exact_b, 256)
+ADD_LOOP (uint16_t, exact_h, 128)
+ADD_LOOP (uint32_t, exact_s, 64)
+ADD_LOOP (uint64_t, exact_d, 32)
+
+svbool_t NOIPA
+finite_b (void)
+{
+ return svptrue_pat_b8 (SV_VL8);
+}
+
+svbool_t NOIPA
+finite_h (void)
+{
+ return svptrue_pat_b16 (SV_VL4);
+}
+
+svbool_t NOIPA
+finite_s (void)
+{
+ return svptrue_pat_b32 (SV_VL2);
+}
+
+svbool_t NOIPA
+finite_d (void)
+{
+ return svptrue_pat_b64 (SV_VL1);
+}
+
+
+ADD_LOOP (uint8_t, partial_b, 255)
+ADD_LOOP (uint16_t, partial_h, 127)
+ADD_LOOP (uint32_t, partial_s, 63)
+ADD_LOOP (uint64_t, partial_d, 31)
+
+void NOIPA
+full_range_h (uint16_t *__restrict a, const uint16_t *__restrict b,
+ unsigned int n)
+{
+ if (n < 1000)
+ __builtin_unreachable ();
+ for (unsigned int i = 0; i < n; ++i)
+ a[i] = b[i] + (uint16_t) 1;
+}
+
+void NOIPA
+partial_range_h (uint16_t *__restrict a, const uint16_t *__restrict b,
+ unsigned int n)
+{
+ if (n < 100 || n > 127)
+ __builtin_unreachable ();
+ for (unsigned int i = 0; i < n; ++i)
+ a[i] = b[i] + (uint16_t) 1;
+}
+
+/* The first WHILELO in the "full" loops should fold to an all-true
+ predicate. GCC prints all-true predicates as .b, regardless of the
+ element size of the consuming instruction. */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, all\n} 9 } } */
+
+/* The finite loops should fold to finite PTRUE patterns. */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, vl8\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.h, vl4\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.s, vl2\n} 1 } } */
+/* { dg-final { scan-assembler-times {\tptrue\tp[0-7]\.b, vl1\n} 1 } } */
+
+/* The "partial" loops should keep their initial WHILELO. */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b, wzr, w[0-9]+} 1 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h, wzr, w[0-9]+} 2 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s, wzr, w[0-9]+} 1 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d, wzr, w[0-9]+} 1 } } */
+
+/* All loops still need a latch WHILELO. */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b, [wx][0-9]+, [wx][0-9]+} 3 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h, [wx][0-9]+, [wx][0-9]+} 5 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s, [wx][0-9]+, [wx][0-9]+} 3 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d, [wx][0-9]+, [wx][0-9]+} 3 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c
new file mode 100644
index 0000000000000000000000000000000000000000..342b11afce92f567c90ab822f7bdc256c51de7cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/while_ult_1_run.c
@@ -0,0 +1,66 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -ftree-vectorize -fopenmp-simd -march=armv8-a+sve -msve-vector-bits=scalable --param aarch64-autovec-preference=sve-only" } */
+
+#include "while_ult_1.c"
+
+#define CHECK_LOOP(TYPE, NAME, N) \
+ do \
+ { \
+ TYPE a[N], b[N]; \
+ for (unsigned int i = 0; i < N; ++i) \
+ { \
+ a[i] = 0; \
+ b[i] = (TYPE) (i * 3 + i % 7); \
+ } \
+ NAME (a, b); \
+ for (unsigned int i = 0; i < N; ++i) \
+ if (a[i] != (TYPE) (b[i] + (TYPE) 1)) \
+ __builtin_abort (); \
+ } \
+ while (0)
+
+#define CHECK_LOOP_ARG(TYPE, NAME, N) \
+ do \
+ { \
+ TYPE a[N], b[N]; \
+ for (unsigned int i = 0; i < N; ++i) \
+ { \
+ a[i] = 0; \
+ b[i] = (TYPE) (i * 3 + i % 7); \
+ } \
+ NAME (a, b, N); \
+ for (unsigned int i = 0; i < N; ++i) \
+ if (a[i] != (TYPE) (b[i] + (TYPE) 1)) \
+ __builtin_abort (); \
+ } \
+ while (0)
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+ CHECK_LOOP (uint8_t, full_b, 300);
+ CHECK_LOOP (uint16_t, full_h, 1000);
+ CHECK_LOOP (uint32_t, full_s, 100);
+ CHECK_LOOP (uint64_t, full_d, 40);
+
+ CHECK_LOOP (uint8_t, exact_b, 256);
+ CHECK_LOOP (uint16_t, exact_h, 128);
+ CHECK_LOOP (uint32_t, exact_s, 64);
+ CHECK_LOOP (uint64_t, exact_d, 32);
+
+ svbool_t pg = svptrue_b8 ();
+ if (svcntp_b8 (pg, finite_b ()) != 8
+ || svcntp_b16 (pg, finite_h ()) != 4
+ || svcntp_b32 (pg, finite_s ()) != 2
+ || svcntp_b64 (pg, finite_d ()) != 1)
+ __builtin_abort ();
+
+ CHECK_LOOP (uint8_t, partial_b, 255);
+ CHECK_LOOP (uint16_t, partial_h, 127);
+ CHECK_LOOP (uint32_t, partial_s, 63);
+ CHECK_LOOP (uint64_t, partial_d, 31);
+
+ CHECK_LOOP_ARG (uint16_t, full_range_h, 1000);
+ CHECK_LOOP_ARG (uint16_t, partial_range_h, 127);
+ return 0;
+}
diff --git a/gcc/testsuite/gfortran.dg/pr88833.f90 b/gcc/testsuite/gfortran.dg/pr88833.f90
index 224e6ce5f3d53135c588b6ac0ab6ee1733eab852..5959cb1d16bf3da90bbbdedcb57631610cfc17d0 100644
--- a/gcc/testsuite/gfortran.dg/pr88833.f90
+++ b/gcc/testsuite/gfortran.dg/pr88833.f90
@@ -6,4 +6,4 @@ subroutine foo(x)
x = x + 10
end subroutine foo
-! { dg-final { scan-assembler {\twhilelo\tp[0-9]+\.s, wzr, (w[0-9]+).*\twhilelo\tp[0-9]+\.s, w[0-9]+, \1} } }
+! { dg-final { scan-assembler-not {\twhilelo\tp[0-9]+\.s, wzr, (w[0-9]+).*\twhilelo\tp[0-9]+\.s, w[0-9]+, \1} } }