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

Benjamin Maxwell via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 06:10:06 PDT 2026


================
@@ -1696,3 +1696,222 @@ bool InductionDescriptor::isInductionPHI(
                           /*InductionBinOp=*/nullptr, /*Casts=*/nullptr, Preds);
   return true;
 }
+
+static bool hasUniqueLoopVariantOperand(Value *Cur, Instruction *I,
+                                        const Loop *L) {
+  auto LoopVariantOp = [&](Value *V, bool /*AllowRepeats*/) -> Value * {
+    return L->isLoopInvariant(V) ? nullptr : V;
+  };
+  return find_singleton<Value>(I->operands(), LoopVariantOp) == Cur;
+}
+
+// Recognize monotonic phi variable by matching the following pattern:
+// loop_header:
+//   %monotonic_phi = phi [ %start, %preheader ], [ %chain_phi0, %latch ]
+//   br i1 %do_step, label %step_bb, label %bbN
+//
+// step_bb:
+//   %step = add/gep %monotonic_phi, %step_val
+//   br label %bbN
+//
+// bbN:
+//   %chain_phiN = phi [ %monotonic_phi, %loop_header ], [ %step, %step_bb ]
+//   br label %bb(N-1)
+//
+// ...
+//
+// bb2:
+//   %chain_phi2 = phi [ %monotonic_phi, %pred2 ], [ %chain_phi3, %bb3 ]
+//   br label %bb1
+//
+// bb1:
+//   %chain_phi1 = phi [ %monotonic_phi, %pred1 ], [ %chain_phi2, %bb2 ]
+//   br label %latch
+//
+// latch:
+//   %chain_phi0 = phi [ %monotonic_phi, %pred0 ], [ %chain_phi1, %bb1 ]
+//   br label %loop_header
+//
+// For this pattern, monotonic phi is described by {%start, +, %step}
+// recurrence and predicate is CFG edge %step_bb -> %bbN.
+std::pair<Instruction *, const SCEV *>
+MonotonicDescriptor::CollectMonotonicPHIChain(PHINode *PN, const Loop *L,
+                                              PHINode *BackEdgeInst,
+                                              SmallPtrSetImpl<PHINode *> &Chain,
+                                              Edge &PredEdge,
+                                              ScalarEvolution &SE) {
+  Value *StepOp = nullptr;
+  PHINode *PHIChain = BackEdgeInst;
+
+  while (PHIChain) {
+    Chain.insert(PHIChain);
+    PHINode *NextPHIChain = nullptr;
+    for (auto [Block, Incoming] :
+         zip_equal(PHIChain->blocks(), PHIChain->incoming_values())) {
+      if (Incoming == PN)
+        continue;
+      if (!Incoming->hasOneUse())
+        return {};
+      if (auto *IncomingPHI = dyn_cast<PHINode>(Incoming)) {
+        if (NextPHIChain)
+          return {};
+        NextPHIChain = IncomingPHI;
+        continue;
+      }
+      // Only one update/step is allowed. The unmodified value must be PN.
+      if (StepOp || NextPHIChain)
+        return {};
+      PredEdge = Edge{Block, PHIChain->getParent()};
+      StepOp = Incoming;
+    }
+    PHIChain = NextPHIChain;
+  }
+
+  auto *StepInst = dyn_cast_if_present<Instruction>(StepOp);
+  if (!StepInst)
+    return {};
+
+  // Construct SCEVAddRec for this value.
+  Value *Start = PN->getIncomingValueForBlock(L->getLoopPreheader());
+
+  Value *Step = nullptr;
+  bool StepMatch =
+      PN->getType()->isPointerTy()
+          ? match(StepInst, m_PtrAdd(m_Specific(PN), m_Value(Step)))
+          : match(StepInst, m_Add(m_Specific(PN), m_Value(Step)));
+  if (!StepMatch || !L->isLoopInvariant(Step))
+    return {};
+
+  SCEV::NoWrapFlags WrapFlags = SCEV::FlagAnyWrap;
+  if (auto *GEP = dyn_cast<GEPOperator>(StepInst)) {
+    if (GEP->hasNoUnsignedWrap())
+      WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNUW);
+    if (GEP->hasNoUnsignedSignedWrap())
+      WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNSW);
+  } else if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(StepInst)) {
+    if (OBO->hasNoUnsignedWrap())
+      WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNUW);
+    if (OBO->hasNoSignedWrap())
+      WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNSW);
+  }
+
+  const SCEV *PhiSCEV =
+      SE.getAddRecExpr(SE.getSCEV(Start), SE.getSCEV(Step), L, WrapFlags);
+  return {StepInst, PhiSCEV};
+}
+
+bool MonotonicDescriptor::CollectCompressedMemOpUsers(
+    PHINode *PN, const Loop *L, Edge PredEdge,
+    const SmallPtrSetImpl<PHINode *> &Chain, const SCEV *PhiSCEV,
+    ScalarEvolution &SE,
+    DenseMap<Instruction *, const SCEV *> &CompressedMemOps) {
+  ValueToSCEVMapTy PhiMap{{PN, PhiSCEV}};
+
+  auto GetCompressedPtrSCEV = [&](Instruction *MemI) -> const SCEV * {
+    // Check that the memory operation has the same predicate as the step.
+    // TODO: Relax these restrictions.
----------------
MacDue wrote:

I've removed the "predicated edge" concept from the descriptor and replaced this with a mask check in VPlan. I think that's simpler to reason about with less duplication. 

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


More information about the llvm-commits mailing list