[llvm] [IVDescriptors] Implement MonotonicDescriptor (PR #214490)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 03:20:49 PDT 2026
================
@@ -1696,3 +1696,95 @@ bool InductionDescriptor::isInductionPHI(
/*InductionBinOp=*/nullptr, /*Casts=*/nullptr, Preds);
return true;
}
+
+// Recognize a monotonic PHI variable by matching the following pattern:
+// loop_header:
+// %monotonic_phi = phi [ %start, %preheader ], [ %latch_phi, %latch ]
+// br i1 %do_step, label %step_bb, label %latch
+//
+// step_bb:
+// %step = add/gep %monotonic_phi, %step_val
+// br label %latch
+//
+// latch:
+// %latch_phi = phi [ %monotonic_phi, %loop_header ], [ %step, %step_bb ]
+// br label %loop_header
+bool MonotonicDescriptor::isMonotonicPHI(PHINode *HeaderPHI, const Loop *L,
+ MonotonicDescriptor &Desc,
+ ScalarEvolution &SE) {
+ BasicBlock *Preheader = L->getLoopPreheader();
+ if (!Preheader)
+ return false;
+
+ BasicBlock *Latch = L->getLoopLatch();
+ if (!Latch || !HeaderPHI->getType()->isIntOrPtrTy() ||
+ HeaderPHI->getParent() != L->getHeader())
+ return false;
+
+ auto *BackedgePHI =
+ dyn_cast<PHINode>(HeaderPHI->getIncomingValueForBlock(Latch));
+ if (!BackedgePHI)
+ return false;
+
+ // Ensure the only users of the backedge PHI are outside the loop or the
+ // header PHI.
+ for (User *U : BackedgePHI->users()) {
+ auto *UI = dyn_cast<Instruction>(U);
+ if (UI && UI != HeaderPHI && L->contains(UI))
+ return false;
+ }
+
+ // Find the step operation used to increment the value of the monotonic PHI.
+ // TODO: Support chains of PHIs.
+ Value *StepOp = find_singleton<Value>(
+ BackedgePHI->incoming_values(),
+ [&](Use &Incoming, bool /*AllowRepeats*/) {
+ return Incoming != HeaderPHI ? Incoming.get() : nullptr;
+ });
+ if (!StepOp || !StepOp->hasOneUse())
+ return false;
+
+ auto *StepInst = dyn_cast<Instruction>(StepOp);
+ if (!StepInst)
+ return false;
+
+ Value *Step = nullptr;
+ bool StepMatch =
+ HeaderPHI->getType()->isPointerTy()
+ ? match(StepInst, m_PtrAdd(m_Specific(HeaderPHI), m_Value(Step)))
+ : match(StepInst, m_c_Add(m_Specific(HeaderPHI), m_Value(Step)));
+ if (!StepMatch || !L->isLoopInvariant(Step))
+ return false;
+
+ // Ensure GEP offsets are extended to the size of the PHI.
+ const SCEV *StepSCEV = SE.getTruncateOrSignExtend(
+ SE.getSCEV(Step), SE.getEffectiveSCEVType(HeaderPHI->getType()));
+
+ if (StepSCEV->isZero())
+ return false;
+
+ Value *Start = HeaderPHI->getIncomingValueForBlock(Preheader);
+ const SCEV *StartSCEV = SE.getSCEV(Start);
+
+ assert(StepSCEV->getType() == StepSCEV->getType());
+
+ SCEV::NoWrapFlags NoWrapFlags = SCEV::FlagAnyWrap;
+ if (auto *GEP = dyn_cast<GEPOperator>(StepInst)) {
+ if (GEP->hasNoUnsignedWrap())
+ NoWrapFlags = ScalarEvolution::setFlags(NoWrapFlags, SCEV::FlagNUW);
+ if (GEP->hasNoUnsignedSignedWrap())
+ NoWrapFlags = ScalarEvolution::setFlags(NoWrapFlags, SCEV::FlagNSW);
----------------
lukel97 wrote:
I don't think nusw -> nsw, is it safer to drop this case for now?
https://github.com/llvm/llvm-project/pull/214490
More information about the llvm-commits
mailing list