diff options
author | Sergey Kachkov <sergey.kachkov@syntacore.com> | 2025-01-29 15:27:01 +0300 |
---|---|---|
committer | Sergey Kachkov <sergey.kachkov@syntacore.com> | 2025-05-19 17:52:27 +0300 |
commit | 9c543ba841411f5dfc05e27da391cb0bb916a1ae (patch) | |
tree | 176ffefcd325c17bbb94e41070c4ae1bc38c964e | |
parent | 1532ee6916ef16627bafddced391c0b5a31390fe (diff) | |
download | llvm-users/skachkov-sc/monotonic-descriptor.zip llvm-users/skachkov-sc/monotonic-descriptor.tar.gz llvm-users/skachkov-sc/monotonic-descriptor.tar.bz2 |
[IVDescriptors] Implement MonotonicDescriptorusers/skachkov-sc/monotonic-descriptor
-rw-r--r-- | llvm/include/llvm/Analysis/IVDescriptors.h | 39 | ||||
-rw-r--r-- | llvm/lib/Analysis/IVDescriptors.cpp | 119 |
2 files changed, 158 insertions, 0 deletions
diff --git a/llvm/include/llvm/Analysis/IVDescriptors.h b/llvm/include/llvm/Analysis/IVDescriptors.h index 140edff..eedb7ee 100644 --- a/llvm/include/llvm/Analysis/IVDescriptors.h +++ b/llvm/include/llvm/Analysis/IVDescriptors.h @@ -27,6 +27,7 @@ class Loop; class PredicatedScalarEvolution; class ScalarEvolution; class SCEV; +class SCEVAddRecExpr; class StoreInst; /// These are the kinds of recurrences that we support. @@ -426,6 +427,44 @@ private: SmallVector<Instruction *, 2> RedundantCasts; }; +/// A struct for saving information about monotonic variables. +/// Monotonic variable can be considered as a "conditional" induction variable: +/// its update happens only on loop iterations for which a certain predicate is +/// satisfied. In this implementation the predicate is represented as an edge in +/// loop CFG: variable is updated if this edge is executed on current loop +/// iteration. +class MonotonicDescriptor { +public: + using Edge = std::pair<BasicBlock *, BasicBlock *>; + + MonotonicDescriptor() = default; + + const SmallPtrSetImpl<PHINode *> &getChain() const { return Chain; } + Instruction *getStepInst() const { return StepInst; } + Edge getPredicateEdge() const { return PredEdge; } + const SCEVAddRecExpr *getExpr() const { return Expr; } + + /// Returns true if \p PN is a monotonic variable in the loop \p L. If \p PN + /// is monotonic, the monotonic descriptor \p D will contain the data + /// describing this variable. + static bool isMonotonicPHI(PHINode *PN, const Loop *L, + MonotonicDescriptor &Desc, ScalarEvolution &SE); + + /// Returns true if \p Val is a monotonic variable in the loop \p L (in this + /// case, the value should transitively contain monotonic phi as part of its + /// calculation). + static bool isMonotonicVal(Value *Val, const Loop *L, + MonotonicDescriptor &Desc, ScalarEvolution &SE); + +private: + SmallPtrSet<PHINode *, 1> Chain; + Instruction *StepInst; + Edge PredEdge; + const SCEVAddRecExpr *Expr; + + bool setSCEV(const SCEV *NewExpr); +}; + } // end namespace llvm #endif // LLVM_ANALYSIS_IVDESCRIPTORS_H diff --git a/llvm/lib/Analysis/IVDescriptors.cpp b/llvm/lib/Analysis/IVDescriptors.cpp index a273338..1818fe4 100644 --- a/llvm/lib/Analysis/IVDescriptors.cpp +++ b/llvm/lib/Analysis/IVDescriptors.cpp @@ -1590,3 +1590,122 @@ bool InductionDescriptor::isInductionPHI( D = InductionDescriptor(StartValue, IK_PtrInduction, Step); return true; } + +bool MonotonicDescriptor::setSCEV(const SCEV *NewExpr) { + auto *AddRec = dyn_cast<SCEVAddRecExpr>(NewExpr); + if (!AddRec || !AddRec->isAffine()) + return false; + Expr = AddRec; + return true; +} + +// Recognize monotonic phi variable by matching the following pattern: +// loop_header: +// %monotonic_phi = [%start, %preheader], [%chain_phi0, %latch] +// +// step_bb: +// %step = add/gep %monotonic_phi, %step_val +// +// bbN: +// %chain_phiN = [%monotonic_phi, ], [%step, ] +// +// ... +// +// bb1: +// %chain_phi1 = [%monotonic_phi, ], [%chain_phi2, ] +// +// latch: +// %chain_phi0 = [%monotonic_phi, %pred], [%chain_phi1, %pred] +// +// For this pattern, monotonic phi is described by {%start, +, %step} recurrence +// and predicate is CFG edge %step_bb -> %bbN. +bool MonotonicDescriptor::isMonotonicPHI(PHINode *PN, const Loop *L, + MonotonicDescriptor &Desc, + ScalarEvolution &SE) { + if (!PN->getType()->isIntOrPtrTy() || PN->getParent() != L->getHeader()) + return false; + auto *BackEdgeInst = + dyn_cast<PHINode>(PN->getIncomingValueForBlock(L->getLoopLatch())); + if (!BackEdgeInst) + return false; + SmallVector<PHINode *, 4> Worklist{BackEdgeInst}; + std::optional<std::pair<Edge, Value *>> Inc; + while (!Worklist.empty()) { + auto *Phi = Worklist.pop_back_val(); + Desc.Chain.insert(Phi); + for (unsigned I = 0, E = Phi->getNumOperands(); I != E; ++I) { + auto *IncomingVal = Phi->getIncomingValue(I); + if (IncomingVal == PN) + continue; + if (!IncomingVal->hasOneUse()) + return false; + if (auto *IncomingPhi = dyn_cast<PHINode>(IncomingVal)) { + Worklist.push_back(IncomingPhi); + continue; + } + if (Inc) + return false; + Inc = std::make_pair(Edge{Phi->getIncomingBlock(I), Phi->getParent()}, + IncomingVal); + } + } + if (!Inc) + return false; + auto [PredEdge, StepOp] = *Inc; + auto *StepInst = dyn_cast<Instruction>(StepOp); + if (!StepInst) + return false; + Desc.StepInst = StepInst; + Desc.PredEdge = PredEdge; + + // 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 false; + + 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); + } + + return Desc.setSCEV( + SE.getAddRecExpr(SE.getSCEV(Start), SE.getSCEV(Step), L, WrapFlags)); +} + +bool MonotonicDescriptor::isMonotonicVal(Value *Val, const Loop *L, + MonotonicDescriptor &Desc, + ScalarEvolution &SE) { + if (!Val->getType()->isIntOrPtrTy() || L->isLoopInvariant(Val)) + return false; + auto *CurInst = cast<Instruction>(Val); + + auto NonInvariantVal = [&](Value *V, bool AllowRepeats) { + return L->isLoopInvariant(V) ? nullptr : cast<Instruction>(V); + }; + + while (!isa<PHINode>(CurInst)) { + CurInst = find_singleton<Instruction>(CurInst->operands(), NonInvariantVal); + if (!CurInst) + return false; + }; + + if (!isMonotonicPHI(cast<PHINode>(CurInst), L, Desc, SE)) + return false; + + ValueToSCEVMapTy Map{{CurInst, Desc.getExpr()}}; + return Desc.setSCEV(SCEVParameterRewriter::rewrite(SE.getSCEV(Val), SE, Map)); +} |