Skip to content

Commit 5c41a4e

Browse files
committed
[InstSimplify] Optimize maximumnum and minimumnum
Add support for the new maximumnum and minimumnum intrinsics in various optimizations in InstSimplify. Also, change the behavior of optimizing maxnum(sNaN, x) to simplify to qNaN instead of x to better match the LLVM IR spec, and add more tests for sNaN behavior for all 3 max/min intrinsic types.
1 parent 292cfa7 commit 5c41a4e

File tree

4 files changed

+927
-146
lines changed

4 files changed

+927
-146
lines changed

llvm/include/llvm/IR/PatternMatch.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,10 +707,25 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
707707
struct is_nan {
708708
bool isValue(const APFloat &C) const { return C.isNaN(); }
709709
};
710+
711+
struct is_snan {
712+
bool isValue(const APFloat &C) const { return C.isSignaling(); }
713+
};
714+
715+
struct is_qnan {
716+
bool isValue(const APFloat &C) const { return C.isNaN() && !C.isSignaling(); }
717+
};
718+
710719
/// Match an arbitrary NaN constant. This includes quiet and signalling nans.
711720
/// For vectors, this includes constants with undefined elements.
712721
inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
713722

723+
/// Match quiet NaN constants, including vectors with undefined elements.
724+
inline cstfp_pred_ty<is_qnan> m_qNaN() { return cstfp_pred_ty<is_qnan>(); }
725+
726+
/// Match signalling NaN constants, including vectors with undefined elements.
727+
inline cstfp_pred_ty<is_snan> m_sNaN() { return cstfp_pred_ty<is_snan>(); }
728+
714729
struct is_nonnan {
715730
bool isValue(const APFloat &C) const { return !C.isNaN(); }
716731
};

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6415,7 +6415,8 @@ static Value *foldMinMaxSharedOp(Intrinsic::ID IID, Value *Op0, Value *Op1) {
64156415
static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
64166416
Value *Op1) {
64176417
assert((IID == Intrinsic::maxnum || IID == Intrinsic::minnum ||
6418-
IID == Intrinsic::maximum || IID == Intrinsic::minimum) &&
6418+
IID == Intrinsic::maximum || IID == Intrinsic::minimum ||
6419+
IID == Intrinsic::maximumnum || IID == Intrinsic::minimumnum) &&
64196420
"Unsupported intrinsic");
64206421

64216422
auto *M0 = dyn_cast<IntrinsicInst>(Op0);
@@ -6711,7 +6712,16 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
67116712
case Intrinsic::maxnum:
67126713
case Intrinsic::minnum:
67136714
case Intrinsic::maximum:
6714-
case Intrinsic::minimum: {
6715+
case Intrinsic::minimum:
6716+
case Intrinsic::maximumnum:
6717+
case Intrinsic::minimumnum: {
6718+
// In several cases here, we deviate from exact IEEE 754 semantics
6719+
// to enable optimizations (as allowed by the LLVM IR spec).
6720+
//
6721+
// For instance, we often return one of the arguments unmodified instead of
6722+
// inserting an llvm.canonicalize to transform input sNaNs into qNaNs or to
6723+
// respect any FTZ semantics, and sometimes assume all NaN inputs are qNaNs.
6724+
67156725
// If the arguments are the same, this is a no-op.
67166726
if (Op0 == Op1)
67176727
return Op0;
@@ -6725,32 +6735,43 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
67256735
return Op0;
67266736

67276737
bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
6728-
bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
6729-
6730-
// minnum(X, nan) -> X
6731-
// maxnum(X, nan) -> X
6732-
// minimum(X, nan) -> nan
6733-
// maximum(X, nan) -> nan
6734-
if (match(Op1, m_NaN()))
6735-
return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
6738+
bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
6739+
bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
6740+
IID == Intrinsic::minimumnum;
6741+
6742+
// minnum(x, qnan) -> x
6743+
// maxnum(x, qnan) -> x
6744+
// minnum(x, snan) -> qnan
6745+
// maxnum(x, snan) -> qnan
6746+
// minimum(X, nan) -> qnan
6747+
// maximum(X, nan) -> qnan
6748+
if (match(Op1, m_NaN())) {
6749+
if (PropagateNaN || (PropagateSNaN && match(Op1, m_sNaN())))
6750+
return propagateNaN(cast<Constant>(Op1));
6751+
return Op0;
6752+
}
67366753

67376754
// In the following folds, inf can be replaced with the largest finite
67386755
// float, if the ninf flag is set.
67396756
const APFloat *C;
67406757
if (match(Op1, m_APFloat(C)) &&
67416758
(C->isInfinity() || (Call && Call->hasNoInfs() && C->isLargest()))) {
6742-
// minnum(X, -inf) -> -inf
6743-
// maxnum(X, +inf) -> +inf
6759+
// minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
6760+
// maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
67446761
// minimum(X, -inf) -> -inf if nnan
67456762
// maximum(X, +inf) -> +inf if nnan
6763+
// minimumnum(X, -inf) -> -inf
6764+
// maximumnum(X, +inf) -> +inf
67466765
if (C->isNegative() == IsMin &&
67476766
(!PropagateNaN || (Call && Call->hasNoNaNs())))
67486767
return ConstantFP::get(ReturnType, *C);
67496768

67506769
// minnum(X, +inf) -> X if nnan
67516770
// maxnum(X, -inf) -> X if nnan
6752-
// minimum(X, +inf) -> X
6753-
// maximum(X, -inf) -> X
6771+
// minimum(X, +inf) -> X (ignoring quieting of sNaNs)
6772+
// maximum(X, -inf) -> X (ignoring quieting of sNaNs)
6773+
// maximumnum(X, -inf) -> X if nnan
6774+
// minimumnum(X, +inf) -> X if nnan
67546775
if (C->isNegative() != IsMin &&
67556776
(PropagateNaN || (Call && Call->hasNoNaNs())))
67566777
return Op0;

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9310,6 +9310,10 @@ Intrinsic::ID llvm::getInverseMinMaxIntrinsic(Intrinsic::ID MinMaxID) {
93109310
case Intrinsic::minimum: return Intrinsic::maximum;
93119311
case Intrinsic::maxnum: return Intrinsic::minnum;
93129312
case Intrinsic::minnum: return Intrinsic::maxnum;
9313+
case Intrinsic::maximumnum:
9314+
return Intrinsic::minimumnum;
9315+
case Intrinsic::minimumnum:
9316+
return Intrinsic::maximumnum;
93139317
default: llvm_unreachable("Unexpected intrinsic");
93149318
}
93159319
}

0 commit comments

Comments
 (0)