[llvm] [IndVarSimplify] Fix Masking Issue by Adding nsw/nuw Flags to Trunc Instruction (PR #150179)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 2 04:05:39 PDT 2025


https://github.com/buggfg updated https://github.com/llvm/llvm-project/pull/150179

>From c6a39d83ea7c90dd00bb1a6832816d5d69be02f7 Mon Sep 17 00:00:00 2001
From: buggfg <3171290993 at qq.com>
Date: Wed, 23 Jul 2025 15:20:40 +0800
Subject: [PATCH 1/5] [IndVarSimplify] Fix Masking Issue  by Adding nsw/nuw
 Flags to Trunc Instruction

---
 llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 21 ++++++++++++++++++-
 .../IndVarSimplify/X86/eliminate-trunc.ll     |  8 +++----
 .../Transforms/IndVarSimplify/lftr-pr41998.ll |  2 +-
 llvm/test/Transforms/IndVarSimplify/lftr.ll   |  2 +-
 .../constraint-elimination-placement.ll       |  3 +--
 5 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 334c911191cb8..04a1f4831b8d8 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1049,9 +1049,28 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
     if (Extended) {
       bool Discard;
       L->makeLoopInvariant(ExitCnt, Discard);
-    } else
+    } else{
       CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
                                       "lftr.wideiv");
