[llvm] [LoopIdiom] Add a range attribute to formed memset/memcpy/memmove (PR #226801)
Kuba Mracek via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 2 11:46:31 PDT 2026
https://github.com/kubamracek updated https://github.com/llvm/llvm-project/pull/226801
>From 6a2be274cd15b919136451485a5531012a7a991a Mon Sep 17 00:00:00 2001
From: Kuba Mracek <mracek at apple.com>
Date: Sun, 27 Sep 2026 16:33:11 +0100
Subject: [PATCH 1/3] [LoopIdiom] Add a range attribute to formed
memset/memcpy/memmove
Attach a range attribute to the length (or, for memset.pattern, the count)
operand. The upper bound comes from the loop's constant maximum backedge-taken
count and conditions guarding the loop, which also hold at the new call in the
preheader. For a loop like this...
for (unsigned i = n; i < 4; ++i)
dst[i] = -1;
...the memset gets `i64 range(i64 0, 17)`.
LoopIdiomRecognize applies this to memset, memset.pattern, memcpy and memmove
calls.
Assisted-by: Claude
---
.../Transforms/Scalar/LoopIdiomRecognize.cpp | 48 +++++++++++-
.../LoopIdiom/mem-intrinsic-length-range.ll | 74 +++++++++++++++++++
2 files changed, 121 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index b3f095f5ba80d..b6dc6599e35dd 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -1092,6 +1092,41 @@ static const SCEV *getNumBytes(const SCEV *BECount, Type *IntPtr,
SCEV::FlagNUW);
}
+/// Add a range to newly formed memset/memmove/memcpy intrinsic with an upper
+/// bound derived from the loop's constant max trip count.
+static void addRangeAttrFromTripCount(CallInst *NewCall, unsigned ArgNo,
+ uint64_t ElemsPerIter,
+ const SCEV *BECount, Loop *L,
+ ScalarEvolution *SE) {
+ Value *Len = NewCall->getArgOperand(ArgNo);
+ if (isa<Constant>(Len)) return;
+
+ // Two upper bounds:
+ // (1) constant max backedge-taken count
+ const APInt *Max1;
+ if (!match(SE->getConstantMaxBackedgeTakenCount(L), m_scev_APInt(Max1)))
+ return;
+
+ // (2) loop guards (new call is in preheader, so guards dominate the call)
+ APInt Max2 = SE->getUnsignedRangeMax(SE->applyLoopGuards(BECount, L));
+
+ // Use the tighter bound
+ unsigned MaxWidth = std::max(Max1->getBitWidth(), Max2.getBitWidth());
+ APInt MaxBTC = APIntOps::umin(Max1->zext(MaxWidth), Max2.zext(MaxWidth));
+
+ // Bail if bound beyond bitwidth, overflows, or is MaxValue
+ unsigned BW = Len->getType()->getIntegerBitWidth();
+ if (MaxBTC.getActiveBits() >= BW) return;
+ APInt MaxTripCount = MaxBTC.zext(BW) + 1;
+ bool Overflow;
+ APInt MaxLen = MaxTripCount.umul_ov(APInt(BW, ElemsPerIter), Overflow);
+ if (Overflow || MaxLen.isMaxValue()) return;
+
+ NewCall->addParamAttr(
+ ArgNo, Attribute::get(NewCall->getContext(), Attribute::Range,
+ ConstantRange(APInt::getZero(BW), MaxLen + 1)));
+}
+
/// processLoopStridedStore - We see a strided store of some value. If we can
/// transform this into a memset or memset_pattern in the loop preheader, do so.
bool LoopIdiomRecognize::processLoopStridedStore(
@@ -1165,6 +1200,7 @@ bool LoopIdiomRecognize::processLoopStridedStore(
// of pattern repetitions if the memset.pattern intrinsic is being used.
Value *MemsetArg;
std::optional<int64_t> BytesWritten;
+ uint64_t PatternRepsPerTrip = 0;
if (PatternValue && (HasMemsetPattern || ForceMemsetPatternIntrinsic)) {
const SCEV *TripCountS =
@@ -1176,7 +1212,7 @@ bool LoopIdiomRecognize::processLoopStridedStore(
return Changed;
Value *TripCount = Expander.expandCodeFor(TripCountS, IntIdxTy,
Preheader->getTerminator());
- uint64_t PatternRepsPerTrip =
+ PatternRepsPerTrip =
(ConstStoreSize->getValue()->getZExtValue() * 8) /
DL->getTypeSizeInBits(PatternValue->getType());
// If ConstStoreSize is not equal to the width of PatternValue, then
@@ -1220,6 +1256,11 @@ bool LoopIdiomRecognize::processLoopStridedStore(
NewCall = Builder.CreateMemSet(BasePtr, SplatValue, MemsetArg,
MaybeAlign(StoreAlignment),
/*isVolatile=*/false, AATags);
+ if (auto *ConstStoreSize = dyn_cast<SCEVConstant>(StoreSizeSCEV)) {
+ addRangeAttrFromTripCount(NewCall, /*ArgNo*/ 2,
+ ConstStoreSize->getValue()->getZExtValue(),
+ BECount, CurLoop, SE);
+ }
} else if (ForceMemsetPatternIntrinsic ||
isLibFuncEmittable(M, TLI, LibFunc_memset_pattern16)) {
assert(isa<SCEVConstant>(StoreSizeSCEV) && "Expected constant store size");
@@ -1232,6 +1273,9 @@ bool LoopIdiomRecognize::processLoopStridedStore(
if (StoreAlignment)
cast<MemSetPatternInst>(NewCall)->setDestAlignment(*StoreAlignment);
NewCall->setAAMetadata(AATags);
+ assert(PatternRepsPerTrip != 0 && "PatternRepsPerTrip must be set");
+ addRangeAttrFromTripCount(NewCall, /*ArgNo*/ 2, PatternRepsPerTrip, BECount,
+ CurLoop, SE);
} else {
// Neither a memset, nor memset_pattern16
return Changed;
@@ -1544,6 +1588,8 @@ bool LoopIdiomRecognize::processLoopStoreOfLoopLoad(
StoreBasePtr, *StoreAlign, LoadBasePtr, *LoadAlign, NumBytes, StoreSize,
AATags);
}
+ addRangeAttrFromTripCount(NewCall, /*ArgNo*/ 2, StoreSize, BECount, CurLoop,
+ SE);
NewCall->setDebugLoc(TheStore->getDebugLoc());
if (MSSAU) {
diff --git a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
new file mode 100644
index 0000000000000..b8fdd788c0c3c
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
@@ -0,0 +1,74 @@
+; RUN: opt -passes=loop-idiom -S < %s | FileCheck %s
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-macosx14.0.0"
+
+; void bounded_by_exit_condition(int *p, unsigned long start) {
+; for (unsigned long i = start; i < 4; ++i)
+; p[i] = -1;
+; }
+
+define void @bounded_by_exit_condition(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @bounded_by_exit_condition(
+; CHECK: call void @llvm.memset.p0.i64(ptr align 4 {{%.*}}, i8 -1, i64 range(i64 0, 17) {{%.*}}, i1 false)
+entry:
+ %guard = icmp ult i64 %start, 4
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 -1, ptr %gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 4
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void bounded_by_guard(char *p, unsigned long n) {
+; if (n <= 100 && n != 0)
+; for (unsigned long i = 0; i < n; ++i)
+; p[i] = 0;
+; }
+
+define void @bounded_by_guard(ptr %p, i64 %n) {
+; CHECK-LABEL: define void @bounded_by_guard(
+; CHECK: call void @llvm.memset.p0.i64(ptr align 1 {{%.*}}, i8 0, i64 range(i64 0, 101) {{%.*}}, i1 false)
+entry:
+ %small = icmp ule i64 %n, 100
+ %nonzero = icmp ne i64 %n, 0
+ %guard = and i1 %small, %nonzero
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %i
+ store i8 0, ptr %gep, align 1
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void unbounded(int *p, unsigned long n) {
+; if (n != 0)
+; for (unsigned long i = 0; i < n; ++i)
+; p[i] = -1;
+; }
+
+define void @unbounded(ptr %p, i64 %n) {
+; CHECK-LABEL: define void @unbounded(
+; CHECK: call void @llvm.memset.p0.i64(ptr align 4 {{%.*}}, i8 -1, i64 {{%.*}}, i1 false)
+entry:
+ %nonzero = icmp ne i64 %n, 0
+ br i1 %nonzero, label %loop, label %exit
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 -1, ptr %gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
>From 42235f12081e2c5a6a2bcbc688d997c44a1e4309 Mon Sep 17 00:00:00 2001
From: Kuba Mracek <mracek at apple.com>
Date: Fri, 2 Oct 2026 19:24:18 +0100
Subject: [PATCH 2/3] [LoopIdiom] Fix overflow in addRangeAttrFromTripCount;
add 32-bit ptr test
---
.../Transforms/Scalar/LoopIdiomRecognize.cpp | 27 +-
.../LoopIdiom/X86/memset-size-compute.ll | 2 +-
.../Transforms/LoopIdiom/add-nsw-zext-fold.ll | 4 +-
llvm/test/Transforms/LoopIdiom/basic.ll | 12 +-
.../mem-intrinsic-length-range-32bit-ptr.ll | 47 +++
.../LoopIdiom/mem-intrinsic-length-range.ll | 369 +++++++++++++++++-
.../Transforms/LoopIdiom/memcpy-intrinsic.ll | 10 +-
llvm/test/Transforms/LoopIdiom/memcpy.ll | 2 +-
llvm/test/Transforms/LoopIdiom/memmove-ext.ll | 8 +-
.../Transforms/LoopIdiom/memset-pr52104.ll | 2 +-
llvm/test/Transforms/LoopIdiom/memset.ll | 4 +-
.../reuse-lcssa-phi-scev-expansion.ll | 2 +-
llvm/test/Transforms/LoopIdiom/struct.ll | 8 +-
.../Transforms/LoopIdiom/struct_pattern.ll | 6 +-
.../Transforms/LoopIdiom/unroll-custom-dl.ll | 2 +-
llvm/test/Transforms/LoopIdiom/unroll.ll | 4 +-
.../AArch64/memset-bounded-length.ll | 72 ++++
.../PhaseOrdering/ARM/arm_fill_q7.ll | 4 +-
.../Transforms/PhaseOrdering/memset-tail.ll | 2 +-
19 files changed, 539 insertions(+), 48 deletions(-)
create mode 100644 llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
create mode 100644 llvm/test/Transforms/PhaseOrdering/AArch64/memset-bounded-length.ll
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index b6dc6599e35dd..4b1f1315c6a7d 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -1099,7 +1099,8 @@ static void addRangeAttrFromTripCount(CallInst *NewCall, unsigned ArgNo,
const SCEV *BECount, Loop *L,
ScalarEvolution *SE) {
Value *Len = NewCall->getArgOperand(ArgNo);
- if (isa<Constant>(Len)) return;
+ if (isa<Constant>(Len))
+ return;
// Two upper bounds:
// (1) constant max backedge-taken count
@@ -1114,17 +1115,26 @@ static void addRangeAttrFromTripCount(CallInst *NewCall, unsigned ArgNo,
unsigned MaxWidth = std::max(Max1->getBitWidth(), Max2.getBitWidth());
APInt MaxBTC = APIntOps::umin(Max1->zext(MaxWidth), Max2.zext(MaxWidth));
- // Bail if bound beyond bitwidth, overflows, or is MaxValue
+ // The length is in [0, (MaxBTC + 1) * ElemsPerIter]. Bail if that doesn't
+ // fit in the length's type.
unsigned BW = Len->getType()->getIntegerBitWidth();
- if (MaxBTC.getActiveBits() >= BW) return;
- APInt MaxTripCount = MaxBTC.zext(BW) + 1;
+ if (MaxBTC.getActiveBits() > BW)
+ return;
bool Overflow;
+ APInt MaxTripCount = MaxBTC.zextOrTrunc(BW).uadd_ov(APInt(BW, 1), Overflow);
+ if (Overflow)
+ return;
APInt MaxLen = MaxTripCount.umul_ov(APInt(BW, ElemsPerIter), Overflow);
- if (Overflow || MaxLen.isMaxValue()) return;
+ if (Overflow)
+ return;
+ // ConstantRange's upper end is exclusive, so the range ends at MaxLen + 1.
+ APInt RangeEnd = MaxLen.uadd_ov(APInt(BW, 1), Overflow);
+ if (Overflow)
+ return;
NewCall->addParamAttr(
ArgNo, Attribute::get(NewCall->getContext(), Attribute::Range,
- ConstantRange(APInt::getZero(BW), MaxLen + 1)));
+ ConstantRange(APInt::getZero(BW), RangeEnd)));
}
/// processLoopStridedStore - We see a strided store of some value. If we can
@@ -1212,9 +1222,8 @@ bool LoopIdiomRecognize::processLoopStridedStore(
return Changed;
Value *TripCount = Expander.expandCodeFor(TripCountS, IntIdxTy,
Preheader->getTerminator());
- PatternRepsPerTrip =
- (ConstStoreSize->getValue()->getZExtValue() * 8) /
- DL->getTypeSizeInBits(PatternValue->getType());
+ PatternRepsPerTrip = (ConstStoreSize->getValue()->getZExtValue() * 8) /
+ DL->getTypeSizeInBits(PatternValue->getType());
// If ConstStoreSize is not equal to the width of PatternValue, then
// MemsetArg is TripCount * (ConstStoreSize/PatternValueWidth). Else
// MemSetArg is just TripCount.
diff --git a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll
index 0123f15334281..aa8ac8e3096f2 100644
--- a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll
+++ b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll
@@ -19,7 +19,7 @@ define void @test(ptr %ptr) {
; CHECK-NEXT: [[TMP2:%.*]] = shl nuw nsw i32 [[UMAX]], 3
; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[TMP2]], -8
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[TMP0]] to i64
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[SCEVGEP]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[SCEVGEP]], i8 0, i64 range(i64 0, 513) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 1, [[FOR_BODY_PREHEADER]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll b/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
index 09419c13aaeb0..294f057c80d4e 100644
--- a/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
+++ b/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
@@ -12,7 +12,7 @@ define void @fold_add_zext_to_sext(ptr %dst, i1 %start) {
; CHECK-NEXT: [[TMP4:%.*]] = shl nuw nsw i32 [[START_EXT]], 2
; CHECK-NEXT: [[TMP2:%.*]] = sub i32 100, [[TMP4]]
; CHECK-NEXT: [[TMP3:%.*]] = zext nneg i32 [[TMP2]] to i64
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 0, i64 [[TMP3]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 0, i64 range(i64 0, 101) [[TMP3]], i1 false)
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START_EXT]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
@@ -64,7 +64,7 @@ define void @test_memset_size_can_use_info_from_guards(i32 %x, ptr %dst) {
; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[SHR]], -1
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
; CHECK-NEXT: [[UMAX:%.*]] = add nuw nsw i64 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[DST]], i8 0, i64 [[UMAX]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[DST]], i8 0, i64 range(i64 0, 2147483648) [[UMAX]], i1 false)
; CHECK-NEXT: br label %[[LOOP2:.*]]
; CHECK: [[LOOP2]]:
; CHECK-NEXT: [[IV_2:%.*]] = phi i32 [ [[IV_2_NEXT:%.*]], %[[LOOP2]] ], [ 0, %[[LOOP2_PREHEADER]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/basic.ll b/llvm/test/Transforms/LoopIdiom/basic.ll
index 98a1f2bb40e23..e468baea971e1 100644
--- a/llvm/test/Transforms/LoopIdiom/basic.ll
+++ b/llvm/test/Transforms/LoopIdiom/basic.ll
@@ -799,7 +799,7 @@ define noalias ptr @test17(ptr nocapture readonly %a, i32 %c) {
; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[A:%.*]], i64 [[TMP5]]
; CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[C]] to i64
; CHECK-NEXT: [[TMP7:%.*]] = shl nuw nsw i64 [[TMP6]], 2
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[SCEVGEP]], ptr align 4 [[SCEVGEP1]], i64 [[TMP7]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[SCEVGEP]], ptr align 4 [[SCEVGEP1]], i64 range(i64 0, 17179869181) [[TMP7]], i1 false)
; CHECK-NEXT: br label [[WHILE_BODY:%.*]]
; CHECK: while.body:
; CHECK-NEXT: [[DEC10_IN:%.*]] = phi i32 [ [[DEC10:%.*]], [[WHILE_BODY]] ], [ [[C]], [[WHILE_BODY_PREHEADER]] ]
@@ -951,7 +951,7 @@ define void @form_memset_narrow_size(ptr %ptr, i32 %size) {
; CHECK: loop.ph:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[SIZE]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 3
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR:%.*]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR:%.*]], i8 0, i64 range(i64 0, 17179869177) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[LOOP_BODY:%.*]]
; CHECK: loop.body:
; CHECK-NEXT: [[STOREMERGE4:%.*]] = phi i32 [ 0, [[LOOP_PH]] ], [ [[INC:%.*]], [[LOOP_BODY]] ]
@@ -996,7 +996,7 @@ define void @form_memcpy_narrow_size(ptr noalias %dst, ptr noalias %src, i32 %si
; CHECK: loop.ph:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[SIZE]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 3
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DST:%.*]], ptr align 8 [[SRC:%.*]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DST:%.*]], ptr align 8 [[SRC:%.*]], i64 range(i64 0, 17179869177) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[LOOP_BODY:%.*]]
; CHECK: loop.body:
; CHECK-NEXT: [[STOREMERGE4:%.*]] = phi i32 [ 0, [[LOOP_PH]] ], [ [[INC:%.*]], [[LOOP_BODY]] ]
@@ -1121,7 +1121,7 @@ define void @PR46179_negative_stride(ptr %Src, i64 %Size) {
; CHECK-NEXT: br i1 [[CMP1]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body.preheader:
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SRC]], i64 [[SIZE]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SRC]], i64 range(i64 0, -9223372036854775808) [[SIZE]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ [[STEP:%.*]], [[FOR_BODY]] ], [ [[SIZE]], [[FOR_BODY_PREHEADER]] ]
@@ -1162,7 +1162,7 @@ define void @loop_with_memcpy_PR46179_negative_stride(ptr %Src, i64 %Size) {
; CHECK-NEXT: br i1 [[CMP1]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body.preheader:
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SRC]], i64 [[SIZE]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SRC]], i64 range(i64 0, -9223372036854775808) [[SIZE]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ [[STEP:%.*]], [[FOR_BODY]] ], [ [[SIZE]], [[FOR_BODY_PREHEADER]] ]
@@ -1202,7 +1202,7 @@ define void @loop_with_memcpy_stride16(ptr %Src, i64 %Size) {
; CHECK-NEXT: [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
; CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP0]], -16
; CHECK-NEXT: [[TMP3:%.*]] = add nuw i64 [[TMP2]], 16
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SRC]], ptr align 1 [[SCEVGEP]], i64 [[TMP3]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SRC]], ptr align 1 [[SCEVGEP]], i64 range(i64 0, 9223372036854775793) [[TMP3]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ [[STEP:%.*]], [[FOR_BODY]] ], [ 0, [[BB_NPH:%.*]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
new file mode 100644
index 0000000000000..9a4d9a393ddcc
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
@@ -0,0 +1,47 @@
+; RUN: opt -passes=loop-idiom -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S64"
+
+; void i64_iv_bounded(int *p, unsigned long long start) {
+; for (unsigned long long i = start; i < 4; ++i)
+; p[i] = -1;
+; }
+
+define void @i64_iv_bounded(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @i64_iv_bounded(
+; CHECK: call void @llvm.memset.p0.i32(ptr align 4 {{%.*}}, i8 -1, i32 range(i32 0, 17) {{%.*}}, i1 false)
+entry:
+ %guard = icmp ult i64 %start, 4
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 -1, ptr %gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 4
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void i64_iv_too_large(char *p, unsigned long long start) {
+; for (unsigned long long i = start; i < (1ULL << 40); ++i)
+; p[i] = 0;
+; }
+
+define void @i64_iv_too_large(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @i64_iv_too_large(
+; CHECK: call void @llvm.memset.p0.i32(ptr align 1 {{%.*}}, i8 0, i32 {{%.*}}, i1 false)
+entry:
+ %guard = icmp ult i64 %start, 1099511627776
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %i
+ store i8 0, ptr %gep, align 1
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 1099511627776
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
index b8fdd788c0c3c..2f64831dd4cba 100644
--- a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
+++ b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=loop-idiom -S < %s | FileCheck %s
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
@@ -10,7 +11,27 @@ target triple = "arm64-apple-macosx14.0.0"
define void @bounded_by_exit_condition(ptr %p, i64 %start) {
; CHECK-LABEL: define void @bounded_by_exit_condition(
-; CHECK: call void @llvm.memset.p0.i64(ptr align 4 {{%.*}}, i8 -1, i64 range(i64 0, 17) {{%.*}}, i1 false)
+; CHECK-SAME: ptr [[P:%.*]], i64 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[GUARD:%.*]] = icmp ult i64 [[START]], 4
+; CHECK-NEXT: br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[START]], 2
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i64 16, [[TMP0]]
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 -1, i64 range(i64 0, 17) [[TMP1]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ [[START]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 4
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
entry:
%guard = icmp ult i64 %start, 4
br i1 %guard, label %loop, label %exit
@@ -33,7 +54,26 @@ exit:
define void @bounded_by_guard(ptr %p, i64 %n) {
; CHECK-LABEL: define void @bounded_by_guard(
-; CHECK: call void @llvm.memset.p0.i64(ptr align 1 {{%.*}}, i8 0, i64 range(i64 0, 101) {{%.*}}, i1 false)
+; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SMALL:%.*]] = icmp ule i64 [[N]], 100
+; CHECK-NEXT: [[NONZERO:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: [[GUARD:%.*]] = and i1 [[SMALL]], [[NONZERO]]
+; CHECK-NEXT: br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P]], i8 0, i64 range(i64 0, 101) [[N]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
entry:
%small = icmp ule i64 %n, 100
%nonzero = icmp ne i64 %n, 0
@@ -50,6 +90,95 @@ exit:
ret void
}
+; void bounded_by_assume(char *p, unsigned long n) {
+; __builtin_assume(n <= 100);
+; if (n != 0)
+; for (unsigned long i = 0; i < n; ++i)
+; p[i] = 0;
+; }
+
+define void @bounded_by_assume(ptr %p, i64 %n) {
+; CHECK-LABEL: define void @bounded_by_assume(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SMALL:%.*]] = icmp ule i64 [[N]], 100
+; CHECK-NEXT: call void @llvm.assume(i1 [[SMALL]])
+; CHECK-NEXT: [[NONZERO:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: br i1 [[NONZERO]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P]], i8 0, i64 range(i64 0, 101) [[N]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %small = icmp ule i64 %n, 100
+ call void @llvm.assume(i1 %small)
+ %nonzero = icmp ne i64 %n, 0
+ br i1 %nonzero, label %loop, label %exit
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %i
+ store i8 0, ptr %gep, align 1
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void bounded_by_type(int *p, unsigned n) {
+; if (n != 0)
+; for (unsigned i = 0; i < n; ++i)
+; p[i] = -1;
+; }
+
+define void @bounded_by_type(ptr %p, i32 %n) {
+; CHECK-LABEL: define void @bounded_by_type(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[NONZERO:%.*]] = icmp ne i32 [[N]], 0
+; CHECK-NEXT: br i1 [[NONZERO]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[P]], i8 -1, i64 range(i64 0, 17179869181) [[TMP1]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[I_WIDE:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I_WIDE]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw i32 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %nonzero = icmp ne i32 %n, 0
+ br i1 %nonzero, label %loop, label %exit
+loop:
+ %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+ %i.wide = zext i32 %i to i64
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i.wide
+ store i32 -1, ptr %gep, align 4
+ %i.next = add nuw i32 %i, 1
+ %cmp = icmp ult i32 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
; void unbounded(int *p, unsigned long n) {
; if (n != 0)
; for (unsigned long i = 0; i < n; ++i)
@@ -58,7 +187,25 @@ exit:
define void @unbounded(ptr %p, i64 %n) {
; CHECK-LABEL: define void @unbounded(
-; CHECK: call void @llvm.memset.p0.i64(ptr align 4 {{%.*}}, i8 -1, i64 {{%.*}}, i1 false)
+; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[NONZERO:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: br i1 [[NONZERO]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl nuw i64 [[N]], 2
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[P]], i8 -1, i64 [[TMP0]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
entry:
%nonzero = icmp ne i64 %n, 0
br i1 %nonzero, label %loop, label %exit
@@ -72,3 +219,219 @@ loop:
exit:
ret void
}
+
+; void constant_trip_count(int *p) {
+; for (unsigned long i = 0; i < 4; ++i)
+; p[i] = -1;
+; }
+
+define void @constant_trip_count(ptr %p) {
+; CHECK-LABEL: define void @constant_trip_count(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[P]], i8 -1, i64 16, i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 4
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 -1, ptr %gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 4
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void memcpy_bounded(int *restrict dst, const int *restrict src,
+; unsigned long start) {
+; for (unsigned long i = start; i < 7; ++i)
+; dst[i] = src[i];
+; }
+
+define void @memcpy_bounded(ptr noalias %dst, ptr noalias %src, i64 %start) {
+; CHECK-LABEL: define void @memcpy_bounded(
+; CHECK-SAME: ptr noalias [[DST:%.*]], ptr noalias [[SRC:%.*]], i64 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[GUARD:%.*]] = icmp ult i64 [[START]], 7
+; CHECK-NEXT: br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[START]], 2
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST]], i64 [[TMP0]]
+; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[SRC]], i64 [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i64 28, [[TMP0]]
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[SCEVGEP]], ptr align 4 [[SCEVGEP1]], i64 range(i64 0, 29) [[TMP1]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ [[START]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[SRC_GEP:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[I]]
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[SRC_GEP]], align 4
+; CHECK-NEXT: [[DST_GEP:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 7
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %guard = icmp ult i64 %start, 7
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %src.gep = getelementptr inbounds i32, ptr %src, i64 %i
+ %val = load i32, ptr %src.gep, align 4
+ %dst.gep = getelementptr inbounds i32, ptr %dst, i64 %i
+ store i32 %val, ptr %dst.gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 7
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void memmove_bounded(int *p, unsigned long start) {
+; for (unsigned long i = start; i < 5; ++i)
+; p[i] = p[i + 1];
+; }
+
+define void @memmove_bounded(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @memmove_bounded(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[GUARD:%.*]] = icmp ult i64 [[START]], 5
+; CHECK-NEXT: br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[START]], 2
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], 4
+; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP1]]
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 20, [[TMP0]]
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 4 [[SCEVGEP]], ptr align 4 [[SCEVGEP1]], i64 range(i64 0, 21) [[TMP2]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ [[START]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[SRC_GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I_NEXT]]
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[SRC_GEP]], align 4
+; CHECK-NEXT: [[DST_GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 5
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %guard = icmp ult i64 %start, 5
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %i.next = add nuw nsw i64 %i, 1
+ %src.gep = getelementptr inbounds i32, ptr %p, i64 %i.next
+ %val = load i32, ptr %src.gep, align 4
+ %dst.gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 %val, ptr %dst.gep, align 4
+ %cmp = icmp ult i64 %i.next, 5
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; Without a guard, only the exit condition bounds the trip count. The length,
+; 4 * (umax(start + 1, 4) - start), is at most 20 (start = ULONG_MAX wraps),
+; but range analysis of the expanded IR alone can't see that.
+;
+; void do_while_no_guard(int *p, unsigned long start) {
+; unsigned long i = start;
+; do {
+; p[i] = -1;
+; } while (++i < 4);
+; }
+
+define void @do_while_no_guard(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @do_while_no_guard(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[START]], 2
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[START]], 1
+; CHECK-NEXT: [[UMAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP1]], i64 4)
+; CHECK-NEXT: [[TMP2:%.*]] = shl i64 [[UMAX]], 2
+; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 -1, i64 range(i64 0, 21) [[TMP3]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[START]], %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 4
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 -1, ptr %gep, align 4
+ %i.next = add i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 4
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
+; void memset_pattern_bounded(int *p, unsigned long start) {
+; for (unsigned long i = start; i < 8; ++i)
+; p[i] = 0x12345678;
+; }
+
+define void @memset_pattern_bounded(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @memset_pattern_bounded(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[GUARD:%.*]] = icmp ult i64 [[START]], 8
+; CHECK-NEXT: br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[START]], 2
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i64 8, [[START]]
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[SCEVGEP]], i32 305419896, i64 range(i64 0, 9) [[TMP1]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ [[START]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], 8
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %guard = icmp ult i64 %start, 8
+ br i1 %guard, label %loop, label %exit
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %i
+ store i32 305419896, ptr %gep, align 4
+ %i.next = add nuw nsw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 8
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopIdiom/memcpy-intrinsic.ll b/llvm/test/Transforms/LoopIdiom/memcpy-intrinsic.ll
index 85561da0ffee7..2f2407fab462e 100644
--- a/llvm/test/Transforms/LoopIdiom/memcpy-intrinsic.ll
+++ b/llvm/test/Transforms/LoopIdiom/memcpy-intrinsic.ll
@@ -19,7 +19,7 @@ define dso_local i32 @copy_noalias(ptr noalias nocapture %a, ptr nocapture reado
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw nsw i64 [[TMP0]], 12
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A:%.*]], ptr align 4 [[B:%.*]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A:%.*]], ptr align 4 [[B:%.*]], i64 range(i64 0, 25769803765) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.cleanup.loopexit:
; CHECK-NEXT: br label [[FOR_COND_CLEANUP]]
@@ -130,7 +130,7 @@ define dso_local void @copy_noalias_read(ptr noalias nocapture %x, ptr noalias n
; CHECK-NEXT: [[S_PROMOTED:%.*]] = load i32, ptr [[S:%.*]], align 4
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw nsw i64 [[TMP0]], 6
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[X:%.*]], ptr align 1 [[Y:%.*]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[X:%.*]], ptr align 1 [[Y:%.*]], i64 range(i64 0, 12884901883) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.for.cond.cleanup_crit_edge:
; CHECK-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD:%.*]], [[FOR_BODY]] ]
@@ -199,7 +199,7 @@ define dso_local i32 @copy_noalias_negative_stride(ptr noalias nocapture %arg, p
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[ARG2]], 1
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = mul nuw nsw i64 [[TMP1]], 12
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARG:%.*]], ptr align 4 [[ARG1:%.*]], i64 [[TMP2]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARG:%.*]], ptr align 4 [[ARG1:%.*]], i64 range(i64 0, 25769803777) [[TMP2]], i1 false)
; CHECK-NEXT: br label [[BB6:%.*]]
; CHECK: bb4:
; CHECK-NEXT: br label [[BB5]]
@@ -312,7 +312,7 @@ define dso_local i32 @copy_noalias_packed(ptr noalias nocapture %a, ptr nocaptur
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw nsw i64 [[TMP0]], 9
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[A:%.*]], ptr align 1 [[B:%.*]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[A:%.*]], ptr align 1 [[B:%.*]], i64 range(i64 0, 19327352824) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.cleanup.loopexit:
; CHECK-NEXT: br label [[FOR_COND_CLEANUP]]
@@ -361,7 +361,7 @@ define dso_local i32 @copy_noalias_aligned(ptr noalias nocapture %a, ptr nocaptu
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 4
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[A:%.*]], ptr align 16 [[B:%.*]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[A:%.*]], ptr align 16 [[B:%.*]], i64 range(i64 0, 34359738353) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.cleanup.loopexit:
; CHECK-NEXT: br label [[FOR_COND_CLEANUP]]
diff --git a/llvm/test/Transforms/LoopIdiom/memcpy.ll b/llvm/test/Transforms/LoopIdiom/memcpy.ll
index 4b99bca0097e5..424a657cdaa2b 100644
--- a/llvm/test/Transforms/LoopIdiom/memcpy.ll
+++ b/llvm/test/Transforms/LoopIdiom/memcpy.ll
@@ -109,7 +109,7 @@ define dso_local void @memcpy_loop(ptr noalias nocapture %p, ptr noalias nocaptu
; CHECK-NEXT: br i1 [[CMP4]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]]
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
-; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[P:%.*]], ptr align 1 [[Q:%.*]], i64 [[TMP0]], i1 false)
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[P:%.*]], ptr align 1 [[Q:%.*]], i64 range(i64 0, 2147483648) [[TMP0]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.cleanup.loopexit:
; CHECK-NEXT: br label [[FOR_COND_CLEANUP]]
diff --git a/llvm/test/Transforms/LoopIdiom/memmove-ext.ll b/llvm/test/Transforms/LoopIdiom/memmove-ext.ll
index 5f3aa8b33b2b6..2d7f9c37ed881 100644
--- a/llvm/test/Transforms/LoopIdiom/memmove-ext.ll
+++ b/llvm/test/Transforms/LoopIdiom/memmove-ext.ll
@@ -19,7 +19,7 @@ define void @move_up_dec_loop_zext(i16 %n, ptr %p) {
; CHECK-NEXT: [[TMP6:%.*]] = sub i64 [[TMP5]], [[TMP3]]
; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP6]]
; CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP3]], 1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SCEVGEP1]], i64 [[TMP7]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SCEVGEP1]], i64 range(i64 0, 32769) [[TMP7]], i1 false)
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[N]], %[[ENTRY]] ]
@@ -61,7 +61,7 @@ define void @move_down_inc_loop_zext(i16 %n, ptr %p) {
; CHECK-NEXT: [[SMAX:%.*]] = call i16 @llvm.smax.i16(i16 [[N]], i16 2)
; CHECK-NEXT: [[TMP0:%.*]] = zext nneg i16 [[SMAX]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i64 [[TMP0]], -1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[P]], ptr align 1 [[SCEVGEP]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[P]], ptr align 1 [[SCEVGEP]], i64 range(i64 0, 32767) [[TMP1]], i1 false)
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ 1, %[[ENTRY]] ]
@@ -111,7 +111,7 @@ define void @move_up_dec_loop_sext(i16 %n, ptr %p) {
; CHECK-NEXT: [[TMP6:%.*]] = sub i64 [[TMP5]], [[TMP3]]
; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP6]]
; CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP3]], 1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SCEVGEP1]], i64 [[TMP7]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[SCEVGEP]], ptr align 1 [[SCEVGEP1]], i64 range(i64 0, 32769) [[TMP7]], i1 false)
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[N]], %[[ENTRY]] ]
@@ -153,7 +153,7 @@ define void @move_down_inc_loop_sext(i16 %n, ptr %p) {
; CHECK-NEXT: [[SMAX:%.*]] = call i16 @llvm.smax.i16(i16 [[N]], i16 2)
; CHECK-NEXT: [[TMP0:%.*]] = zext nneg i16 [[SMAX]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i64 [[TMP0]], -1
-; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[P]], ptr align 1 [[SCEVGEP]], i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[P]], ptr align 1 [[SCEVGEP]], i64 range(i64 0, 32767) [[TMP1]], i1 false)
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ 1, %[[ENTRY]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll
index 0cacb74bf01be..81382eff33f7c 100644
--- a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll
+++ b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll
@@ -11,7 +11,7 @@ define void @f(ptr nocapture nonnull align 4 dereferenceable(20) %0, i32 %1) loc
; CHECK-NEXT: [[TMP7:%.*]] = zext i2 [[TMP6]] to i64
; CHECK-NEXT: [[TMP8:%.*]] = shl nuw nsw i64 [[TMP7]], 2
; CHECK-NEXT: [[TMP9:%.*]] = add nuw nsw i64 [[TMP8]], 4
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 0, i64 [[TMP9]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[SCEVGEP]], i8 0, i64 range(i64 0, 17) [[TMP9]], i1 false)
; CHECK-NEXT: br label [[TMP10:%.*]]
; CHECK: 10:
; CHECK-NEXT: [[TMP11:%.*]] = phi i32 [ [[TMP15:%.*]], [[TMP10]] ], [ [[TMP1]], [[TMP2:%.*]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/memset.ll b/llvm/test/Transforms/LoopIdiom/memset.ll
index 8b4f68b62664e..e2ae7f9d3219d 100644
--- a/llvm/test/Transforms/LoopIdiom/memset.ll
+++ b/llvm/test/Transforms/LoopIdiom/memset.ll
@@ -9,9 +9,9 @@ define dso_local void @double_memset(ptr nocapture %p, ptr noalias nocapture %q,
; CHECK-NEXT: br i1 [[CMP4]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]]
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[Q:%.*]], i8 0, i64 [[TMP0]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[Q:%.*]], i8 0, i64 range(i64 0, 2147483648) [[TMP0]], i1 false)
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[N]] to i64
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P:%.*]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P:%.*]], i8 0, i64 range(i64 0, 2147483648) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.cond.cleanup.loopexit:
; CHECK-NEXT: br label [[FOR_COND_CLEANUP]]
diff --git a/llvm/test/Transforms/LoopIdiom/reuse-lcssa-phi-scev-expansion.ll b/llvm/test/Transforms/LoopIdiom/reuse-lcssa-phi-scev-expansion.ll
index 5011d21d7a044..bcd974d484573 100644
--- a/llvm/test/Transforms/LoopIdiom/reuse-lcssa-phi-scev-expansion.ll
+++ b/llvm/test/Transforms/LoopIdiom/reuse-lcssa-phi-scev-expansion.ll
@@ -249,7 +249,7 @@ define void @expand_truncated_ptrtoaddr(ptr %A, ptr %B) {
; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[INDVAR_LCSSA]], -1
; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP3]]
; CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP5]] to i64
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[SCEVGEP]], i8 0, i64 range(i64 0, 2147483649) [[TMP6]], i1 false)
; CHECK-NEXT: br label %[[LOOP_2:.*]]
; CHECK: [[LOOP_2]]:
; CHECK-NEXT: [[P_1:%.*]] = phi ptr [ [[B]], %[[MIDDLE]] ], [ [[P_1_NEXT:%.*]], %[[LOOP_2]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/struct.ll b/llvm/test/Transforms/LoopIdiom/struct.ll
index 73d569113f898..bd5cfcc643b61 100644
--- a/llvm/test/Transforms/LoopIdiom/struct.ll
+++ b/llvm/test/Transforms/LoopIdiom/struct.ll
@@ -22,7 +22,7 @@ define void @bar1(ptr %f, i32 %n) nounwind ssp {
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 3
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 range(i64 0, 34359738361) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -76,7 +76,7 @@ define void @bar2(ptr %f, i32 %n) nounwind ssp {
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 3
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 range(i64 0, 34359738361) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -135,7 +135,7 @@ define void @bar3(ptr nocapture %f, i32 %n) nounwind ssp {
; CHECK-NEXT: [[TMP4:%.*]] = shl nuw nsw i64 [[TMP3]], 3
; CHECK-NEXT: [[TMP5:%.*]] = sub i64 [[TMP1]], [[TMP4]]
; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[F:%.*]], i64 [[TMP5]]
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[UGLYGEP]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[UGLYGEP]], i8 0, i64 range(i64 0, 34359738361) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -299,7 +299,7 @@ define void @bar6(ptr nocapture %f, i32 %n) nounwind ssp {
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 3
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 range(i64 0, 34359738361) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/struct_pattern.ll b/llvm/test/Transforms/LoopIdiom/struct_pattern.ll
index 37a5822bfd59e..7015eee7efc95 100644
--- a/llvm/test/Transforms/LoopIdiom/struct_pattern.ll
+++ b/llvm/test/Transforms/LoopIdiom/struct_pattern.ll
@@ -24,7 +24,7 @@ define void @bar1(ptr %f, i32 %n) nounwind ssp {
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2
-; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 range(i64 0, 8589934591) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -78,7 +78,7 @@ define void @bar2(ptr %f, i32 %n) nounwind ssp {
; CHECK: for.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2
-; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 [[TMP1]], i1 false)
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 range(i64 0, 8589934591) [[TMP1]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -138,7 +138,7 @@ define void @bar3(ptr nocapture %f, i32 %n) nounwind ssp {
; CHECK-NEXT: [[TMP5:%.*]] = sub i64 [[TMP1]], [[TMP4]]
; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[F:%.*]], i64 [[TMP5]]
; CHECK-NEXT: [[TMP7:%.*]] = mul i64 [[TMP0]], 2
-; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[UGLYGEP]], i32 2, i64 [[TMP7]], i1 false)
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[UGLYGEP]], i32 2, i64 range(i64 0, 8589934591) [[TMP7]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll
index 888f44e7226cf..f4bfb0fa16ecc 100644
--- a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll
+++ b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll
@@ -80,7 +80,7 @@ define void @test_pattern(ptr %f, i32 %n) nounwind ssp {
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[TMP0]], 1
; CHECK-NEXT: [[TMP2:%.*]] = add nuw i32 [[TMP1]], 1
; CHECK-NEXT: [[TMP3:%.*]] = mul i32 [[TMP2]], 2
-; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i32(ptr align 4 [[F:%.*]], i32 2, i32 [[TMP3]], i1 false)
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i32(ptr align 4 [[F:%.*]], i32 2, i32 range(i32 0, -1) [[TMP3]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i32 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
diff --git a/llvm/test/Transforms/LoopIdiom/unroll.ll b/llvm/test/Transforms/LoopIdiom/unroll.ll
index 695d38dbb7a4e..b8a1cb8bc8e30 100644
--- a/llvm/test/Transforms/LoopIdiom/unroll.ll
+++ b/llvm/test/Transforms/LoopIdiom/unroll.ll
@@ -23,7 +23,7 @@ define void @test(ptr %f, i32 %n) nounwind ssp {
; CHECK-NEXT: [[TMP2:%.*]] = lshr i64 [[TMP1]], 1
; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 3
; CHECK-NEXT: [[TMP4:%.*]] = add i64 [[TMP3]], 8
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 [[TMP4]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[F:%.*]], i8 0, i64 range(i64 0, 17179869177) [[TMP4]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -83,7 +83,7 @@ define void @test_pattern(ptr %f, i32 %n) nounwind ssp {
; CHECK-NEXT: [[TMP2:%.*]] = lshr i64 [[TMP1]], 1
; CHECK-NEXT: [[TMP3:%.*]] = add nuw i64 [[TMP2]], 1
; CHECK-NEXT: [[TMP4:%.*]] = mul i64 [[TMP3]], 2
-; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 [[TMP4]], i1 false)
+; CHECK-NEXT: call void @llvm.experimental.memset.pattern.p0.i32.i64(ptr align 4 [[F:%.*]], i32 2, i64 range(i64 0, 4294967295) [[TMP4]], i1 false)
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/memset-bounded-length.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/memset-bounded-length.ll
new file mode 100644
index 0000000000000..02dd6988f2434
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/memset-bounded-length.ll
@@ -0,0 +1,72 @@
+; RUN: opt -passes="default<O3>" -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64"
+
+; #define MAX_CNT 4
+; void foo(unsigned* src, unsigned* dst, unsigned int n) {
+; for (unsigned i = 0; i < n; ++i)
+; dst[i] = src[i];
+; for (unsigned i = n; i < MAX_CNT; ++i)
+; dst[i] = -1;
+; }
+
+define void @foo(ptr noundef %src, ptr noundef %dst, i32 noundef %n) {
+; CHECK-LABEL: define void @foo(
+; CHECK: call void @llvm.memset.p0.i64(ptr align 4 {{%.*}}, i8 -1, i64 range(i64 0, 17) {{%.*}}, i1 false)
+entry:
+ %src.addr = alloca ptr, align 8
+ %dst.addr = alloca ptr, align 8
+ %n.addr = alloca i32, align 4
+ %i = alloca i32, align 4
+ %i3 = alloca i32, align 4
+ store ptr %src, ptr %src.addr, align 8
+ store ptr %dst, ptr %dst.addr, align 8
+ store i32 %n, ptr %n.addr, align 4
+ store i32 0, ptr %i, align 4
+ br label %for.cond
+for.cond:
+ %0 = load i32, ptr %i, align 4
+ %1 = load i32, ptr %n.addr, align 4
+ %cmp = icmp ult i32 %0, %1
+ br i1 %cmp, label %for.body, label %for.end
+for.body:
+ %2 = load ptr, ptr %src.addr, align 8
+ %3 = load i32, ptr %i, align 4
+ %idxprom = zext i32 %3 to i64
+ %arrayidx = getelementptr inbounds nuw i32, ptr %2, i64 %idxprom
+ %4 = load i32, ptr %arrayidx, align 4
+ %5 = load ptr, ptr %dst.addr, align 8
+ %6 = load i32, ptr %i, align 4
+ %idxprom1 = zext i32 %6 to i64
+ %arrayidx2 = getelementptr inbounds nuw i32, ptr %5, i64 %idxprom1
+ store i32 %4, ptr %arrayidx2, align 4
+ br label %for.inc
+for.inc:
+ %7 = load i32, ptr %i, align 4
+ %inc = add i32 %7, 1
+ store i32 %inc, ptr %i, align 4
+ br label %for.cond
+for.end:
+ %8 = load i32, ptr %n.addr, align 4
+ store i32 %8, ptr %i3, align 4
+ br label %for.cond4
+for.cond4:
+ %9 = load i32, ptr %i3, align 4
+ %cmp5 = icmp ult i32 %9, 4
+ br i1 %cmp5, label %for.body6, label %for.end11
+for.body6:
+ %10 = load ptr, ptr %dst.addr, align 8
+ %11 = load i32, ptr %i3, align 4
+ %idxprom7 = zext i32 %11 to i64
+ %arrayidx8 = getelementptr inbounds nuw i32, ptr %10, i64 %idxprom7
+ store i32 -1, ptr %arrayidx8, align 4
+ br label %for.inc9
+for.inc9:
+ %12 = load i32, ptr %i3, align 4
+ %inc10 = add i32 %12, 1
+ store i32 %inc10, ptr %i3, align 4
+ br label %for.cond4
+for.end11:
+ ret void
+}
diff --git a/llvm/test/Transforms/PhaseOrdering/ARM/arm_fill_q7.ll b/llvm/test/Transforms/PhaseOrdering/ARM/arm_fill_q7.ll
index c1ee91f48c2d9..fc214cb8660c9 100644
--- a/llvm/test/Transforms/PhaseOrdering/ARM/arm_fill_q7.ll
+++ b/llvm/test/Transforms/PhaseOrdering/ARM/arm_fill_q7.ll
@@ -15,7 +15,7 @@ define dso_local void @arm_fill_q7(i8 signext %value, ptr %pDst, i32 %blockSize)
; CHECK-NEXT: br i1 [[CMP_NOT14]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]]
; CHECK: while.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = and i32 [[BLOCKSIZE]], -4
-; CHECK-NEXT: tail call void @llvm.memset.p0.i32(ptr align 1 [[PDST:%.*]], i8 [[VALUE:%.*]], i32 [[TMP0]], i1 false), !tbaa [[TBAA3:![0-9]+]]
+; CHECK-NEXT: tail call void @llvm.memset.p0.i32(ptr align 1 [[PDST:%.*]], i8 [[VALUE:%.*]], i32 range(i32 0, -3) [[TMP0]], i1 false), !tbaa [[TBAA3:![0-9]+]]
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[PDST]], i32 [[TMP0]]
; CHECK-NEXT: br label [[WHILE_END]]
; CHECK: while.end:
@@ -24,7 +24,7 @@ define dso_local void @arm_fill_q7(i8 signext %value, ptr %pDst, i32 %blockSize)
; CHECK-NEXT: [[CMP14_NOT17:%.*]] = icmp eq i32 [[REM]], 0
; CHECK-NEXT: br i1 [[CMP14_NOT17]], label [[WHILE_END18:%.*]], label [[WHILE_BODY16_PREHEADER:%.*]]
; CHECK: while.body16.preheader:
-; CHECK-NEXT: tail call void @llvm.memset.p0.i32(ptr align 1 [[PDST_ADDR_0_LCSSA]], i8 [[VALUE]], i32 [[REM]], i1 false), !tbaa [[TBAA3]]
+; CHECK-NEXT: tail call void @llvm.memset.p0.i32(ptr align 1 [[PDST_ADDR_0_LCSSA]], i8 [[VALUE]], i32 range(i32 0, 4) [[REM]], i1 false), !tbaa [[TBAA3]]
; CHECK-NEXT: br label [[WHILE_END18]]
; CHECK: while.end18:
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll
index 489da5a1ed327..dc10f55ac3bf3 100644
--- a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll
+++ b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll
@@ -8,7 +8,7 @@ define void @PR47852(ptr noundef %d, i32 noundef %c) {
; CHECK-NEXT: br i1 [[CMP_NOT1]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]]
; CHECK: while.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[C]] to i64
-; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[D:%.*]], i8 0, i64 [[TMP0]], i1 false)
+; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[D:%.*]], i8 0, i64 range(i64 0, 4294967296) [[TMP0]], i1 false)
; CHECK-NEXT: br label [[WHILE_END]]
; CHECK: while.end:
; CHECK-NEXT: ret void
>From 9b71cdd8df94bd1a7f5cdb4b97232b6ac6eec498 Mon Sep 17 00:00:00 2001
From: Kuba Mracek <mracek at apple.com>
Date: Fri, 2 Oct 2026 19:46:05 +0100
Subject: [PATCH 3/3] [LoopIdiom] Add tests for unbounded and 32-bit ptr
overflow cases in range attr
---
.../mem-intrinsic-length-range-32bit-ptr.ll | 19 +++++++++
.../LoopIdiom/mem-intrinsic-length-range.ll | 42 +++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
index 9a4d9a393ddcc..58c219472116e 100644
--- a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
+++ b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range-32bit-ptr.ll
@@ -45,3 +45,22 @@ loop:
exit:
ret void
}
+
+; An i64 IV runs up to 2^32 with no guard, so the max backedge-taken count,
+; 2^32 - 1, still fits in the i32 length, but the max trip count doesn't.
+
+define void @i64_iv_trip_count_too_large(ptr %p, i64 %start) {
+; CHECK-LABEL: define void @i64_iv_trip_count_too_large(
+; CHECK: call void @llvm.memset.p0.i32(ptr align 1 {{%.*}}, i8 0, i32 {{%.*}}, i1 false)
+entry:
+ br label %loop
+loop:
+ %i = phi i64 [ %start, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %i
+ store i8 0, ptr %gep, align 1
+ %i.next = add nuw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, 4294967296
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
index 2f64831dd4cba..9ca27b65fed5d 100644
--- a/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
+++ b/llvm/test/Transforms/LoopIdiom/mem-intrinsic-length-range.ll
@@ -220,6 +220,48 @@ exit:
ret void
}
+; Byte stores with an unbounded trip count: the max length, 2^64 - 1, fits in
+; the length's type, but the exclusive end of the range doesn't.
+;
+; void unbounded_bytes(char *p, unsigned long n) {
+; for (unsigned long i = 0; i < n; ++i)
+; p[i] = 0;
+; }
+
+define void @unbounded_bytes(ptr %p, i64 %n) {
+; CHECK-LABEL: define void @unbounded_bytes(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[NONZERO:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: br i1 [[NONZERO]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P]], i8 0, i64 [[N]], i1 false)
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[I_NEXT]] = add nuw i64 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[I_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %nonzero = icmp ne i64 %n, 0
+ br i1 %nonzero, label %loop, label %exit
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %i
+ store i8 0, ptr %gep, align 1
+ %i.next = add nuw i64 %i, 1
+ %cmp = icmp ult i64 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+exit:
+ ret void
+}
+
; void constant_trip_count(int *p) {
; for (unsigned long i = 0; i < 4; ++i)
; p[i] = -1;
More information about the llvm-commits
mailing list