[llvm] [IVDescriptors] Identify min/max recurrences in single pass. (PR #163460)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 5 07:58:43 PST 2025
================
@@ -214,6 +214,173 @@ static bool checkOrderedReduction(RecurKind Kind, Instruction *ExactFPMathInst,
return true;
}
+// Helper to collect FMF from a value and its associated fcmp in select patterns
+static FastMathFlags collectMinMaxFMF(Value *V) {
+ FastMathFlags FMF = cast<FPMathOperator>(V)->getFastMathFlags();
+ if (auto *Sel = dyn_cast<SelectInst>(V)) {
+ // Accept FMF on either fcmp or select of a min/max idiom.
+ // TODO: This is a hack to work-around the fact that FMF may not be
+ // assigned/propagated correctly. If that problem is fixed or we
+ // standardize on fmin/fmax via intrinsics, this can be removed.
+ if (auto *FCmp = dyn_cast<FCmpInst>(Sel->getCondition()))
+ FMF |= FCmp->getFastMathFlags();
+ }
+ return FMF;
+}
+
+static std::optional<FastMathFlags>
+hasRequiredFastMathFlags(FPMathOperator *FPOp, RecurKind &RK,
+ FastMathFlags FuncFMF) {
+ bool HasRequiredFMF =
+ (FuncFMF.noNaNs() && FuncFMF.noSignedZeros()) ||
+ (FPOp && FPOp->hasNoNaNs() && FPOp->hasNoSignedZeros()) ||
+ RK == RecurKind::FMinimum || RK == RecurKind::FMaximum ||
+ RK == RecurKind::FMinimumNum || RK == RecurKind::FMaximumNum;
+ if (!HasRequiredFMF) {
+ if (RK == RecurKind::FMax &&
+ match(FPOp, m_Intrinsic<Intrinsic::maxnum>(m_Value(), m_Value())))
+ RK = RecurKind::FMaxNum;
+ else if (RK == RecurKind::FMin &&
+ match(FPOp, m_Intrinsic<Intrinsic::minnum>(m_Value(), m_Value())))
+ RK = RecurKind::FMinNum;
+ else
+ return std::nullopt;
+ }
+ return {collectMinMaxFMF(FPOp)};
+}
+
+static std::optional<RecurrenceDescriptor>
+getMultiUseMinMax(PHINode *Phi, Loop *TheLoop, FastMathFlags FuncFMF,
+ ScalarEvolution *SE) {
+ if (Phi->getNumIncomingValues() != 2 ||
+ Phi->getParent() != TheLoop->getHeader())
+ return std::nullopt;
+
+ Type *Ty = Phi->getType();
+ BasicBlock *Latch = TheLoop->getLoopLatch();
+ if ((!Ty->isIntegerTy() && !Ty->isFloatingPointTy()) || !Latch)
+ return std::nullopt;
+
+ auto Matches = [](Value *V, Value *&A, Value *&B) -> RecurKind {
+ if (match(V, m_UMin(m_Value(A), m_Value(B))))
+ return RecurKind::UMin;
+ if (match(V, m_UMax(m_Value(A), m_Value(B))))
+ return RecurKind::UMax;
+ if (match(V, m_SMax(m_Value(A), m_Value(B))))
+ return RecurKind::SMax;
+ if (match(V, m_SMin(m_Value(A), m_Value(B))))
+ return RecurKind::SMin;
+ if (match(V, m_OrdOrUnordFMin(m_Value(A), m_Value(B))) ||
+ match(V, m_Intrinsic<Intrinsic::minnum>(m_Value(A), m_Value(B))))
+ return RecurKind::FMin;
+ if (match(V, m_OrdOrUnordFMax(m_Value(A), m_Value(B))) ||
+ match(V, m_Intrinsic<Intrinsic::maxnum>(m_Value(A), m_Value(B))))
+ return RecurKind::FMax;
+ if (match(V, m_FMinimum(m_Value(A), m_Value(B))))
+ return RecurKind::FMinimum;
+ if (match(V, m_FMaximum(m_Value(A), m_Value(B))))
+ return RecurKind::FMaximum;
+ if (match(V, m_Intrinsic<Intrinsic::minimumnum>(m_Value(A), m_Value(B))))
+ return RecurKind::FMinimumNum;
+ if (match(V, m_Intrinsic<Intrinsic::maximumnum>(m_Value(A), m_Value(B))))
+ return RecurKind::FMaximumNum;
+ return RecurKind::None;
+ };
+
+ FastMathFlags FMF = FastMathFlags::getFast();
+ Value *RdxNext = Phi->getIncomingValueForBlock(Latch);
+ RecurKind RK = RecurKind::None;
+ // Identify min/max recurrences by walking the def-use chains upwards,
+ // starting at RdxNext.
+ SmallVector<Value *> WorkList = {RdxNext};
+ SmallPtrSet<Value *, 8> Chain = {Phi};
+ while (!WorkList.empty()) {
+ Value *Cur = WorkList.pop_back_val();
+ if (!Chain.insert(Cur).second)
+ continue;
+ auto *I = dyn_cast<Instruction>(Cur);
+ if (!I || !TheLoop->contains(I))
+ return std::nullopt;
+ if (auto *PN = dyn_cast<PHINode>(I)) {
+ if (PN != Phi)
+ append_range(WorkList, PN->operands());
+ continue;
+ }
+ Value *A, *B;
+ RecurKind CurRK = Matches(Cur, A, B);
+ if (CurRK == RecurKind::None || (RK != RecurKind::None && CurRK != RK))
+ return std::nullopt;
+
+ RK = CurRK;
+ // For floating point recurrences, check we have the required fast-math
+ // flags.
+ if (RecurrenceDescriptor::isFPMinMaxRecurrenceKind(CurRK)) {
+ if (auto CurFMF =
+ hasRequiredFastMathFlags(cast<FPMathOperator>(Cur), RK, FuncFMF))
+ FMF &= *CurFMF;
+ else
+ return std::nullopt;
+ }
+
+ Chain.insert(I);
+ if (auto *SI = dyn_cast<SelectInst>(I))
+ Chain.insert(SI->getCondition());
+
+ if (A == Phi || B == Phi)
+ continue;
+
+ // Add operand to worklist if it matches the pattern - exactly one must
+ // match
+ Value *X, *Y;
+ auto *IA = dyn_cast<Instruction>(A);
+ auto *IB = dyn_cast<Instruction>(B);
+ bool AMatches = IA && TheLoop->contains(IA) && Matches(A, X, Y) == RK;
+ bool BMatches = IB && TheLoop->contains(IB) && Matches(B, X, Y) == RK;
+ if (AMatches == BMatches) // Both or neither match
+ return std::nullopt;
+ WorkList.push_back(AMatches ? A : B);
+ }
+
+ // Check users of RdxNext. It can have
+ // * a single user outside the loop,
+ // * used stores to the same invariant address,
+ // * used by the starting recurrence phis.
+ unsigned IncOut = 0;
+ StoreInst *IntermediateStore = nullptr;
+ for (Use &U : RdxNext->uses()) {
+ auto *User = cast<Instruction>(U.getUser());
+ if (!TheLoop->contains(User->getParent())) {
+ if (++IncOut > 1)
----------------
fhahn wrote:
updated thanks
https://github.com/llvm/llvm-project/pull/163460
More information about the llvm-commits
mailing list