+
+      // Set the correct wrap flag to avoid the masking issue.
+      Instruction *TruncInst = dyn_cast<Instruction>(CmpIndVar);
+      
+      // The TruncatedIV is incrementing.
+      if (const SCEVAddRecExpr *TruncAR =
+              dyn_cast<SCEVAddRecExpr>(TruncatedIV)) {
+        // If TruncIV does not cause self-wrap, explicitly add the nsw and nuw
+        // flags to TruncInst.
+        if (TruncAR->hasNoSelfWrap()) {
+          TruncInst->setHasNoSignedWrap();
+          TruncInst->setHasNoUnsignedWrap();
+        } else if (TruncAR->hasNoSignedWrap()) {
+          TruncInst->setHasNoSignedWrap();
+        } else if (TruncAR->hasNoUnsignedWrap()) {
+          TruncInst->setHasNoUnsignedWrap();
+        }
+      }
+    }  
   }
   LLVM_DEBUG(dbgs() << "INDVARS: Rewriting loop exit condition to:\n"
                     << "      LHS:" << *CmpIndVar << '\n'
diff --git a/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll b/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
index 565ac5c8743d4..7e7a3f192f998 100644
--- a/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
+++ b/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
@@ -227,7 +227,7 @@ define void @test_01_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -255,7 +255,7 @@ define void @test_02_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 4294967294, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -304,7 +304,7 @@ define void @test_04_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -332,7 +332,7 @@ define void @test_05_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll b/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
index b7f4756b2757f..376ef1ac5ffac 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
@@ -13,7 +13,7 @@ define void @test_int(i32 %start, ptr %p) {
 ; CHECK-NEXT:    [[I2:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I2_INC:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[I2_INC]] = add nuw nsw i32 [[I2]], 1
 ; CHECK-NEXT:    store volatile i32 [[I2_INC]], ptr [[P:%.*]], align 4
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i32 [[I2_INC]] to i3
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i32 [[I2_INC]] to i3
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i3 [[LFTR_WIDEIV]], [[TMP1]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[END:%.*]], label [[LOOP]]
 ; CHECK:       end:
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr.ll b/llvm/test/Transforms/IndVarSimplify/lftr.ll
index 5ee62ba357ab6..cfa4baa2d3b11 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr.ll
@@ -415,7 +415,7 @@ define void @wide_trip_count_test1(ptr %autoc,
 ; CHECK-NEXT:    [[ADD3:%.*]] = fadd float [[TEMP2]], [[MUL]]
 ; CHECK-NEXT:    store float [[ADD3]], ptr [[ARRAYIDX2]], align 4
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[INDVARS_IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[SUB]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[FOR_END_LOOPEXIT:%.*]]
 ; CHECK:       for.end.loopexit:
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
index bbdbd95c6017a..ddd98f21c36a4 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
@@ -33,8 +33,7 @@ define i1 @test_order_1(ptr %this, ptr noalias %other, i1 %tobool9.not, i32 %cal
 ; CHECK-NEXT:    br i1 [[CMP44]], label [[FOR_BODY45]], label [[FOR_COND]]
 ; CHECK:       for.inc57:
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[INDVARS_IV_NEXT]], 4294967295
-; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[TMP1]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[INDVARS_IV]], 0
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_COND41_PREHEADER_PREHEADER]], label [[FOR_COND41_PREHEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret i1 false

>From ac8981b9d1d7f053d324f22ac63c00720a5ab914 Mon Sep 17 00:00:00 2001
From: bernadate <3171290993 at qq.com>
Date: Thu, 24 Jul 2025 17:28:29 +0800
Subject: [PATCH 2/5] Update llvm/lib/Transforms/Scalar/IndVarSimplify.cpp

Co-authored-by: Luke Lau <luke_lau at icloud.com>
---
 llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 04a1f4831b8d8..bd04981f8b0d4 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1050,11 +1050,8 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
       bool Discard;
       L->makeLoopInvariant(ExitCnt, Discard);
     } else{
-      CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
-                                      "lftr.wideiv");
-
-      // Set the correct wrap flag to avoid the masking issue.
-      Instruction *TruncInst = dyn_cast<Instruction>(CmpIndVar);
+CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
+"lftr.wideiv", TruncAR->hasNoUnsignedWrap(), TruncAR->hasSignedWrap());
       
       // The TruncatedIV is incrementing.
       if (const SCEVAddRecExpr *TruncAR =

>From 2e146339ad528e29186d4e0dc1b1cc1d6f10f702 Mon Sep 17 00:00:00 2001
From: buggfg <3171290993 at qq.com>
Date: Tue, 12 Aug 2025 14:27:26 +0800
Subject: [PATCH 3/5] Revert "[IndVarSimplify] Fix Masking Issue  by Adding
 nsw/nuw Flags to Trunc Instruction"

This reverts commit c6a39d83ea7c90dd00bb1a6832816d5d69be02f7.

# Conflicts:
#	llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
---
 llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 22 +++----------------
 .../IndVarSimplify/X86/eliminate-trunc.ll     |  8 +++----
 .../Transforms/IndVarSimplify/lftr-pr41998.ll |  2 +-
 llvm/test/Transforms/IndVarSimplify/lftr.ll   |  2 +-
 .../constraint-elimination-placement.ll       |  3 ++-
 5 files changed, 11 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index bd04981f8b0d4..334c911191cb8 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1049,25 +1049,9 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
     if (Extended) {
       bool Discard;
       L->makeLoopInvariant(ExitCnt, Discard);
-    } else{
-CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
-"lftr.wideiv", TruncAR->hasNoUnsignedWrap(), TruncAR->hasSignedWrap());
-      
-      // The TruncatedIV is incrementing.
-      if (const SCEVAddRecExpr *TruncAR =
-              dyn_cast<SCEVAddRecExpr>(TruncatedIV)) {
-        // If TruncIV does not cause self-wrap, explicitly add the nsw and nuw
-        // flags to TruncInst.
-        if (TruncAR->hasNoSelfWrap()) {
-          TruncInst->setHasNoSignedWrap();
-          TruncInst->setHasNoUnsignedWrap();
-        } else if (TruncAR->hasNoSignedWrap()) {
-          TruncInst->setHasNoSignedWrap();
-        } else if (TruncAR->hasNoUnsignedWrap()) {
-          TruncInst->setHasNoUnsignedWrap();
-        }
-      }
-    }  
+    } else
+      CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
+                                      "lftr.wideiv");
   }
   LLVM_DEBUG(dbgs() << "INDVARS: Rewriting loop exit condition to:\n"
                     << "      LHS:" << *CmpIndVar << '\n'
diff --git a/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll b/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
index 7e7a3f192f998..565ac5c8743d4 100644
--- a/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
+++ b/llvm/test/Transforms/IndVarSimplify/X86/eliminate-trunc.ll
@@ -227,7 +227,7 @@ define void @test_01_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -255,7 +255,7 @@ define void @test_02_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 4294967294, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -304,7 +304,7 @@ define void @test_04_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
@@ -332,7 +332,7 @@ define void @test_05_unsigned(i32 %n) {
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]]
 ; CHECK:       exit:
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll b/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
index 376ef1ac5ffac..b7f4756b2757f 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr-pr41998.ll
@@ -13,7 +13,7 @@ define void @test_int(i32 %start, ptr %p) {
 ; CHECK-NEXT:    [[I2:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I2_INC:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[I2_INC]] = add nuw nsw i32 [[I2]], 1
 ; CHECK-NEXT:    store volatile i32 [[I2_INC]], ptr [[P:%.*]], align 4
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i32 [[I2_INC]] to i3
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i32 [[I2_INC]] to i3
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i3 [[LFTR_WIDEIV]], [[TMP1]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[END:%.*]], label [[LOOP]]
 ; CHECK:       end:
diff --git a/llvm/test/Transforms/IndVarSimplify/lftr.ll b/llvm/test/Transforms/IndVarSimplify/lftr.ll
index cfa4baa2d3b11..5ee62ba357ab6 100644
--- a/llvm/test/Transforms/IndVarSimplify/lftr.ll
+++ b/llvm/test/Transforms/IndVarSimplify/lftr.ll
@@ -415,7 +415,7 @@ define void @wide_trip_count_test1(ptr %autoc,
 ; CHECK-NEXT:    [[ADD3:%.*]] = fadd float [[TEMP2]], [[MUL]]
 ; CHECK-NEXT:    store float [[ADD3]], ptr [[ARRAYIDX2]], align 4
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc nuw nsw i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT:    [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], [[SUB]]
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[FOR_END_LOOPEXIT:%.*]]
 ; CHECK:       for.end.loopexit:
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
index ddd98f21c36a4..bbdbd95c6017a 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
@@ -33,7 +33,8 @@ define i1 @test_order_1(ptr %this, ptr noalias %other, i1 %tobool9.not, i32 %cal
 ; CHECK-NEXT:    br i1 [[CMP44]], label [[FOR_BODY45]], label [[FOR_COND]]
 ; CHECK:       for.inc57:
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[INDVARS_IV]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[INDVARS_IV_NEXT]], 4294967295
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[TMP1]], 1
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_COND41_PREHEADER_PREHEADER]], label [[FOR_COND41_PREHEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret i1 false

>From 4578509dc7f49328664829f1ce227de470e47fe6 Mon Sep 17 00:00:00 2001
From: buggfg <3171290993 at qq.com>
Date: Tue, 12 Aug 2025 14:36:40 +0800
Subject: [PATCH 4/5] Solve the unnecessary masking issue

---
 llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 57 ++++++++++++++++++-
 .../constraint-elimination-placement.ll       |  3 +-
 2 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 334c911191cb8..df527ea2fd162 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -160,6 +160,8 @@ class IndVarSimplify {
 
   bool sinkUnusedInvariants(Loop *L);
 
+  bool isIVInitValTargetType(Value *InitVal, unsigned TargetTypeSize);
+
 public:
   IndVarSimplify(LoopInfo *LI, ScalarEvolution *SE, DominatorTree *DT,
                  const DataLayout &DL, TargetLibraryInfo *TLI,
@@ -817,6 +819,37 @@ static bool isLoopCounter(PHINode* Phi, Loop *L,
           isa<SCEVAddRecExpr>(SE->getSCEV(IncV)));
 }
 
+/// Determine if the initial value of IV is within the range of TargetTypeSize.
+/// If it is a constant, check if it falls within the representable range. If
+/// not, verify whether the width of the initial type exceeds TargetTypeSize.
+bool IndVarSimplify::isIVInitValTargetType(Value *InitVal,
+                                           unsigned TargetTypeSize) {
+
+  unsigned InitTypeSize = SE->getTypeSizeInBits(InitVal->getType());
+  if (InitTypeSize <= TargetTypeSize)
+    return true;
+
+  // If the initial value of the IV is a constant, check whether it is within
+  // the range.
+  if (auto *CI = dyn_cast<ConstantInt>(InitVal)) {
+
+    APInt Value = CI->getValue();
+    APInt MinRange = APInt(TargetTypeSize, 1ULL << (TargetTypeSize - 1));
+    APInt MaxRange = APInt(TargetTypeSize, (1ULL << (TargetTypeSize - 1)) - 1);
+    // Check if the constant is within a valid range
+    return (Value.sle(MaxRange) && Value.sge(-MinRange));
+  }
+
+  // Check the initial type of the initial value.
+  if (auto *SextInst = dyn_cast<SExtInst>(InitVal)) {
+    return isIVInitValTargetType(SextInst->getOperand(0), TargetTypeSize);
+  }
+  if (auto *ZextInst = dyn_cast<ZExtInst>(InitVal)) {
+    return isIVInitValTargetType(ZextInst->getOperand(0), TargetTypeSize);
+  }
+  return false;
+}
+
 /// Search the loop header for a loop counter (anadd rec w/step of one)
 /// suitable for use by LFTR.  If multiple counters are available, select the
 /// "best" one based profitable heuristics.
@@ -1049,9 +1082,27 @@ linearFunctionTestReplace(Loop *L, BasicBlock *ExitingBB,
     if (Extended) {
       bool Discard;
       L->makeLoopInvariant(ExitCnt, Discard);
-    } else
-      CmpIndVar = Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(),
-                                      "lftr.wideiv");
+    } else {
+      // The design of LFTR requires:
+      // 1. The IV to be a LoopCounter, ensuring a step of 1.
+      // 2. The ICmpInst::Predicate can only be eq or ne, meaning that
+      //    ExitCnt must represent the final value of IV.
+      // Given these conditions, if the initial value 'start' of IV does not
+      // exceed ExitCntSize, then the range [start, end) of IV will stay
+      // within ExitCntSize. Consequently, the truncation will not result in
+      // signed or unsigned overflow.
+      // By legally adding the nsw/nuw flag to the Trunc instruction, avoid the
+      // unnecessary masking issue.
+      Value *InitVal = IndVar->getIncomingValueForBlock(L->getLoopPreheader());
+      if (isIVInitValTargetType(InitVal, ExitCntSize))
+        CmpIndVar = Builder.CreateTrunc(
+            CmpIndVar, ExitCnt->getType(), "lftr.wideiv",
+            cast<ICmpInst>(BI->getCondition())->isUnsigned(),
+            cast<ICmpInst>(BI->getCondition())->isSigned());
+      else
+        CmpIndVar =
+            Builder.CreateTrunc(CmpIndVar, ExitCnt->getType(), "lftr.wideiv");
+    }
   }
   LLVM_DEBUG(dbgs() << "INDVARS: Rewriting loop exit condition to:\n"
                     << "      LHS:" << *CmpIndVar << '\n'
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
index bbdbd95c6017a..ddd98f21c36a4 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll
@@ -33,8 +33,7 @@ define i1 @test_order_1(ptr %this, ptr noalias %other, i1 %tobool9.not, i32 %cal
 ; CHECK-NEXT:    br i1 [[CMP44]], label [[FOR_BODY45]], label [[FOR_COND]]
 ; CHECK:       for.inc57:
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[INDVARS_IV_NEXT]], 4294967295
-; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[TMP1]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[INDVARS_IV]], 0
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_COND41_PREHEADER_PREHEADER]], label [[FOR_COND41_PREHEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret i1 false

>From 23bd524ba9c436c145b8501e859e9fe07782130a Mon Sep 17 00:00:00 2001
From: buggfg <3171290993 at qq.com>
Date: Tue, 2 Sep 2025 19:05:17 +0800
Subject: [PATCH 5/5] Add a test

---
 .../Transforms/IndVarSimplify/lftr-masking.ll | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 llvm/test/Transforms/IndVarSimplify/lftr-masking.ll

diff --git a/llvm/test/Transforms/IndVarSimplify/lftr-masking.ll b/llvm/test/Transforms/IndVarSimplify/lftr-masking.ll
new file mode 100644
index 0000000000000..af66002f02dc6
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/lftr-masking.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes='indvars' -S  | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+
+define dso_local void @func(ptr noundef captures(none) %0, i32 noundef %1) local_unnamed_addr {
+; CHECK-LABEL: define dso_local void @func(ptr noundef captures(none) %0, i32 noundef %1) local_unnamed_addr {
+; CHECK-NEXT:  [[CHECK:%.*]] = icmp slt i32 %1, 100
+; CHECK-NEXT:  br i1 [[CHECK]], label [[LOOP_PREHEADER:%.*]], label [[LOOP_EXIT:%.*]]
+; CHECK:       loop.preheader:
+; CHECK-NEXT:  [[SEXT_START:%.*]] = sext i32 %1 to i64
+; CHECK-NEXT:  br label [[LOOP_BODY:%.*]]
+; CHECK:       loop.exit.loopexit:
+; CHECK-NEXT:  br label [[LOOPEXIT:%.*]]
+; CHECK:       loop.exit:
+; CHECK-NEXT:  ret void
+; CHECK:       loop.body:
+; CHECK-NEXT:  [[IV:%.*]] = phi i64 [ [[POSTINC:%.*]], [[LOOP_BODY]] ], [ [[SEXT_START:%.*]], [[LOOP_PREHEADER]] ]
+; CHECK-NEXT:  [[GEP:%.*]] = getelementptr inbounds i32, ptr %0, i64 [[IV]]
+; CHECK-NEXT:  [[LOAD:%.*]] = load i32, ptr [[GEP]], align 4
+; CHECK-NEXT:  [[DATA:%.*]] = add nsw i32 [[LOAD]], 1
+; CHECK-NEXT:  store i32 [[DATA]], ptr [[GEP]], align 4
+; CHECK-NEXT:  [[POSTINC:%.*]] = add nsw i64 [[IV]], 1
+; CHECK-NEXT:  [[LFTR_WIDEIV:%.*]] = trunc nsw i64 [[POSTINC]] to i32
+; CHECK-NEXT:  [[EXITCOND:%.*]] = icmp ne i32 [[LFTR_WIDEIV]], 100
+; CHECK-NEXT:  br i1 [[EXITCOND]], label [[LOOP_BODY]], label [[LOOP_EXIT_LOOPEXIT:%.*]]
+; CHECK-NEXT:  }
+  %3 = icmp slt i32 %1, 100
+  br i1 %3, label %loop.preheader, label %loop.exit
+loop.preheader:                                                
+  br label %loop.body
+
+loop.exit: 
+  ret void
+loop.body:                                                
+  %8 = phi i32 [ %13, %loop.body ], [ %1, %loop.preheader ]
+  %9 = sext i32 %8 to i64
+  %10 = getelementptr inbounds i32, ptr %0, i64 %9
+  %11 = load i32, ptr %10, align 4
+  %12 = add nsw i32 %11, 1
+  store i32 %12, ptr %10, align 4
+  %13 = add nsw i32 %8, 1
+  %14 = icmp slt i32 %8, 99
+  br i1 %14, label %loop.body, label %loop.exit
+}



More information about the llvm-commits mailing list