[llvm] [AArch64] Test all needles when vectorising find_first_of loops. (PR #179298)
David Sherwood via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 3 00:54:48 PST 2026
================
@@ -52,19 +53,21 @@ define ptr @find_first_of_i8(ptr %search_start, ptr %search_end, ptr %needle_sta
; CHECK-NEXT: [[NEEDLE0_SPLAT:%.*]] = shufflevector <vscale x 16 x i8> [[NEEDLE0_SPLATINSERT]], <vscale x 16 x i8> poison, <vscale x 16 x i32> zeroinitializer
; CHECK-NEXT: [[NEEDLE_SPLAT:%.*]] = select <vscale x 16 x i1> [[NEEDLE_MASKED]], <vscale x 16 x i8> [[NEEDLE_LOAD_VEC]], <vscale x 16 x i8> [[NEEDLE0_SPLAT]]
; CHECK-NEXT: [[NEEDLE_VEC:%.*]] = call <16 x i8> @llvm.vector.extract.v16i8.nxv16i8(<vscale x 16 x i8> [[NEEDLE_SPLAT]], i64 0)
-; CHECK-NEXT: [[MATCH_PRED:%.*]] = call <vscale x 16 x i1> @llvm.experimental.vector.match.nxv16i8.v16i8(<vscale x 16 x i8> [[SEARCH_LOAD_VEC]], <16 x i8> [[NEEDLE_VEC]], <vscale x 16 x i1> [[SEARCH_MASKED]])
-; CHECK-NEXT: [[TMP3:%.*]] = call i1 @llvm.vector.reduce.or.nxv16i1(<vscale x 16 x i1> [[MATCH_PRED]])
-; CHECK-NEXT: br i1 [[TMP3]], label %[[CALCULATE_MATCH:.*]], label %[[NEEDLE_CHECK_VEC]]
+; CHECK-NEXT: [[MATCH_SEGMENT:%.*]] = call <vscale x 16 x i1> @llvm.experimental.vector.match.nxv16i8.v16i8(<vscale x 16 x i8> [[SEARCH_LOAD_VEC]], <16 x i8> [[NEEDLE_VEC]], <vscale x 16 x i1> [[SEARCH_MASKED]])
+; CHECK-NEXT: [[MATCH_ACCUMULATOR]] = or <vscale x 16 x i1> [[PMATCH]], [[MATCH_SEGMENT]]
+; CHECK-NEXT: [[NEEDLE_NEXT_VEC]] = getelementptr i8, ptr [[PNEEDLE]], i64 16
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[NEEDLE_NEXT_VEC]], [[NEEDLE_END]]
----------------
david-arm wrote:
Unfortunately this isn't safe because the pointer could wrap. Suppose NEEDLE_END=UINT64_MAX (a perfectly valid address) and NEEDLE_START=UINT64_MAX - 5 (another perfectly valid address). Both lie on the same page in memory so we pass the runtime checks. At the end of this loop we increment NEEDLE_START by 16, which causes it to wrap around to 10. This will then appear to be less than NEEDLE_END and you'll be stuck in an infinite loop.
There are a few ways of solving this problem - you can look at how we handle tail-folding in the loop vectoriser for SVE to see this. Two solutions are:
1. Add more runtime checks to ensure that NEEDLE_END is < UINT64_MAX - 16, which means that NEEDLE_NEXT_VEC can never wrap, or
2. Modify the check to use an extra integer induction variable based on the trip count, i.e. TRIP_COUNT = NEEDLE_END - NEEDLE_START. We know the trip count can never be > the page size, and so long as the minimum page size is something sensible (i.e. < UINT64_MAX/2 or whatever the limit of a ptrint_t type is) then the induction variable can never overflow.
For approach 2 it would be something like:
```
[[TC:%.*]] = sub i64 [[NEEDLE_END]], [[NEEDLE_START]]
NEEDLE_CHECK_VEC:
[[IDX:%.*]] = phi i64 [ 0, %[[FIND_FIRST_VEC_HEADER]] ], [ [[IDX_NEXT:%.*]], %[[NEEDLE_CHECK_VEC]] ]
[[NEEDLE_PRED:%.*]] = call <vscale x 16 x i1> @llvm.get.active.lane.mask.nxv16i1.i64(i64 [[IDX]], i64 [[%tc]])
[[PNEEDLE:%.*]] = getelementptr i8, ptr [[NEEDLE_START]], i64 [[IDX]]
...
[[IDX_NEXT]] = add i64 [[IDX]], 16
[[TMP3:%.*]] = icmp ult ptr [[IDX_NEXT]], [[TC]]
```
https://github.com/llvm/llvm-project/pull/179298
More information about the llvm-commits
mailing list