[llvm] [IVDescriptors] Implement MonotonicDescriptor (PR #140720)

Sergey Kachkov via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 12 02:56:26 PST 2025


================
@@ -259,3 +260,133 @@ for.end:
         EXPECT_EQ(Kind, RecurKind::FMax);
       });
 }
+
+TEST(IVDescriptorsTest, MonotonicIntVar) {
+  // Parse the module.
+  LLVMContext Context;
+
+  std::unique_ptr<Module> M =
+      parseIR(Context,
+              R"(define void @foo(i32 %start, i1 %cond, i64 %n) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %for.inc ]
+  %monotonic = phi i32 [ %start, %entry ], [ %monotonic.next, %for.inc ]
+  br i1 %cond, label %if.then, label %for.inc
+
+if.then:
+  %inc = add nsw i32 %monotonic, 1
+  br label %for.inc
+
+for.inc:
+  %monotonic.next = phi i32 [ %inc, %if.then ], [ %monotonic, %for.body ]
+  %i.next = add nuw nsw i64 %i, 1
+  %exitcond.not = icmp eq i64 %i.next, %n
+  br i1 %exitcond.not, label %for.end, label %for.body
+
+for.end:
+  ret void
+})");
+
+  runWithLoopInfoAndSE(
+      *M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+        Function::iterator FI = F.begin();
+        // First basic block is entry - skip it.
+        BasicBlock *Header = &*(++FI);
+        assert(Header->getName() == "for.body");
+        Loop *L = LI.getLoopFor(Header);
+        EXPECT_NE(L, nullptr);
+        BasicBlock::iterator BBI = Header->begin();
+        assert((&*BBI)->getName() == "i");
+        PHINode *Phi = dyn_cast<PHINode>(&*(++BBI));
+        assert(Phi->getName() == "monotonic");
+        BasicBlock *IfThen = &*(++FI);
+        assert(IfThen->getName() == "if.then");
+        Instruction *StepInst = &*(IfThen->begin());
+        assert(StepInst->getName() == "inc");
+        BasicBlock *IfEnd = &*(++FI);
+        assert(IfEnd->getName() == "for.inc");
+        auto *ChainPhi = dyn_cast<PHINode>(&*(IfEnd->begin()));
+        assert(ChainPhi->getName() == "monotonic.next");
+        MonotonicDescriptor Desc;
+        bool IsMonotonicPhi =
+            MonotonicDescriptor::isMonotonicPHI(Phi, L, Desc, SE);
+        EXPECT_TRUE(IsMonotonicPhi);
+        auto &Chain = Desc.getChain();
+        EXPECT_TRUE(Chain.size() == 1 && Chain.contains(ChainPhi));
+        EXPECT_EQ(Desc.getStepInst(), StepInst);
+        EXPECT_EQ(Desc.getPredicateEdge(),
+                  MonotonicDescriptor::Edge(IfThen, IfEnd));
+        auto *StartSCEV = SE.getSCEV(F.getArg(0));
+        auto *StepSCEV = SE.getConstant(StartSCEV->getType(), 1);
+        EXPECT_EQ(Desc.getExpr(),
+                  SE.getAddRecExpr(StartSCEV, StepSCEV, L, SCEV::FlagNW));
+      });
+}
+
+TEST(IVDescriptorsTest, MonotonicPtrVar) {
+  // Parse the module.
+  LLVMContext Context;
+
+  std::unique_ptr<Module> M =
+      parseIR(Context,
+              R"(define void @foo(ptr %start, i1 %cond, i64 %n) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %for.inc ]
+  %monotonic = phi ptr [ %start, %entry ], [ %monotonic.next, %for.inc ]
+  br i1 %cond, label %if.then, label %for.inc
+
+if.then:
+  %inc = getelementptr inbounds i8, ptr %monotonic, i64 4
+  br label %for.inc
+
+for.inc:
+  %monotonic.next = phi ptr [ %inc, %if.then ], [ %monotonic, %for.body ]
+  %i.next = add nuw nsw i64 %i, 1
+  %exitcond.not = icmp eq i64 %i.next, %n
+  br i1 %exitcond.not, label %for.end, label %for.body
+
+for.end:
+  ret void
+})");
+
+  runWithLoopInfoAndSE(
+      *M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+        Function::iterator FI = F.begin();
+        // First basic block is entry - skip it.
+        BasicBlock *Header = &*(++FI);
+        assert(Header->getName() == "for.body");
+        Loop *L = LI.getLoopFor(Header);
+        EXPECT_NE(L, nullptr);
+        BasicBlock::iterator BBI = Header->begin();
+        assert((&*BBI)->getName() == "i");
+        PHINode *Phi = dyn_cast<PHINode>(&*(++BBI));
+        assert(Phi->getName() == "monotonic");
+        BasicBlock *IfThen = &*(++FI);
+        assert(IfThen->getName() == "if.then");
+        Instruction *StepInst = &*(IfThen->begin());
+        assert(StepInst->getName() == "inc");
+        BasicBlock *IfEnd = &*(++FI);
+        assert(IfEnd->getName() == "for.inc");
+        auto *ChainPhi = dyn_cast<PHINode>(&*(IfEnd->begin()));
+        assert(ChainPhi->getName() == "monotonic.next");
+        MonotonicDescriptor Desc;
+        bool IsMonotonicPhi =
+            MonotonicDescriptor::isMonotonicPHI(Phi, L, Desc, SE);
----------------
skachkov-sc wrote:

Added testing of isMonotonicVal() in MonotonicIntVar; this should cover the case we are mostly interested in - when loop has integer monotonic phi which is used as an index in array subscript operator (and we should recognize the access to this array as "monotonic"; when monotonic access has the stride equals of element size that means that the access is "compressed")

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


More information about the llvm-commits mailing list