[llvm] [AMDGPU] Avoid fract fold when result is observable at +/-inf (PR #219515)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 05:19:56 PDT 2026
================
@@ -2112,7 +2121,71 @@ Value *AMDGPUCodeGenPrepareImpl::matchFractPatNanAvoidant(Value &V) {
m_FMinimum(m_Value(Arg0), m_APFloatAllowPoison(C)))))
return nullptr;
- return matchFractPatImpl(*Arg0, *C);
+ Value *FractArg = matchFractPatImpl(*Arg0, *C);
+ if (!FractArg)
+ return nullptr;
+
+ if (RootToReplace &&
+ !isFractNanAvoidantFoldLegalAtInf(cast<IntrinsicInst>(&V), Arg0, FractArg,
+ RootToReplace))
+ return nullptr;
+
+ return FractArg;
+}
+
+/// True if \p U is a select that never picks \p V when \p X is infinity, e.g.
+/// fract's library inf-clamp: select (fcmp oeq (fabs X), +inf), 0.0, V
+static bool isInfDiscardingSelect(User *U, const Value *X, const Value *V) {
+ auto *Sel = dyn_cast<SelectInst>(U);
+ if (!Sel)
+ return false;
+
+ bool IsTrueArm = Sel->getTrueValue() == V;
+ if (!IsTrueArm && Sel->getFalseValue() != V)
+ return false;
+ if (IsTrueArm && Sel->getFalseValue() == V)
+ return false;
+
+ auto *Cmp = dyn_cast<FCmpInst>(Sel->getCondition());
+ if (!Cmp)
+ return false;
+
+ auto [ClassVal, ClassesIfTrue, ClassesIfFalse] =
+ fcmpImpliesClass(Cmp->getPredicate(), *Sel->getFunction(),
+ Cmp->getOperand(0), Cmp->getOperand(1));
+ if (ClassVal != X)
+ return false;
+
+ return !((IsTrueArm ? ClassesIfTrue : ClassesIfFalse) & fcInf);
+}
+
+/// At +/-inf \p Sub is nan, so \p MinI clamps while fract would return nan;
+/// true if replacing \p Root with fract of \p X is unobservable there. Callers
+/// must not pass a \p Root that can select around \p MinI at +/-inf, since the
+/// poison cases assume \p Root is poison whenever \p MinI is.
+bool AMDGPUCodeGenPrepareImpl::isFractNanAvoidantFoldLegalAtInf(
+ const IntrinsicInst *MinI, const Value *Sub, const Value *X,
+ Instruction *Root) const {
+ // minimum propagates the nan from x - floor(x), exactly like fract.
+ if (MinI->getIntrinsicID() == Intrinsic::minimum)
+ return true;
+
+ // nnan poisons the min at +/-inf; ninf proves nothing here.
+ if (MinI->hasNoNaNs())
+ return true;
+
+ // At +/-inf the fsub is nan with infinite operands, so nnan/ninf poison it.
+ const auto *FPSub = cast<FPMathOperator>(Sub);
+ if (FPSub->hasNoNaNs() || FPSub->hasNoInfs())
+ return true;
+
+ // The result is only observed where an explicit infinity check discards it:
+ // isinf(x) ? 0.0 : (isnan(x) ? x : minnum(x - floor(x), C))
+ if (all_of(Root->users(),
----------------
arsenm wrote:
Shouldn't need to do a user scan, the patterns are matched from the root to start?
https://github.com/llvm/llvm-project/pull/219515
More information about the llvm-commits
mailing list