[llvm] [IndVarSimplify] Keep additional nuw and nsw flags during LinearFunctionTestReplace (PR #177433)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 30 18:44:36 PDT 2026
https://github.com/yasmincs updated https://github.com/llvm/llvm-project/pull/177433
>From 8fdbef5107d19a6b62c8db8a94c4c80fd62071ca Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Thu, 22 Jan 2026 18:50:02 +0000
Subject: [PATCH 1/5] Kept additional nsw nuw flags during
linearfunctiontestreplace
---
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 39 ++++++++++++++++++-
.../IndVarSimplify/lftr-multi-exit.ll | 6 +--
...ple-unreachable-exits-for-vectorization.ll | 4 +-
.../loop-rotation-vs-common-code-hoisting.ll | 2 +-
4 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index f377c192371ea..b049eab5ffcea 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1095,10 +1095,45 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
// dead, it could not be poison on the first iteration in the first place.)
if (auto *BO = dyn_cast<BinaryOperator>(IncVar)) {
const SCEVAddRecExpr *AR = cast<SCEVAddRecExpr>(SE->getSCEV(IncVar));
+
+ // However, if we successfully computed an ExitCount, we can infer nowrap
+ // properties from the original loop exit condition. If the original
+ // comparison was signed and SCEV computed an exit count, then NSW is safe.
+ // Similarly, if it was unsigned, NUW is safe. This is because SCEV's exit
+ // count analysis proves the loop exits before overflow occurs for that
+ // signedness.
+ bool KeepNSW = false;
+ bool KeepNUW = false;
+
+ BranchInst *BI = cast<BranchInst>(ExitingBB->getTerminator());
+ if (auto *OrigCmp = dyn_cast<ICmpInst>(BI->getCondition())) {
+ // Check if the chosen IV is actually used in the original comparison.
+ bool IVUsedInOriginalCmp = (OrigCmp->getOperand(0) == IndVar ||
+ OrigCmp->getOperand(0) == IncVar ||
+ OrigCmp->getOperand(1) == IndVar ||
+ OrigCmp->getOperand(1) == IncVar);
+
+ if (IVUsedInOriginalCmp) {
+ ICmpInst::Predicate Pred = OrigCmp->getPredicate();
+
+ // Don't keep flags if the predicate sign was changed by SimplifyIndvar.
+ // The samesign flag indicates the predicate was originally signed but
+ // was canonicalized to unsigned (e.g., slt -> samesign ult).
+ if (!OrigCmp->hasSameSign()) {
+ // If the original comparison was signed, SCEV proved NSW is safe.
+ KeepNSW = ICmpInst::isSigned(Pred);
+ // If the original comparison was unsigned, SCEV proved NUW is safe.
+ KeepNUW = ICmpInst::isUnsigned(Pred);
+ }
+ }
+ }
+
+ // Use SCEV's analysis, but also keep flags if the original comparison
+ // guarantees safety for that signedness.
if (BO->hasNoUnsignedWrap())
- BO->setHasNoUnsignedWrap(AR->hasNoUnsignedWrap());
+ BO->setHasNoUnsignedWrap(AR->hasNoUnsignedWrap() || KeepNUW);
if (BO->hasNoSignedWrap())
- BO->setHasNoSignedWrap(AR->hasNoSignedWrap());
+ BO->setHasNoSignedWrap(AR->hasNoSignedWrap() || KeepNSW);
}
Value *ExitCnt = genLoopLimit(
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr-multi-exit.ll b/llvm/test/Transforms/IndVarSimplify/lftr-multi-exit.ll
index 0898c6c2dae34..a2cdcaaff59db 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr-multi-exit.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr-multi-exit.ll
@@ -172,7 +172,7 @@ define void @unanalyzeable_latch(i32 %n) {
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]]
; CHECK: latch:
-; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
; CHECK-NEXT: store i32 [[IV]], ptr @A, align 4
; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, ptr @A, align 4
; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[VOL]], 1000
@@ -208,7 +208,7 @@ define void @single_exit_no_latch(i32 %n) {
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]]
; CHECK: latch:
-; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
; CHECK-NEXT: store i32 [[IV]], ptr @A, align 4
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
@@ -247,7 +247,7 @@ define void @no_latch_exit(i32 %n, i32 %m) {
; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LATCH]], label [[EXIT]]
; CHECK: latch:
; CHECK-NEXT: store volatile i32 [[IV]], ptr @A, align 4
-; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/peel-multiple-unreachable-exits-for-vectorization.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/peel-multiple-unreachable-exits-for-vectorization.ll
index 2fe49a31b7722..4a0d81bb4fc4a 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/peel-multiple-unreachable-exits-for-vectorization.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/peel-multiple-unreachable-exits-for-vectorization.ll
@@ -79,7 +79,7 @@ define i64 @sum_2_at_with_int_conversion(ptr %A, ptr %B, i64 %N) {
; CHECK-NEXT: [[LV_I9:%.*]] = load i64, ptr [[GEP_IDX_I8]], align 8
; CHECK-NEXT: [[ADD:%.*]] = add i64 [[LV_I]], [[SUM]]
; CHECK-NEXT: [[SUM_NEXT]] = add i64 [[ADD]], [[LV_I9]]
-; CHECK-NEXT: [[IV_NEXT]] = add nuw i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[IV]], [[SMAX]]
; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[EXIT]], label [[LOOP]], !llvm.loop [[LOOP3:![0-9]+]]
; CHECK: error.i:
@@ -203,7 +203,7 @@ define i64 @sum_3_at_with_int_conversion(ptr %A, ptr %B, ptr %C, i64 %N) {
; CHECK-NEXT: [[ADD_1:%.*]] = add i64 [[LV_I]], [[SUM]]
; CHECK-NEXT: [[ADD_2:%.*]] = add i64 [[ADD_1]], [[LV_I9]]
; CHECK-NEXT: [[SUM_NEXT]] = add i64 [[ADD_2]], [[LV_I20]]
-; CHECK-NEXT: [[IV_NEXT]] = add nuw i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[IV]], [[SMAX]]
; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[EXIT]], label [[LOOP]], !llvm.loop [[LOOP5:![0-9]+]]
; CHECK: error.i:
diff --git a/llvm/test/Transforms/PhaseOrdering/loop-rotation-vs-common-code-hoisting.ll b/llvm/test/Transforms/PhaseOrdering/loop-rotation-vs-common-code-hoisting.ll
index 5ff57eae3a773..9333be4e6c4c7 100644
--- a/llvm/test/Transforms/PhaseOrdering/loop-rotation-vs-common-code-hoisting.ll
+++ b/llvm/test/Transforms/PhaseOrdering/loop-rotation-vs-common-code-hoisting.ll
@@ -59,7 +59,7 @@ define void @_Z4loopi(i32 %width) {
; HOIST-NEXT: br label [[RETURN]]
; HOIST: for.body:
; HOIST-NEXT: tail call void @f1()
-; HOIST-NEXT: [[INC]] = add nuw i32 [[I_0]], 1
+; HOIST-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1
; HOIST-NEXT: br label [[FOR_COND]]
; HOIST: return:
; HOIST-NEXT: ret void
>From 402c2e7663310a4b85d1b52fa0600b5d4855a2ee Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Tue, 21 Apr 2026 18:56:32 +0000
Subject: [PATCH 2/5] updated lit test
---
.../Transforms/IndVarSimplify/X86/loop-invariant-conditions.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/IndVarSimplify/X86/loop-invariant-conditions.ll b/llvm/test/Transforms/IndVarSimplify/X86/loop-invariant-conditions.ll
index 2e19311294fcb..65527329a87ca 100644
--- a/llvm/test/Transforms/IndVarSimplify/X86/loop-invariant-conditions.ll
+++ b/llvm/test/Transforms/IndVarSimplify/X86/loop-invariant-conditions.ll
@@ -526,7 +526,7 @@ define void @test3_neg(i64 %start) {
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[LOOP]] ]
-; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nsw i64 [[INDVARS_IV]], 1
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[TMP0]]
; CHECK-NEXT: br i1 [[CMP1]], label [[LOOP]], label [[FOR_END:%.*]]
; CHECK: for.end:
>From 82fe2476f459f40807eb0d79631d9b9a63dcfe43 Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Tue, 21 Apr 2026 22:39:43 +0000
Subject: [PATCH 3/5] updated to CondBrInst
---
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 1eb08736c60f3..49d6d3b6b99fb 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1120,7 +1120,7 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
bool KeepNSW = false;
bool KeepNUW = false;
- BranchInst *BI = cast<BranchInst>(ExitingBB->getTerminator());
+ CondBrInst *BI = cast<CondBrInst>(ExitingBB->getTerminator());
if (auto *OrigCmp = dyn_cast<ICmpInst>(BI->getCondition())) {
// Check if the chosen IV is actually used in the original comparison.
bool IVUsedInOriginalCmp = (OrigCmp->getOperand(0) == IndVar ||
>From 64b7a81e5f6204df83b40de188abe16efe3138cf Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Thu, 23 Apr 2026 23:08:04 +0000
Subject: [PATCH 4/5] Included samesign for nsw
---
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 49d6d3b6b99fb..cb4bf64791311 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1131,12 +1131,13 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
if (IVUsedInOriginalCmp) {
ICmpInst::Predicate Pred = OrigCmp->getPredicate();
- // Don't keep flags if the predicate sign was changed by SimplifyIndvar.
- // The samesign flag indicates the predicate was originally signed but
- // was canonicalized to unsigned (e.g., slt -> samesign ult).
+ // If the original comparison was signed, SCEV proved NSW is safe.
+ KeepNSW = ICmpInst::isSigned(Pred);
+ // Don't keep nuw flag if the predicate sign was changed by
+ // SimplifyIndvar. The samesign flag indicates the predicate was
+ // originally signed but was canonicalized to unsigned (e.g., slt ->
+ // samesign ult).
if (!OrigCmp->hasSameSign()) {
- // If the original comparison was signed, SCEV proved NSW is safe.
- KeepNSW = ICmpInst::isSigned(Pred);
// If the original comparison was unsigned, SCEV proved NUW is safe.
KeepNUW = ICmpInst::isUnsigned(Pred);
}
>From 0517e44605010cea228457510c51b856cb218689 Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Fri, 1 May 2026 01:43:56 +0000
Subject: [PATCH 5/5] excluded samesign flag and added tests
---
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 11 +-
.../Transforms/IndVarSimplify/lftr-pr31181.ll | 110 ++++++++++++++++++
2 files changed, 115 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index cb4bf64791311..49d6d3b6b99fb 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1131,13 +1131,12 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
if (IVUsedInOriginalCmp) {
ICmpInst::Predicate Pred = OrigCmp->getPredicate();
- // If the original comparison was signed, SCEV proved NSW is safe.
- KeepNSW = ICmpInst::isSigned(Pred);
- // Don't keep nuw flag if the predicate sign was changed by
- // SimplifyIndvar. The samesign flag indicates the predicate was
- // originally signed but was canonicalized to unsigned (e.g., slt ->
- // samesign ult).
+ // Don't keep flags if the predicate sign was changed by SimplifyIndvar.
+ // The samesign flag indicates the predicate was originally signed but
+ // was canonicalized to unsigned (e.g., slt -> samesign ult).
if (!OrigCmp->hasSameSign()) {
+ // If the original comparison was signed, SCEV proved NSW is safe.
+ KeepNSW = ICmpInst::isSigned(Pred);
// If the original comparison was unsigned, SCEV proved NUW is safe.
KeepNUW = ICmpInst::isUnsigned(Pred);
}
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr-pr31181.ll b/llvm/test/Transforms/IndVarSimplify/lftr-pr31181.ll
index e836a7dfccea0..7c11d09f8b928 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr-pr31181.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr-pr31181.ll
@@ -358,3 +358,113 @@ always_taken:
for.end:
ret i32 0
}
+
+; Below are similar tests with explicit samesign flag
+
+define i32 @test_samesign_ult_negative_iv() {
+; CHECK-LABEL: @test_samesign_ult_negative_iv(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ -2, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; CHECK-NEXT: store i32 [[STOREMERGE]], ptr @a, align 4
+; CHECK-NEXT: [[INC]] = add nsw i32 [[STOREMERGE]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[INC]], 0
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %loop
+
+loop:
+ %storemerge = phi i32 [ -2, %entry ], [ %inc, %loop ]
+ store i32 %storemerge, ptr @a
+ %cmp = icmp samesign ult i32 %storemerge, -1
+ %inc = add nuw nsw i32 %storemerge, 1
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret i32 0
+}
+
+define i32 @test_samesign_slt_negative_iv() {
+; CHECK-LABEL: @test_samesign_slt_negative_iv(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ -2, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; CHECK-NEXT: store i32 [[STOREMERGE]], ptr @a, align 4
+; CHECK-NEXT: [[INC]] = add nsw i32 [[STOREMERGE]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[INC]], 0
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %loop
+
+loop:
+ %storemerge = phi i32 [ -2, %entry ], [ %inc, %loop ]
+ store i32 %storemerge, ptr @a
+ %cmp = icmp samesign slt i32 %storemerge, -1
+ %inc = add nuw nsw i32 %storemerge, 1
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret i32 0
+}
+
+define i32 @test_samesign_ult_positive_iv() {
+; CHECK-LABEL: @test_samesign_ult_positive_iv(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; CHECK-NEXT: store i32 [[STOREMERGE]], ptr @a, align 4
+; CHECK-NEXT: [[INC]] = add nuw i32 [[STOREMERGE]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[INC]], -2147483648
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %loop
+
+loop:
+ %storemerge = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ store i32 %storemerge, ptr @a
+ %cmp = icmp samesign ult i32 %storemerge, 2147483647
+ %inc = add nuw nsw i32 %storemerge, 1
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret i32 0
+}
+
+define i32 @test_samesign_slt_positive_iv() {
+; CHECK-LABEL: @test_samesign_slt_positive_iv(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; CHECK-NEXT: store i32 [[STOREMERGE]], ptr @a, align 4
+; CHECK-NEXT: [[INC]] = add nuw i32 [[STOREMERGE]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[INC]], -2147483648
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %loop
+
+loop:
+ %storemerge = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ store i32 %storemerge, ptr @a
+ %cmp = icmp samesign slt i32 %storemerge, 2147483647
+ %inc = add nuw nsw i32 %storemerge, 1
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret i32 0
+}
More information about the llvm-commits
mailing list