Skip to content

Commit 9c543ba

Browse files
committed
[IVDescriptors] Implement MonotonicDescriptor
1 parent 1532ee6 commit 9c543ba

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

llvm/include/llvm/Analysis/IVDescriptors.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Loop;
2727
class PredicatedScalarEvolution;
2828
class ScalarEvolution;
2929
class SCEV;
30+
class SCEVAddRecExpr;
3031
class StoreInst;
3132

3233
/// These are the kinds of recurrences that we support.
@@ -426,6 +427,44 @@ class InductionDescriptor {
426427
SmallVector<Instruction *, 2> RedundantCasts;
427428
};
428429

430+
/// A struct for saving information about monotonic variables.
431+
/// Monotonic variable can be considered as a "conditional" induction variable:
432+
/// its update happens only on loop iterations for which a certain predicate is
433+
/// satisfied. In this implementation the predicate is represented as an edge in
434+
/// loop CFG: variable is updated if this edge is executed on current loop
435+
/// iteration.
436+
class MonotonicDescriptor {
437+
public:
438+
using Edge = std::pair<BasicBlock *, BasicBlock *>;
439+
440+
MonotonicDescriptor() = default;
441+
442+
const SmallPtrSetImpl<PHINode *> &getChain() const { return Chain; }
443+
Instruction *getStepInst() const { return StepInst; }
444+
Edge getPredicateEdge() const { return PredEdge; }
445+
const SCEVAddRecExpr *getExpr() const { return Expr; }
446+
447+
/// Returns true if \p PN is a monotonic variable in the loop \p L. If \p PN
448+
/// is monotonic, the monotonic descriptor \p D will contain the data
449+
/// describing this variable.
450+
static bool isMonotonicPHI(PHINode *PN, const Loop *L,
451+
MonotonicDescriptor &Desc, ScalarEvolution &SE);
452+
453+
/// Returns true if \p Val is a monotonic variable in the loop \p L (in this
454+
/// case, the value should transitively contain monotonic phi as part of its
455+
/// calculation).
456+
static bool isMonotonicVal(Value *Val, const Loop *L,
457+
MonotonicDescriptor &Desc, ScalarEvolution &SE);
458+
459+
private:
460+
SmallPtrSet<PHINode *, 1> Chain;
461+
Instruction *StepInst;
462+
Edge PredEdge;
463+
const SCEVAddRecExpr *Expr;
464+
465+
bool setSCEV(const SCEV *NewExpr);
466+
};
467+
429468
} // end namespace llvm
430469

431470
#endif // LLVM_ANALYSIS_IVDESCRIPTORS_H

