[llvm] [LV] Add additional SCEV expansion tests with AddRecs. (NFC) (PR #210159)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 03:48:07 PDT 2026
================
@@ -345,13 +345,313 @@ inner:
store i8 0, ptr %gep
%iv.next = add nuw i64 %iv, 3
%bound = add i64 %outer.iv, 5
- %cmp.inner = icmp ult i64 %iv.next, %bound
- br i1 %cmp.inner, label %inner, label %outer.latch
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %outer.latch
outer.latch:
%outer.iv.next = add nuw i64 %outer.iv, 1
- %cmp.outer = icmp ult i64 %outer.iv.next, 100
- br i1 %cmp.outer, label %outer, label %exit
+ %ec.outer = icmp ult i64 %outer.iv.next, 100
+ br i1 %ec.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+define void @addrec_outer_iv_narrow(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_outer_iv_narrow'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%0> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<outer>:
+; CHECK-NEXT: IR %indvar = phi i64 [ %indvar.next, %outer.latch ], [ 0, %entry ]
+; CHECK-NEXT: IR %outer.iv = phi i32 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+; CHECK-NEXT: IR %0 = add i64 %indvar, 5
+; CHECK-NEXT: IR %ext = zext i32 %outer.iv to i64
+; CHECK-NEXT: Successor(s): vector.ph
+;
+entry:
+ br label %outer
+
+outer:
+ %outer.iv = phi i32 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+ %ext = zext i32 %outer.iv to i64
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %outer ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 1
+ %bound = add i64 %ext, 5
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %outer.iv.next = add nuw i32 %outer.iv, 1
+ %ec.outer = icmp ult i32 %outer.iv.next, 100
+ br i1 %ec.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+; AddRec {0,+,2}<nuw> from the outer loop feeds the inner trip count via its
+; incremented value.
+define void @addrec_non_unit_outer_stride(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_non_unit_outer_stride'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%2> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<outer>:
+; CHECK-NEXT: IR %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+; CHECK-NEXT: IR %0 = add i64 %outer.iv, 6
+; CHECK-NEXT: IR %1 = udiv i64 %0, 3
+; CHECK-NEXT: IR %2 = add nuw nsw i64 %1, 1
+; CHECK-NEXT: IR %outer.iv.next = add nuw i64 %outer.iv, 2
+; CHECK-NEXT: EMIT vp<%min.iters.check> = icmp ult ir<%2>, ir<4>
+; CHECK-NEXT: EMIT branch-on-cond vp<%min.iters.check>
+; CHECK-NEXT: Successor(s): ir-bb<scalar.ph>, vector.ph
+;
+entry:
+ br label %outer
+
+outer:
+ %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+ %outer.iv.next = add nuw i64 %outer.iv, 2
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %outer ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 3
+ %bound = add i64 %outer.iv.next, 5
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %ec.outer = icmp ult i64 %outer.iv.next, 100
+ br i1 %ec.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+; AddRec in outer-most loop needs expansion.
+define void @addrec_over_grandparent_loop(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_over_grandparent_loop'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%2> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<middle>:
+; CHECK-NEXT: IR %mid = phi i64 [ 0, %outermost ], [ %mid.next, %mid.latch ]
+; CHECK-NEXT: EMIT vp<%min.iters.check> = icmp ult ir<%2>, ir<4>
+; CHECK-NEXT: EMIT branch-on-cond vp<%min.iters.check>
+; CHECK-NEXT: Successor(s): ir-bb<scalar.ph>, vector.ph
+;
+entry:
+ br label %outermost
+
+outermost:
+ %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.iv.latch ]
+ br label %middle
+
+middle:
+ %mid = phi i64 [ 0, %outermost ], [ %mid.next, %mid.latch ]
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %middle ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 3
+ %bound = add i64 %outer.iv, 5
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %mid.latch
+
+mid.latch:
+ %mid.next = add nuw i64 %mid, 1
+ %cm = icmp ult i64 %mid.next, 50
+ br i1 %cm, label %middle, label %outer.iv.latch
+
+outer.iv.latch:
+ %outer.iv.next = add nuw i64 %outer.iv, 1
+ %co = icmp ult i64 %outer.iv.next, 100
+ br i1 %co, label %outermost, label %exit
+
+exit:
+ ret void
+}
+
+define void @addrec_phi_not_in_inner_preheader(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_phi_not_in_inner_preheader'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%2> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<inner.ph>:
+; CHECK-NEXT: EMIT vp<%min.iters.check> = icmp ult ir<%2>, ir<4>
+; CHECK-NEXT: EMIT branch-on-cond vp<%min.iters.check>
+; CHECK-NEXT: Successor(s): ir-bb<scalar.ph>, vector.ph
+;
+entry:
+ br label %outer.header
+
+outer.header:
+ %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+ %scaled = add i64 %outer.iv, 5
+ %cc = icmp ult i64 %outer.iv, 1000
+ br i1 %cc, label %inner.ph, label %exit
+
+inner.ph:
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %inner.ph ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 3
+ %ec.inner = icmp ult i64 %iv.next, %scaled
+ br i1 %ec.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %outer.iv.next = add nuw i64 %outer.iv, 1
+ br label %outer.header
+
+exit:
+ ret void
+}
+
+; {5,+,4}<nuw> from outer loop needs expansion.
+define void @addrec_nuw_flags(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_nuw_flags'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%3> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<outer>:
+; CHECK-NEXT: IR %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+; CHECK-NEXT: IR %0 = shl nuw nsw i64 %outer.iv, 2
+; CHECK-NEXT: IR %1 = add i64 %0, 4
+; CHECK-NEXT: IR %2 = udiv i64 %1, 3
+; CHECK-NEXT: IR %3 = add nuw nsw i64 %2, 1
+; CHECK-NEXT: IR %m = mul nuw i64 %outer.iv, 4
+; CHECK-NEXT: IR %bound = add nuw i64 %m, 5
+; CHECK-NEXT: EMIT vp<%min.iters.check> = icmp ult ir<%3>, ir<4>
+; CHECK-NEXT: EMIT branch-on-cond vp<%min.iters.check>
+; CHECK-NEXT: Successor(s): ir-bb<scalar.ph>, vector.ph
+;
+entry:
+ br label %outer
+
+outer:
+ %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+ %m = mul nuw i64 %outer.iv, 4
+ %bound = add nuw i64 %m, 5
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %outer ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 3
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %outer.iv.next = add nuw i64 %outer.iv, 1
+ %ec.outer = icmp ult i64 %outer.iv.next, 100
+ br i1 %ec.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+; The outer loop has a non-affine recurrence %ar = {4,+,5,+,3}<outer> which
+; needs expanding as part of the trip count.
+define void @addrec_non_affine_outer_recurrence_no_canonical_iv(ptr %dst) {
+; CHECK-LABEL: VPlan for loop in 'addrec_non_affine_outer_recurrence_no_canonical_iv'
+; CHECK: VPlan 'Final VPlan for VF={4},UF={1}' {
+; CHECK-NEXT: Live-in ir<%2> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<outer>:
+; CHECK-NEXT: IR %induction.iv = phi i64 [ %induction.iv.next, %outer.latch ], [ 9, %entry ]
+; CHECK-NEXT: IR %outer.iv = phi i64 [ 5, %entry ], [ %outer.iv.next, %outer.latch ]
+; CHECK-NEXT: IR %ar = phi i64 [ 4, %entry ], [ %ar.next, %outer.latch ]
+; CHECK-NEXT: IR %umax = call i64 @llvm.umax.i64(i64 %induction.iv, i64 3)
+; CHECK-NEXT: IR %0 = add i64 %umax, -1
+; CHECK-NEXT: IR %1 = udiv i64 %0, 3
+; CHECK-NEXT: IR %2 = add nuw nsw i64 %1, 1
+; CHECK-NEXT: EMIT vp<%min.iters.check> = icmp ult ir<%2>, ir<4>
+; CHECK-NEXT: EMIT branch-on-cond vp<%min.iters.check>
+; CHECK-NEXT: Successor(s): ir-bb<scalar.ph>, vector.ph
+;
+entry:
+ br label %outer
+
+outer:
+ %outer.iv = phi i64 [ 5, %entry ], [ %outer.iv.next, %outer.latch ]
+ %ar = phi i64 [ 4, %entry ], [ %ar.next, %outer.latch ]
+ br label %inner
+
+inner:
+ %iv = phi i64 [ 0, %outer ], [ %iv.next, %inner ]
+ %gep = getelementptr i8, ptr %dst, i64 %iv
+ store i8 0, ptr %gep
+ %iv.next = add nuw i64 %iv, 3
+ %bound = add i64 %ar, 5
+ %ec.inner = icmp ult i64 %iv.next, %bound
+ br i1 %ec.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %outer.iv.next = add nuw i64 %outer.iv, 3
+ %ar.next = add i64 %ar, %outer.iv
+ %ec.outer = icmp ult i64 %outer.iv.next, 100
+ br i1 %ec.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+; The outer loop has a canonical IV (%outer.iv) and non-affine recurrence
+; %ar = {4,+,5,+,3}<outer> which needs expanding as part of the trip count.
+define void @addrec_non_affine_outer_recurrence_with_canonical_iv(ptr %dst) {
----------------
artagnon wrote:
Thanks for adding this test!
https://github.com/llvm/llvm-project/pull/210159
More information about the llvm-commits
mailing list