[llvm] [LV] Derive max trip count for remainder loops through LCSSA phis (PR #206887)

David Sherwood via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 01:21:59 PDT 2026


================
@@ -0,0 +1,126 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=loop-vectorize -S | FileCheck %s
+;
+; When a remainder loop follows a stride-8 main loop, the remainder loop
+; has a proven max trip count of 7 (it handles n%8 elements). SCEV fails
+; to derive this bound because it does not trace the LCSSA phi back to the
+; preceding loop's exit condition. The loop vectorizer should detect this
+; "remainder of stride" pattern and avoid vectorizing the remainder loop
+; since its trip count is too small to benefit from vectorization.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+; The remainder loop (for.body14) should NOT be vectorized.
+
+define void @remainder_after_stride8(ptr %src, i64 %n, ptr %dst) {
+; CHECK-LABEL: @remainder_after_stride8(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_NOT36:%.*]] = icmp ult i64 [[N:%.*]], 8
+; CHECK-NEXT:    br i1 [[CMP_NOT36]], label [[FOR_COND12_PREHEADER:%.*]], label [[FOR_COND1_PREHEADER_PREHEADER:%.*]]
+; CHECK:       for.cond1.preheader.preheader:
+; CHECK-NEXT:    br label [[FOR_COND1_PREHEADER:%.*]]
+; CHECK:       for.cond.loopexit:
+; CHECK-NEXT:    [[ADD:%.*]] = add i64 [[ADD38:%.*]], 8
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp ugt i64 [[ADD]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP_NOT]], label [[FOR_COND12_PREHEADER_LOOPEXIT:%.*]], label [[FOR_COND1_PREHEADER]]
+; CHECK:       for.cond1.preheader:
+; CHECK-NEXT:    [[ADD38]] = phi i64 [ [[ADD]], [[FOR_COND_LOOPEXIT:%.*]] ], [ 8, [[FOR_COND1_PREHEADER_PREHEADER]] ]
+; CHECK-NEXT:    [[I_037:%.*]] = phi i64 [ [[ADD38]], [[FOR_COND_LOOPEXIT]] ], [ 0, [[FOR_COND1_PREHEADER_PREHEADER]] ]
+; CHECK-NEXT:    br label [[FOR_BODY3:%.*]]
+; CHECK:       for.cond12.preheader.loopexit:
+; CHECK-NEXT:    [[ADD38_LCSSA:%.*]] = phi i64 [ [[ADD38]], [[FOR_COND_LOOPEXIT]] ]
+; CHECK-NEXT:    br label [[FOR_COND12_PREHEADER]]
+; CHECK:       for.cond12.preheader:
+; CHECK-NEXT:    [[I_0_LCSSA:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[ADD38_LCSSA]], [[FOR_COND12_PREHEADER_LOOPEXIT]] ]
+; CHECK-NEXT:    [[CMP1339:%.*]] = icmp ult i64 [[I_0_LCSSA]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP1339]], label [[FOR_BODY14_PREHEADER:%.*]], label [[FOR_END21:%.*]]
+; CHECK:       for.body14.preheader:
+; CHECK-NEXT:    br label [[FOR_BODY15:%.*]]
+; CHECK:       for.body3:
+; CHECK-NEXT:    [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_COND1_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY3]] ]
+; CHECK-NEXT:    [[ADD4:%.*]] = or disjoint i64 [[INDVARS_IV]], [[I_037]]
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC:%.*]], i64 [[ADD4]]
+; CHECK-NEXT:    [[TMP13:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT:    [[ADD5:%.*]] = add i32 [[TMP13]], 32768
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[ADD5]], -65536
+; CHECK-NEXT:    [[ARRAYIDX8:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST:%.*]], i64 [[ADD4]]
+; CHECK-NEXT:    store i32 [[AND]], ptr [[ARRAYIDX8]], align 4
+; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 8
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label [[FOR_COND_LOOPEXIT]], label [[FOR_BODY3]]
+; CHECK:       for.body14:
+; CHECK-NEXT:    [[I_140:%.*]] = phi i64 [ [[INC20:%.*]], [[FOR_BODY15]] ], [ [[I_0_LCSSA]], [[FOR_BODY14_PREHEADER]] ]
+; CHECK-NEXT:    [[ARRAYIDX15:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[I_140]]
+; CHECK-NEXT:    [[TMP14:%.*]] = load i32, ptr [[ARRAYIDX15]], align 4
+; CHECK-NEXT:    [[ADD16:%.*]] = add i32 [[TMP14]], 32768
+; CHECK-NEXT:    [[AND17:%.*]] = and i32 [[ADD16]], -65536
+; CHECK-NEXT:    [[ARRAYIDX18:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[I_140]]
+; CHECK-NEXT:    store i32 [[AND17]], ptr [[ARRAYIDX18]], align 4
+; CHECK-NEXT:    [[INC20]] = add nuw i64 [[I_140]], 1
+; CHECK-NEXT:    [[EXITCOND42_NOT:%.*]] = icmp eq i64 [[INC20]], [[N]]
+; CHECK-NEXT:    br i1 [[EXITCOND42_NOT]], label [[FOR_END21_LOOPEXIT:%.*]], label [[FOR_BODY15]]
+; CHECK:       for.end21.loopexit:
+; CHECK-NEXT:    br label [[FOR_END21]]
+; CHECK:       for.end21:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %cmp.not36 = icmp ult i64 %n, 8
+  br i1 %cmp.not36, label %for.cond12.preheader, label %for.cond1.preheader.preheader
+
+for.cond1.preheader.preheader:
+  br label %for.cond1.preheader
+
+for.cond.loopexit:
+  %add = add i64 %add38, 8
+  %cmp.not = icmp ugt i64 %add, %n
+  br i1 %cmp.not, label %for.cond12.preheader.loopexit, label %for.cond1.preheader
+
+for.cond1.preheader:
+  %add38 = phi i64 [ %add, %for.cond.loopexit ], [ 8, %for.cond1.preheader.preheader ]
+  %i.037 = phi i64 [ %add38, %for.cond.loopexit ], [ 0, %for.cond1.preheader.preheader ]
+  br label %for.body3
+
+for.cond12.preheader.loopexit:
+  %add38.lcssa = phi i64 [ %add38, %for.cond.loopexit ]
+  br label %for.cond12.preheader
+
+for.cond12.preheader:
+  %i.0.lcssa = phi i64 [ 0, %entry ], [ %add38.lcssa, %for.cond12.preheader.loopexit ]
+  %cmp1339 = icmp ult i64 %i.0.lcssa, %n
+  br i1 %cmp1339, label %for.body14.preheader, label %for.end21
+
+for.body14.preheader:
+  br label %for.body14
+
+for.body3:
+  %indvars.iv = phi i64 [ 0, %for.cond1.preheader ], [ %indvars.iv.next, %for.body3 ]
+  %add4 = or disjoint i64 %indvars.iv, %i.037
+  %arrayidx = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %add4
+  %0 = load i32, ptr %arrayidx, align 4
+  %add5 = add i32 %0, 32768
+  %and = and i32 %add5, -65536
+  %arrayidx8 = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %add4
+  store i32 %and, ptr %arrayidx8, align 4
+  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+  %exitcond.not = icmp eq i64 %indvars.iv.next, 8
+  br i1 %exitcond.not, label %for.cond.loopexit, label %for.body3
+
+for.body14:
+  %i.140 = phi i64 [ %inc20, %for.body14 ], [ %i.0.lcssa, %for.body14.preheader ]
+  %arrayidx15 = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %i.140
+  %1 = load i32, ptr %arrayidx15, align 4
+  %add16 = add i32 %1, 32768
+  %and17 = and i32 %add16, -65536
+  %arrayidx18 = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %i.140
+  store i32 %and17, ptr %arrayidx18, align 4
+  %inc20 = add nuw i64 %i.140, 1
+  %exitcond42.not = icmp eq i64 %inc20, %n
+  br i1 %exitcond42.not, label %for.end21.loopexit, label %for.body14
+
+for.end21.loopexit:
----------------
david-arm wrote:

This block can be deleted and the loop above can exit directly to for.end21

https://github.com/llvm/llvm-project/pull/206887


More information about the llvm-commits mailing list