llvm/lib/Analysis/IVDescriptors.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,3 +1590,122 @@ bool InductionDescriptor::isInductionPHI(
15901590
D = InductionDescriptor(StartValue, IK_PtrInduction, Step);
15911591
return true;
15921592
}
1593+
1594+
bool MonotonicDescriptor::setSCEV(const SCEV *NewExpr) {
1595+
auto *AddRec = dyn_cast<SCEVAddRecExpr>(NewExpr);
1596+
if (!AddRec || !AddRec->isAffine())
1597+
return false;
1598+
Expr = AddRec;
1599+
return true;
1600+
}
1601+
1602+
// Recognize monotonic phi variable by matching the following pattern:
1603+
// loop_header:
1604+
// %monotonic_phi = [%start, %preheader], [%chain_phi0, %latch]
1605+
//
1606+
// step_bb:
1607+
// %step = add/gep %monotonic_phi, %step_val
1608+
//
1609+
// bbN:
1610+
// %chain_phiN = [%monotonic_phi, ], [%step, ]
1611+
//
1612+
// ...
1613+
//
1614+
// bb1:
1615+
// %chain_phi1 = [%monotonic_phi, ], [%chain_phi2, ]
1616+
//
1617+
// latch:
1618+
// %chain_phi0 = [%monotonic_phi, %pred], [%chain_phi1, %pred]
1619+
//
1620+
// For this pattern, monotonic phi is described by {%start, +, %step} recurrence
1621+
// and predicate is CFG edge %step_bb -> %bbN.
1622+
bool MonotonicDescriptor::isMonotonicPHI(PHINode *PN, const Loop *L,
1623+
MonotonicDescriptor &Desc,
1624+
ScalarEvolution &SE) {
1625+
if (!PN->getType()->isIntOrPtrTy() || PN->getParent() != L->getHeader())
1626+
return false;
1627+
auto *BackEdgeInst =
1628+
dyn_cast<PHINode>(PN->getIncomingValueForBlock(L->getLoopLatch()));
1629+
if (!BackEdgeInst)
1630+
return false;
1631+
SmallVector<PHINode *, 4> Worklist{BackEdgeInst};
1632+
std::optional<std::pair<Edge, Value *>> Inc;
1633+
while (!Worklist.empty()) {
1634+
auto *Phi = Worklist.pop_back_val();
1635+
Desc.Chain.insert(Phi);
1636+
for (unsigned I = 0, E = Phi->getNumOperands(); I != E; ++I) {
1637+
auto *IncomingVal = Phi->getIncomingValue(I);
1638+
if (IncomingVal == PN)
1639+
continue;
1640+
if (!IncomingVal->hasOneUse())
1641+
return false;
1642+
if (auto *IncomingPhi = dyn_cast<PHINode>(IncomingVal)) {
1643+
Worklist.push_back(IncomingPhi);
1644+
continue;
1645+
}
1646+
if (Inc)
1647+
return false;
1648+
Inc = std::make_pair(Edge{Phi->getIncomingBlock(I), Phi->getParent()},
1649+
IncomingVal);
1650+
}
1651+
}
1652+
if (!Inc)
1653+
return false;
1654+
auto [PredEdge, StepOp] = *Inc;
1655+
auto *StepInst = dyn_cast<Instruction>(StepOp);
1656+
if (!StepInst)
1657+
return false;
1658+
Desc.StepInst = StepInst;
1659+
Desc.PredEdge = PredEdge;
1660+
1661+
// Construct SCEVAddRec for this value.
1662+
Value *Start = PN->getIncomingValueForBlock(L->getLoopPreheader());
1663+
1664+
Value *Step = nullptr;
1665+
bool StepMatch =
1666+
PN->getType()->isPointerTy()
1667+
? match(StepInst, m_PtrAdd(m_Specific(PN), m_Value(Step)))
1668+
: match(StepInst, m_Add(m_Specific(PN), m_Value(Step)));
1669+
if (!StepMatch || !L->isLoopInvariant(Step))
1670+
return false;
1671+
1672+
SCEV::NoWrapFlags WrapFlags = SCEV::FlagAnyWrap;
1673+
if (auto *GEP = dyn_cast<GEPOperator>(StepInst)) {
1674+
if (GEP->hasNoUnsignedWrap())
1675+
WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNUW);
1676+
if (GEP->hasNoUnsignedSignedWrap())
1677+
WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNSW);
1678+
} else if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(StepInst)) {
1679+
if (OBO->hasNoUnsignedWrap())
1680+
WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNUW);
1681+
if (OBO->hasNoSignedWrap())
1682+
WrapFlags = ScalarEvolution::setFlags(WrapFlags, SCEV::FlagNSW);
1683+
}
1684+
1685+
return Desc.setSCEV(
1686+
SE.getAddRecExpr(SE.getSCEV(Start), SE.getSCEV(Step), L, WrapFlags));
1687+
}
1688+
1689+
bool MonotonicDescriptor::isMonotonicVal(Value *Val, const Loop *L,
1690+
MonotonicDescriptor &Desc,
1691+
ScalarEvolution &SE) {
1692+
if (!Val->getType()->isIntOrPtrTy() || L->isLoopInvariant(Val))
1693+
return false;
1694+
auto *CurInst = cast<Instruction>(Val);
1695+
1696+
auto NonInvariantVal = [&](Value *V, bool AllowRepeats) {
1697+
return L->isLoopInvariant(V) ? nullptr : cast<Instruction>(V);
1698+
};
1699+
1700+
while (!isa<PHINode>(CurInst)) {
1701+
CurInst = find_singleton<Instruction>(CurInst->operands(), NonInvariantVal);
1702+
if (!CurInst)
1703+
return false;
1704+
};
1705+
1706+
if (!isMonotonicPHI(cast<PHINode>(CurInst), L, Desc, SE))
1707+
return false;
1708+
1709+
ValueToSCEVMapTy Map{{CurInst, Desc.getExpr()}};
1710+
return Desc.setSCEV(SCEVParameterRewriter::rewrite(SE.getSCEV(Val), SE, Map));
1711+
}

0 commit comments

Comments
 (0)