[llvm] [InstSimplify] Optimize maximumnum and minimumnum (PR #139581)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 19 08:58:04 PDT 2025
================
@@ -6715,36 +6725,138 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
if (Q.isUndefValue(Op1))
return Op0;
- bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
- bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
-
- // minnum(X, nan) -> X
- // maxnum(X, nan) -> X
- // minimum(X, nan) -> nan
- // maximum(X, nan) -> nan
- if (match(Op1, m_NaN()))
- return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
-
- // In the following folds, inf can be replaced with the largest finite
- // float, if the ninf flag is set.
- const APFloat *C;
- if (match(Op1, m_APFloat(C)) &&
- (C->isInfinity() || (Call && Call->hasNoInfs() && C->isLargest()))) {
- // minnum(X, -inf) -> -inf
- // maxnum(X, +inf) -> +inf
- // minimum(X, -inf) -> -inf if nnan
- // maximum(X, +inf) -> +inf if nnan
- if (C->isNegative() == IsMin &&
- (!PropagateNaN || (Call && Call->hasNoNaNs())))
- return ConstantFP::get(ReturnType, *C);
-
- // minnum(X, +inf) -> X if nnan
- // maxnum(X, -inf) -> X if nnan
- // minimum(X, +inf) -> X
- // maximum(X, -inf) -> X
- if (C->isNegative() != IsMin &&
- (PropagateNaN || (Call && Call->hasNoNaNs())))
- return Op0;
+ if (Constant *C = dyn_cast<Constant>(Op1)) {
+ bool PropagateNaN =
+ IID == Intrinsic::minimum || IID == Intrinsic::maximum;
+ bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
+ bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
+ IID == Intrinsic::minimumnum;
+
+ // Get the optimized value for a constant scalar input. The result may
+ // indicate either to use the non-const LHS value, or return a pointer
+ // to a new constant value to use instead of the input (after e.g.
+ // quieting NaNs). Returns empty optional value if it cannot be optimized.
+ typedef struct {
+ bool UseNonConstVal;
+ Constant *NewConstVal;
+ } OptResult;
+ auto GetOptResultFor = [PropagateNaN, PropagateSNaN, IsMin,
+ Call](Constant *C) -> std::optional<OptResult> {
+ auto UseNonConstVal = []() -> OptResult { return {true, nullptr}; };
+ auto UseConstVal = [](Constant *C) -> OptResult { return {false, C}; };
+
+ // min/max(opt, poison) -> poison
+ if (isa<UndefValue>(C))
+ return UseConstVal(C);
+
+ const ConstantFP *CFP = dyn_cast<ConstantFP>(C);
+ if (!CFP)
+ return {};
+ APFloat CAPF = CFP->getValueAPF();
+
+ // minnum(x, qnan) -> x
+ // maxnum(x, qnan) -> x
+ // minnum(x, snan) -> qnan
+ // maxnum(x, snan) -> qnan
+ // minimum(X, nan) -> qnan
+ // maximum(X, nan) -> qnan
+ // minimumnum(X, nan) -> x
+ // maximumnum(X, nan) -> x
+ if (CAPF.isNaN()) {
+ if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling()))
+ return UseConstVal(ConstantFP::get(C->getType(), CAPF.makeQuiet()));
+ else
+ return UseNonConstVal();
+ }
+
+ if (CAPF.isInfinity() ||
+ (Call && Call->hasNoInfs() && CAPF.isLargest())) {
+ // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
+ // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
+ // minimum(X, -inf) -> -inf if nnan
+ // maximum(X, +inf) -> +inf if nnan
+ // minimumnum(X, -inf) -> -inf
+ // maximumnum(X, +inf) -> +inf
+ if (CAPF.isNegative() == IsMin &&
+ (!PropagateNaN || (Call && Call->hasNoNaNs())))
+ return UseConstVal(C);
+
+ // minnum(X, +inf) -> X if nnan
+ // maxnum(X, -inf) -> X if nnan
+ // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
+ // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
+ // minimumnum(X, +inf) -> X if nnan
+ // maximumnum(X, -inf) -> X if nnan
+ if (CAPF.isNegative() != IsMin &&
+ (PropagateNaN || (Call && Call->hasNoNaNs())))
+ return UseNonConstVal();
+ }
+
+ // Cannot optimize this element
+ return {};
+ };
+
+ if (VectorType *VTy = dyn_cast<VectorType>(C->getType())) {
+ // Handle splat vectors (including scalable vectors)
+ if (Constant *SplatVal = C->getSplatValue()) {
+ std::optional<OptResult> OptSplatVal = GetOptResultFor(SplatVal);
+ if (OptSplatVal.has_value()) {
+ if (OptSplatVal.value().UseNonConstVal)
+ return Op0;
+ assert(OptSplatVal.value().NewConstVal != nullptr);
+ return ConstantVector::getSplat(VTy->getElementCount(),
+ OptSplatVal.value().NewConstVal);
+ }
+ } else if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+ // Check elementwise whether we can optimize to either a constant
+ // value or return the LHS value. We cannot mix and match LHS +
+ // constant elements, as this would require inserting a new
+ // VectorShuffle instruction, which is not allowed in simplifyBinOp,
+ // so bail early if any element cannot be optimized, or if lhs vs
+ // const optimizations start to mismatch. However, we can turn
+ // undef/poison into the LHS value, so only bail if we need at least 1
+ // non undef/poison RHS const.
+ bool CanOptimize = true;
+ bool AllConstValsAreUndef = true;
+ unsigned NumElts = FVty->getNumElements();
+ // Storage to build up the constant return value (possible altered
+ // from the input RHS value by quieting NaNs)
+ SmallVector<Constant *, 16> NewC(NumElts);
+
+ bool NeedsConstElement = false;
+ bool NeedsLHSElement = false;
+ for (unsigned i = 0; i != NumElts; ++i) {
----------------
arsenm wrote:
We have a few too many places doing the same thing over fixed constant vectors
https://github.com/llvm/llvm-project/pull/139581
More information about the llvm-commits
mailing list