[llvm] b33af86 - [SCEV][NFC] Precommit tests for #208778 (#208784)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 10 11:49:23 PDT 2026


Author: Aleksandr Popov
Date: 2026-07-10T20:49:19+02:00
New Revision: b33af86b032e4a89a947b1c2818cb36861814ad9

URL: https://github.com/llvm/llvm-project/commit/b33af86b032e4a89a947b1c2818cb36861814ad9
DIFF: https://github.com/llvm/llvm-project/commit/b33af86b032e4a89a947b1c2818cb36861814ad9.diff

LOG: [SCEV][NFC] Precommit tests for #208778 (#208784)

These tests capture the current missed vectorization behavior and serve
as a baseline for the upcoming fix.

Added: 
    llvm/test/Transforms/LoopVectorize/single_early_exit_zext_trip_count.ll

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/llvm/test/Transforms/LoopVectorize/single_early_exit_zext_trip_count.ll b/llvm/test/Transforms/LoopVectorize/single_early_exit_zext_trip_count.ll
new file mode 100644
index 0000000000000..2ea1fc2e34729
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/single_early_exit_zext_trip_count.ll
@@ -0,0 +1,163 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 4
+; RUN: opt -S < %s -p loop-vectorize -force-vector-width=4 | FileCheck %s
+
+;   assume(dereferenceable(p, zext(len + 8)));
+;   if (len > 0)
+;     for (u64 i = 0; i <= zext(len - 1); ++i)
+;       if (p[8 + i] == 0) return;
+;
+; LAA needs to prove AccessSize <=u KnownDerefSize inside the loop. The raw
+; SCEV expressions, before loop guards are applied, look like:
+;   AccessSize = (9 + (zext i32 (-1 + %len)<nsw> to i64))<nuw><nsw>
+;   KnownSize = (8 + (zext i32 %len to i64))<nuw><nsw>
+;
+; TODO: when applying loop guards SCEV should move -1 outside the zext and fold
+; it with the outer 9. Then AccessSize and KnownSize would have equal
+; loop-guarded forms, so ULE would be trivially true.
+define void @test1(ptr %p) {
+; CHECK-LABEL: define void @test1(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[BASE:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[LEN:%.*]] = load atomic i32, ptr [[BASE]] unordered, align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    [[LEN_PLUS_8:%.*]] = add nuw nsw i32 [[LEN]], 8
+; CHECK-NEXT:    [[LEN_PLUS_8_WIDE:%.*]] = zext i32 [[LEN_PLUS_8]] to i64
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 [[LEN_PLUS_8_WIDE]]) ]
+; CHECK-NEXT:    [[LEN_IS_ZERO:%.*]] = icmp eq i32 [[LEN]], 0
+; CHECK-NEXT:    br i1 [[LEN_IS_ZERO]], label [[RET:%.*]], label [[PREHEADER:%.*]]
+; CHECK:       preheader:
+; CHECK-NEXT:    [[EXIT_32:%.*]] = add nsw i32 [[LEN]], -1
+; CHECK-NEXT:    [[EXIT:%.*]] = zext i32 [[EXIT_32]] to i64
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ], [ 0, [[PREHEADER]] ]
+; CHECK-NEXT:    [[ELEM_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[BASE]], i64 [[IV]]
+; CHECK-NEXT:    [[ELEM:%.*]] = load i8, ptr [[ELEM_PTR]], align 1
+; CHECK-NEXT:    [[IS_TARGET:%.*]] = icmp eq i8 [[ELEM]], 0
+; CHECK-NEXT:    br i1 [[IS_TARGET]], label [[DEOPT:%.*]], label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i64 [[IV]], [[EXIT]]
+; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[DEOPT]]
+; CHECK:       ret.loopexit:
+; CHECK-NEXT:    br label [[RET]]
+; CHECK:       ret:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %base = getelementptr inbounds nuw i8, ptr %p, i64 8
+  %len = load atomic i32, ptr %base unordered, align 4, !range !0
+  %len.plus.8 = add nuw nsw i32 %len, 8
+  %len.plus.8.wide = zext i32 %len.plus.8 to i64
+  call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 %len.plus.8.wide) ]
+  %len.is.zero = icmp eq i32 %len, 0
+  br i1 %len.is.zero, label %ret, label %preheader
+
+preheader:
+  %exit.32 = add nsw i32 %len, -1
+  %exit = zext i32 %exit.32 to i64
+  br label %loop
+
+loop:
+  %iv = phi i64 [ %iv.next, %latch ], [ 0, %preheader ]
+  %elem.ptr = getelementptr inbounds nuw i8, ptr %base, i64 %iv
+  %elem = load i8, ptr %elem.ptr, align 1
+  %is.target = icmp eq i8 %elem, 0
+  br i1 %is.target, label %ret, label %latch
+
+latch:
+  %iv.next = add nuw nsw i64 %iv, 1
+  %loop.cond = icmp ult i64 %iv, %exit
+  br i1 %loop.cond, label %loop, label %ret
+
+ret:
+  ret void
+}
+
+
+;   assume(dereferenceable(p, zext(len + 8)));
+;   if (len > 0) {
+;     u64 exit = zext(umin(A, len - 1));
+;     if (exit > 1)
+;       for (u64 i = 0; i <= exit; ++i)
+;         if (p[8 + i] == 0) return;
+;   }
+;
+; LAA needs to prove AccessSize <=u KnownDerefSize inside the loop. The raw
+; SCEV expressions, before loop guards are applied, look like:
+;   AccessSize = (9 + ((zext i32 (-1 + %len)<nsw> to i64) umin (zext i32 %A to i64)))<nuw><nsw>
+;   KnownSize  = (8 + (zext i32 %len to i64))<nuw><nsw>
+;
+; TODO: when applying loop guards SCEV should:
+; (1) move -1 outside the zext and fold it with the outer 9;
+; (2) apply len >= 3 (implied by the umin(A, len - 1) > 1 guard) to both
+;     AccessSize and KnownSize.
+; Then AccessSize would collapse to umin(KnownSize, ...), so ULE against
+; KnownSize would be trivially true.
+define void @test2(ptr %p, i32 %A) {
+; CHECK-LABEL: define void @test2(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[BASE:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[LEN:%.*]] = load atomic i32, ptr [[BASE]] unordered, align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[LEN_64:%.*]] = zext i32 [[LEN]] to i64
+; CHECK-NEXT:    [[LEN_PLUS_8:%.*]] = add nuw nsw i64 [[LEN_64]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 [[LEN_PLUS_8]]) ]
+; CHECK-NEXT:    [[LEN_NONZERO:%.*]] = icmp ne i32 [[LEN]], 0
+; CHECK-NEXT:    br i1 [[LEN_NONZERO]], label [[PREHEADER:%.*]], label [[RET2:%.*]]
+; CHECK:       preheader:
+; CHECK-NEXT:    [[LEN_MINUS_1:%.*]] = add nsw i32 [[LEN]], -1
+; CHECK-NEXT:    [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.umin.i32(i32 [[A]], i32 [[LEN_MINUS_1]])
+; CHECK-NEXT:    [[EXIT_UMIN:%.*]] = zext nneg i32 [[EXIT_MAINLOOP_AT]] to i64
+; CHECK-NEXT:    [[ENTRY_COND:%.*]] = icmp ult i64 1, [[EXIT_UMIN]]
+; CHECK-NEXT:    br i1 [[ENTRY_COND]], label [[LOOP2_PREHEADER:%.*]], label [[RET2]]
+; CHECK:       loop2.preheader:
+; CHECK-NEXT:    br label [[LOOP2:%.*]]
+; CHECK:       loop2:
+; CHECK-NEXT:    [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LATCH2:%.*]] ], [ 0, [[LOOP2_PREHEADER]] ]
+; CHECK-NEXT:    [[ELEM_PTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[BASE]], i64 [[IV2]]
+; CHECK-NEXT:    [[ELEM2:%.*]] = load i8, ptr [[ELEM_PTR2]], align 1
+; CHECK-NEXT:    [[IS_TARGET2:%.*]] = icmp eq i8 [[ELEM2]], 0
+; CHECK-NEXT:    br i1 [[IS_TARGET2]], label [[DEOPT2:%.*]], label [[LATCH2]]
+; CHECK:       latch2:
+; CHECK-NEXT:    [[IV2_NEXT]] = add nuw nsw i64 [[IV2]], 1
+; CHECK-NEXT:    [[LOOP_COND2:%.*]] = icmp ult i64 [[IV2]], [[EXIT_UMIN]]
+; CHECK-NEXT:    br i1 [[LOOP_COND2]], label [[LOOP2]], label [[DEOPT2]]
+; CHECK:       ret2.loopexit:
+; CHECK-NEXT:    br label [[RET2]]
+; CHECK:       ret2:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %base = getelementptr inbounds nuw i8, ptr %p, i64 8
+  %len = load atomic i32, ptr %base unordered, align 4, !range !0
+  %len.64 = zext i32 %len to i64
+  %len.plus.8 = add nuw nsw i64 %len.64, 8
+  call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 %len.plus.8) ]
+  %len.nonzero = icmp ne i32 %len, 0
+  br i1 %len.nonzero, label %preheader, label %ret2
+
+preheader:
+  %len.minus.1 = add nsw i32 %len, -1
+  %exit.mainloop.at = call i32 @llvm.umin.i32(i32 %A, i32 %len.minus.1)
+  %exit.umin = zext nneg i32 %exit.mainloop.at to i64
+  %entry.cond = icmp ult i64 1, %exit.umin
+  br i1 %entry.cond, label %loop2, label %ret2
+
+loop2:
+  %iv2 = phi i64 [ %iv2.next, %latch2 ], [ 0, %preheader ]
+  %elem.ptr2 = getelementptr inbounds nuw i8, ptr %base, i64 %iv2
+  %elem2 = load i8, ptr %elem.ptr2, align 1
+  %is.target2 = icmp eq i8 %elem2, 0
+  br i1 %is.target2, label %ret2, label %latch2
+
+latch2:
+  %iv2.next = add nuw nsw i64 %iv2, 1
+  %loop.cond2 = icmp ult i64 %iv2, %exit.umin
+  br i1 %loop.cond2, label %loop2, label %ret2
+
+ret2:
+  ret void
+}
+
+!0 = !{i32 0, i32 1000}


        


More information about the llvm-commits mailing list