Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -11560,6 +11560,82 @@ correctly return the non-NaN input (e.g. by using the equivalent of ``llvm.canonicalize``). +'``llvm.minnan.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.minnan`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare float @llvm.minnan.f32(float %Val0, float %Val1) + declare double @llvm.minnan.f64(double %Val0, double %Val1) + declare x86_fp80 @llvm.minnan.f80(x86_fp80 %Val0, x86_fp80 %Val1) + declare fp128 @llvm.minnan.f128(fp128 %Val0, fp128 %Val1) + declare ppc_fp128 @llvm.minnan.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1) + +Overview: +""""""""" + +The '``llvm.minnan.*``' intrinsics return the minimum of the two +arguments, propagating NaNs. + + +Arguments: +"""""""""" + +The arguments and return value are floating-point numbers of the same +type. + +Semantics: +"""""""""" +If either operand is a NaN, returns NaN. If the operands compare +equal, returns a value that compares equal to both operands. This +means that llvm.minnan(+/-0.0, +/-0.0) could return either -0.0 or +0.0. + +'``llvm.maxnan.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.maxnan`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare float @llvm.maxnan.f32(float %Val0, float %Val1l) + declare double @llvm.maxnan.f64(double %Val0, double %Val1) + declare x86_fp80 @llvm.maxnan.f80(x86_fp80 %Val0, x86_fp80 %Val1) + declare fp128 @llvm.maxnan.f128(fp128 %Val0, fp128 %Val1) + declare ppc_fp128 @llvm.maxnan.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1) + +Overview: +""""""""" + +The '``llvm.maxnan.*``' intrinsics return the maximum of the two +arguments, propagating NaNs. + + +Arguments: +"""""""""" + +The arguments and return value are floating-point numbers of the same +type. + +Semantics: +"""""""""" +If either operand is a NaN, returns NaN. If the operands compare +equal, returns a value that compares equal to both operands. This +means that llvm.maxnan(+/-0.0, +/-0.0) could return either -0.0 or +0.0. + '``llvm.copysign.*``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: include/llvm/ADT/APFloat.h =================================================================== --- include/llvm/ADT/APFloat.h +++ include/llvm/ADT/APFloat.h @@ -1243,6 +1243,28 @@ return (A.compare(B) == APFloat::cmpLessThan) ? B : A; } +/// Implements IEEE 754-2018 `minimum` semantics. Returns the smaller of the 2 +/// arguments if both are not Nan. If either argument is a NaN, return the NaN. +LLVM_READONLY +inline APFloat minnan(const APFloat &A, const APFloat &B) { + if (A.isNaN()) + return A; + if (B.isNaN()) + return B; + return (B.compare(A) == APFloat::cmpLessThan) ? B : A; +} + +/// Implements IEEE 754-2018 `maximum` semantics. Returns the larger of the 2 +/// arguments if both are not Nan. If either argument is a NaN, return the NaN. +LLVM_READONLY +inline APFloat maxnan(const APFloat &A, const APFloat &B) { + if (A.isNaN()) + return A; + if (B.isNaN()) + return B; + return (A.compare(B) == APFloat::cmpLessThan) ? B : A; +} + } // namespace llvm #undef APFLOAT_DISPATCH_ON_SEMANTICS Index: include/llvm/IR/IRBuilder.h =================================================================== --- include/llvm/IR/IRBuilder.h +++ include/llvm/IR/IRBuilder.h @@ -703,6 +703,16 @@ return CreateBinaryIntrinsic(Intrinsic::maxnum, LHS, RHS, Name); } + /// Create call to the minnan intrinsic. + CallInst *CreateMinNan(Value *LHS, Value *RHS, const Twine &Name = "") { + return CreateBinaryIntrinsic(Intrinsic::minnan, LHS, RHS, Name); + } + + /// Create call to the maxnan intrinsic. + CallInst *CreateMaxNan(Value *LHS, Value *RHS, const Twine &Name = "") { + return CreateBinaryIntrinsic(Intrinsic::maxnan, LHS, RHS, Name); + } + private: /// Create a call to a masked intrinsic with given Id. CallInst *CreateMaskedIntrinsic(Intrinsic::ID Id, ArrayRef Ops, Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -453,6 +453,14 @@ [LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem, IntrSpeculatable, Commutative] >; +def int_minnan : Intrinsic<[llvm_anyfloat_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable, Commutative] +>; +def int_maxnan : Intrinsic<[llvm_anyfloat_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable, Commutative] +>; // NOTE: these are internal interfaces. def int_setjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty]>; Index: lib/Analysis/ConstantFolding.cpp =================================================================== --- lib/Analysis/ConstantFolding.cpp +++ lib/Analysis/ConstantFolding.cpp @@ -1363,6 +1363,8 @@ case Intrinsic::fabs: case Intrinsic::minnum: case Intrinsic::maxnum: + case Intrinsic::minnan: + case Intrinsic::maxnan: case Intrinsic::log: case Intrinsic::log2: case Intrinsic::log10: @@ -1912,6 +1914,18 @@ return ConstantFP::get(Ty->getContext(), maxnum(C1, C2)); } + if (IntrinsicID == Intrinsic::minnan) { + const APFloat &C1 = Op1->getValueAPF(); + const APFloat &C2 = Op2->getValueAPF(); + return ConstantFP::get(Ty->getContext(), minnan(C1, C2)); + } + + if (IntrinsicID == Intrinsic::maxnan) { + const APFloat &C1 = Op1->getValueAPF(); + const APFloat &C2 = Op2->getValueAPF(); + return ConstantFP::get(Ty->getContext(), maxnan(C1, C2)); + } + if (!TLI) return nullptr; if ((Name == "pow" && TLI->has(LibFunc_pow)) || Index: lib/Analysis/InstructionSimplify.cpp =================================================================== --- lib/Analysis/InstructionSimplify.cpp +++ lib/Analysis/InstructionSimplify.cpp @@ -4829,13 +4829,24 @@ } break; case Intrinsic::maxnum: - case Intrinsic::minnum: { + case Intrinsic::minnum: + case Intrinsic::maxnan: + case Intrinsic::minnan: { // If the arguments are the same, this is a no-op. if (Op0 == Op1) return Op0; - // If one argument is NaN or undef, return the other argument. - if (match(Op0, m_CombineOr(m_NaN(), m_Undef()))) return Op1; - if (match(Op1, m_CombineOr(m_NaN(), m_Undef()))) return Op0; + // If one argument is undef, return the other argument. + if (match(Op0, m_Undef())) + return Op1; + if (match(Op1, m_Undef())) + return Op0; + + // If one argument is NaN, return other or NaN appropriately. + bool PropagateNaN = (IID == Intrinsic::minnan || IID == Intrinsic::maxnan); + if (match(Op0, m_NaN())) + return PropagateNaN ? Op0 : Op1; + if (match(Op1, m_NaN())) + return PropagateNaN ? Op1 : Op0; // Min/max of the same operation with common operand: // m(m(X, Y)), X --> m(X, Y) (4 commuted variants) @@ -4848,9 +4859,9 @@ (M1->getOperand(0) == Op0 || M1->getOperand(1) == Op0)) return Op1; - // minnum(X, -Inf) --> -Inf (and commuted variant) - // maxnum(X, +Inf) --> +Inf (and commuted variant) - bool UseNegInf = IID == Intrinsic::minnum; + // min(X, -Inf) --> -Inf (and commuted variant) + // max(X, +Inf) --> +Inf (and commuted variant) + bool UseNegInf = (IID == Intrinsic::minnum || IID == Intrinsic::minnan); const APFloat *C; if ((match(Op0, m_APFloat(C)) && C->isInfinity() && C->isNegative() == UseNegInf) || Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -2898,7 +2898,13 @@ cannotBeOrderedLessThanZeroImpl(I->getOperand(1), TLI, SignBitOnly, Depth + 1)); + case Intrinsic::maxnan: + return cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, + Depth + 1) || + cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, + Depth + 1); case Intrinsic::minnum: + case Intrinsic::minnan: return cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, Depth + 1) && cannotBeOrderedLessThanZeroImpl(I->getOperand(1), TLI, SignBitOnly, Index: lib/Analysis/VectorUtils.cpp =================================================================== --- lib/Analysis/VectorUtils.cpp +++ lib/Analysis/VectorUtils.cpp @@ -54,6 +54,8 @@ case Intrinsic::fabs: case Intrinsic::minnum: case Intrinsic::maxnum: + case Intrinsic::minnan: + case Intrinsic::maxnan: case Intrinsic::copysign: case Intrinsic::floor: case Intrinsic::ceil: Index: lib/CodeGen/SelectionDAG/DAGCombiner.cpp =================================================================== --- lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -365,6 +365,8 @@ SDValue visitFFLOOR(SDNode *N); SDValue visitFMINNUM(SDNode *N); SDValue visitFMAXNUM(SDNode *N); + SDValue visitFMINNAN(SDNode *N); + SDValue visitFMAXNAN(SDNode *N); SDValue visitBRCOND(SDNode *N); SDValue visitBR_CC(SDNode *N); SDValue visitLOAD(SDNode *N); @@ -1576,6 +1578,8 @@ case ISD::FFLOOR: return visitFFLOOR(N); case ISD::FMINNUM: return visitFMINNUM(N); case ISD::FMAXNUM: return visitFMAXNUM(N); + case ISD::FMINNAN: return visitFMINNAN(N); + case ISD::FMAXNAN: return visitFMAXNAN(N); case ISD::FCEIL: return visitFCEIL(N); case ISD::FTRUNC: return visitFTRUNC(N); case ISD::BRCOND: return visitBRCOND(N); @@ -12105,7 +12109,8 @@ return SDValue(); } -SDValue DAGCombiner::visitFMINNUM(SDNode *N) { +static SDValue visitFMinMax(SelectionDAG &DAG, SDNode *N, + APFloat (*Op)(const APFloat&, const APFloat&)) { SDValue N0 = N->getOperand(0); SDValue N1 = N->getOperand(1); EVT VT = N->getValueType(0); @@ -12115,36 +12120,31 @@ if (N0CFP && N1CFP) { const APFloat &C0 = N0CFP->getValueAPF(); const APFloat &C1 = N1CFP->getValueAPF(); - return DAG.getConstantFP(minnum(C0, C1), SDLoc(N), VT); + return DAG.getConstantFP(Op(C0, C1), SDLoc(N), VT); } // Canonicalize to constant on RHS. if (isConstantFPBuildVectorOrConstantFP(N0) && !isConstantFPBuildVectorOrConstantFP(N1)) - return DAG.getNode(ISD::FMINNUM, SDLoc(N), VT, N1, N0); + return DAG.getNode(N->getOpcode(), SDLoc(N), VT, N1, N0); return SDValue(); } -SDValue DAGCombiner::visitFMAXNUM(SDNode *N) { - SDValue N0 = N->getOperand(0); - SDValue N1 = N->getOperand(1); - EVT VT = N->getValueType(0); - const ConstantFPSDNode *N0CFP = isConstOrConstSplatFP(N0); - const ConstantFPSDNode *N1CFP = isConstOrConstSplatFP(N1); +SDValue DAGCombiner::visitFMINNUM(SDNode *N) { + return visitFMinMax(DAG, N, minnum); +} - if (N0CFP && N1CFP) { - const APFloat &C0 = N0CFP->getValueAPF(); - const APFloat &C1 = N1CFP->getValueAPF(); - return DAG.getConstantFP(maxnum(C0, C1), SDLoc(N), VT); - } +SDValue DAGCombiner::visitFMAXNUM(SDNode *N) { + return visitFMinMax(DAG, N, maxnum); +} - // Canonicalize to constant on RHS. - if (isConstantFPBuildVectorOrConstantFP(N0) && - !isConstantFPBuildVectorOrConstantFP(N1)) - return DAG.getNode(ISD::FMAXNUM, SDLoc(N), VT, N1, N0); +SDValue DAGCombiner::visitFMINNAN(SDNode *N) { + return visitFMinMax(DAG, N, minnan); +} - return SDValue(); +SDValue DAGCombiner::visitFMAXNAN(SDNode *N) { + return visitFMinMax(DAG, N, maxnan); } SDValue DAGCombiner::visitFABS(SDNode *N) { @@ -17280,7 +17280,7 @@ NewMask.push_back(M < 0 ? -1 : Scale * M + s); return NewMask; }; - + SDValue BC0 = peekThroughOneUseBitcasts(N0); if (BC0.getOpcode() == ISD::VECTOR_SHUFFLE && BC0.hasOneUse()) { EVT SVT = VT.getScalarType(); Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5585,6 +5585,18 @@ getValue(I.getArgOperand(1)))); return nullptr; } + case Intrinsic::minnan: + setValue(&I, DAG.getNode(ISD::FMINNAN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; + case Intrinsic::maxnan: + setValue(&I, DAG.getNode(ISD::FMAXNAN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; case Intrinsic::copysign: setValue(&I, DAG.getNode(ISD::FCOPYSIGN, sdl, getValue(I.getArgOperand(0)).getValueType(), Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2020,7 +2020,9 @@ } case Intrinsic::minnum: - case Intrinsic::maxnum: { + case Intrinsic::maxnum: + case Intrinsic::minnan: + case Intrinsic::maxnan: { Value *Arg0 = II->getArgOperand(0); Value *Arg1 = II->getArgOperand(1); // Canonicalize constants to the RHS. @@ -2034,10 +2036,25 @@ if (match(Arg0, m_FNeg(m_Value(X))) && match(Arg1, m_FNeg(m_Value(Y))) && (Arg0->hasOneUse() || Arg1->hasOneUse())) { // If both operands are negated, invert the call and negate the result: - // minnum(-X, -Y) --> -(maxnum(X, Y)) - // maxnum(-X, -Y) --> -(minnum(X, Y)) - Intrinsic::ID NewIID = II->getIntrinsicID() == Intrinsic::maxnum ? - Intrinsic::minnum : Intrinsic::maxnum; + // min(-X, -Y) --> -(max(X, Y)) + // max(-X, -Y) --> -(min(X, Y)) + Intrinsic::ID NewIID; + switch (II->getIntrinsicID()) { + case Intrinsic::maxnum: + NewIID = Intrinsic::minnum; + break; + case Intrinsic::minnum: + NewIID = Intrinsic::maxnum; + break; + case Intrinsic::maxnan: + NewIID = Intrinsic::minnan; + break; + case Intrinsic::minnan: + NewIID = Intrinsic::maxnan; + break; + default: + llvm_unreachable("unexpected intrinsic ID"); + } Value *NewCall = Builder.CreateIntrinsic(NewIID, { X, Y }, II); Instruction *FNeg = BinaryOperator::CreateFNeg(NewCall); FNeg->copyIRFlags(II); Index: test/Analysis/ConstantFolding/min-max.ll =================================================================== --- /dev/null +++ test/Analysis/ConstantFolding/min-max.ll @@ -0,0 +1,84 @@ +; RUN: opt -instcombine -S -o - %s | FileCheck %s +; Tests that constant folding of min and max operations works as expected. + +declare float @llvm.minnum.f32(float, float) +declare float @llvm.maxnum.f32(float, float) +declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) +declare <4 x float> @llvm.maxnum.v4f32(<4 x float>, <4 x float>) + +declare float @llvm.minnan.f32(float, float) +declare float @llvm.maxnan.f32(float, float) +declare <4 x float> @llvm.minnan.v4f32(<4 x float>, <4 x float>) +declare <4 x float> @llvm.maxnan.v4f32(<4 x float>, <4 x float>) + +; CHECK: define float @minnum_float() { +define float @minnum_float() { + ; CHECK-NEXT: ret float 5.000000e+00 + %1 = call float @llvm.minnum.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @minnum_float_vec() { +define <4 x float> @minnum_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.minnum.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} + +; CHECK: define float @maxnum_float() { +define float @maxnum_float() { + ; CHECK-NEXT: ret float 4.200000e+01 + %1 = call float @llvm.maxnum.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @maxnum_float_vec() { +define <4 x float> @maxnum_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.maxnum.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} + +; CHECK: define float @minnan_float() { +define float @minnan_float() { + ; CHECK-NEXT: ret float 5.000000e+00 + %1 = call float @llvm.minnan.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @minnan_float_vec() { +define <4 x float> @minnan_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.minnan.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} + +; CHECK: define float @maxnan_float() { +define float @maxnan_float() { + ; CHECK-NEXT: ret float 4.200000e+01 + %1 = call float @llvm.maxnan.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @maxnan_float_vec() { +define <4 x float> @maxnan_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.maxnan.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} Index: test/Transforms/InstCombine/maxnan.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/maxnan.ll @@ -0,0 +1,238 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.maxnan.f32(float, float) +declare <2 x float> @llvm.maxnan.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.maxnan.v4f32(<4 x float>, <4 x float>) + +declare double @llvm.maxnan.f64(double, double) +declare <2 x double> @llvm.maxnan.v2f64(<2 x double>, <2 x double>) + +define float @constant_fold_maxnan_f32() { +; CHECK-LABEL: @constant_fold_maxnan_f32( +; CHECK-NEXT: ret float 2.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 1.0, float 2.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_inv() { +; CHECK-LABEL: @constant_fold_maxnan_f32_inv( +; CHECK-NEXT: ret float 2.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 2.0, float 1.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan0( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float 2.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan1() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan1( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 2.0, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan_nan() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_maxnan_f32_p0_p0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_p0_p0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 0.0, float 0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_p0_n0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_p0_n0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 0.0, float -0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_n0_p0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_n0_p0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float -0.0, float 0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_n0_n0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_n0_n0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float -0.0, float -0.0) + ret float %x +} + +define <4 x float> @constant_fold_maxnan_v4f32() { +; CHECK-LABEL: @constant_fold_maxnan_v4f32( +; CHECK-NEXT: ret <4 x float> +; + %x = call <4 x float> @llvm.maxnan.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +define double @constant_fold_maxnan_f64() { +; CHECK-LABEL: @constant_fold_maxnan_f64( +; CHECK-NEXT: ret double 2.000000e+00 +; + %x = call double @llvm.maxnan.f64(double 1.0, double 2.0) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan0() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 0x7FF8000000000000, double 2.0) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan1() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan1( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 2.0, double 0x7FF8000000000000) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan_nan() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan_nan( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) + ret double %x +} + +define float @canonicalize_constant_maxnan_f32(float %x) { +; CHECK-LABEL: @canonicalize_constant_maxnan_f32( +; CHECK-NEXT: [[Y:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float 1.000000e+00) +; CHECK-NEXT: ret float [[Y]] +; + %y = call float @llvm.maxnan.f32(float 1.0, float %x) + ret float %y +} + +define float @maxnan_f32_nan_val(float %x) { +; CHECK-LABEL: @maxnan_f32_nan_val( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float %x) + ret float %y +} + +define float @maxnan_f32_val_nan(float %x) { +; CHECK-LABEL: @maxnan_f32_val_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.maxnan.f32(float %x, float 0x7FF8000000000000) + ret float %y +} + +define float @maxnan4(float %x, float %y, float %z, float %w) { +; CHECK-LABEL: @maxnan4( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[Z:%.*]], float [[W:%.*]]) +; CHECK-NEXT: [[C:%.*]] = call float @llvm.maxnan.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[C]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %z, float %w) + %c = call float @llvm.maxnan.f32(float %a, float %b) + ret float %c +} + +; PR37404 - https://bugs.llvm.org/show_bug.cgi?id=37404 + +define <2 x float> @neg_neg(<2 x float> %x, <2 x float> %y) { +; CHECK-LABEL: @neg_neg( +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @llvm.minnan.v2f32(<2 x float> [[X:%.*]], <2 x float> [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub <2 x float> , [[TMP1]] +; CHECK-NEXT: ret <2 x float> [[R]] +; + %negx = fsub <2 x float> , %x + %negy = fsub <2 x float> , %y + %r = call <2 x float> @llvm.maxnan.v2f32(<2 x float> %negx, <2 x float> %negy) + ret <2 x float> %r +} + +; FMF is not required, but it should be propagated from the intrinsic (not the fnegs). + +define float @neg_neg_vec_fmf(float %x, float %y) { +; CHECK-LABEL: @neg_neg_vec_fmf( +; CHECK-NEXT: [[TMP1:%.*]] = call fast float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub fast float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub arcp float -0.0, %x + %negy = fsub afn float -0.0, %y + %r = call fast float @llvm.maxnan.f32(float %negx, float %negy) + ret float %r +} + +; 1 extra use of an intermediate value should still allow the fold, +; but 2 would require more instructions than we started with. + +declare void @use(float) +define float @neg_neg_extra_use_x(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_x( +; CHECK-NEXT: [[NEGX:%.*]] = fsub float -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.minnan.f32(float [[X]], float [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(float [[NEGX]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negx) + ret float %r +} + +define float @neg_neg_extra_use_y(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_y( +; CHECK-NEXT: [[NEGY:%.*]] = fsub float -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y]]) +; CHECK-NEXT: [[R:%.*]] = fsub float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(float [[NEGY]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negy) + ret float %r +} + +define float @neg_neg_extra_use_x_and_y(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_x_and_y( +; CHECK-NEXT: [[NEGX:%.*]] = fsub float -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[NEGY:%.*]] = fsub float -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call float @llvm.maxnan.f32(float [[NEGX]], float [[NEGY]]) +; CHECK-NEXT: call void @use(float [[NEGX]]) +; CHECK-NEXT: call void @use(float [[NEGY]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negx) + call void @use(float %negy) + ret float %r +} Index: test/Transforms/InstCombine/minnan.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/minnan.ll @@ -0,0 +1,263 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.minnan.f32(float, float) +declare float @llvm.minnan.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.minnan.v4f32(<4 x float>, <4 x float>) + +declare double @llvm.minnan.f64(double, double) +declare <2 x double> @llvm.minnan.v2f64(<2 x double>, <2 x double>) + +declare float @llvm.maxnan.f32(float, float) + +define float @constant_fold_minnan_f32() { +; CHECK-LABEL: @constant_fold_minnan_f32( +; CHECK-NEXT: ret float 1.000000e+00 +; + %x = call float @llvm.minnan.f32(float 1.0, float 2.0) + ret float %x +} + +define float @constant_fold_minnan_f32_inv() { +; CHECK-LABEL: @constant_fold_minnan_f32_inv( +; CHECK-NEXT: ret float 1.000000e+00 +; + %x = call float @llvm.minnan.f32(float 2.0, float 1.0) + ret float %x +} + +define float @constant_fold_minnan_f32_nan0() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan0( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 0x7FF8000000000000, float 2.0) + ret float %x +} + +define float @constant_fold_minnan_f32_nan1() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan1( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 2.0, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_minnan_f32_nan_nan() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_minnan_f32_p0_p0() { +; CHECK-LABEL: @constant_fold_minnan_f32_p0_p0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.minnan.f32(float 0.0, float 0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_p0_n0() { +; CHECK-LABEL: @constant_fold_minnan_f32_p0_n0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.minnan.f32(float 0.0, float -0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_n0_p0() { +; CHECK-LABEL: @constant_fold_minnan_f32_n0_p0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.minnan.f32(float -0.0, float 0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_n0_n0() { +; CHECK-LABEL: @constant_fold_minnan_f32_n0_n0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.minnan.f32(float -0.0, float -0.0) + ret float %x +} + +define <4 x float> @constant_fold_minnan_v4f32() { +; CHECK-LABEL: @constant_fold_minnan_v4f32( +; CHECK-NEXT: ret <4 x float> +; + %x = call <4 x float> @llvm.minnan.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +define double @constant_fold_minnan_f64() { +; CHECK-LABEL: @constant_fold_minnan_f64( +; CHECK-NEXT: ret double 1.000000e+00 +; + %x = call double @llvm.minnan.f64(double 1.0, double 2.0) + ret double %x +} + +define double @constant_fold_minnan_f64_nan0() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 0x7FF8000000000000, double 2.0) + ret double %x +} + +define double @constant_fold_minnan_f64_nan1() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan1( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 2.0, double 0x7FF8000000000000) + ret double %x +} + +define double @constant_fold_minnan_f64_nan_nan() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan_nan( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) + ret double %x +} + +define float @canonicalize_constant_minnan_f32(float %x) { +; CHECK-LABEL: @canonicalize_constant_minnan_f32( +; CHECK-NEXT: [[Y:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float 1.000000e+00) +; CHECK-NEXT: ret float [[Y]] +; + %y = call float @llvm.minnan.f32(float 1.0, float %x) + ret float %y +} + +define float @minnan_f32_nan_val(float %x) { +; CHECK-LABEL: @minnan_f32_nan_val( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.minnan.f32(float 0x7FF8000000000000, float %x) + ret float %y +} + +define float @minnan_f32_val_nan(float %x) { +; CHECK-LABEL: @minnan_f32_val_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.minnan.f32(float %x, float 0x7FF8000000000000) + ret float %y +} + +define float @minnan4(float %x, float %y, float %z, float %w) { +; CHECK-LABEL: @minnan4( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[Z:%.*]], float [[W:%.*]]) +; CHECK-NEXT: [[C:%.*]] = call float @llvm.minnan.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[C]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %z, float %w) + %c = call float @llvm.minnan.f32(float %a, float %b) + ret float %c +} + +define float @minnan_x_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[X]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %x, float %a) + ret float %b +} + +define float @maxnan_x_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[X]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %x, float %a) + ret float %b +} + +; PR37405 - https://bugs.llvm.org/show_bug.cgi?id=37405 + +define double @neg_neg(double %x, double %y) { +; CHECK-LABEL: @neg_neg( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X:%.*]], double [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + ret double %r +} + +; FMF is not required, but it should be propagated from the intrinsic (not the fnegs). +; Also, make sure this works with vectors. + +define <2 x double> @neg_neg_vec_fmf(<2 x double> %x, <2 x double> %y) { +; CHECK-LABEL: @neg_neg_vec_fmf( +; CHECK-NEXT: [[TMP1:%.*]] = call nnan ninf <2 x double> @llvm.maxnan.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub nnan ninf <2 x double> , [[TMP1]] +; CHECK-NEXT: ret <2 x double> [[R]] +; + %negx = fsub reassoc <2 x double> , %x + %negy = fsub fast <2 x double> , %y + %r = call nnan ninf <2 x double> @llvm.minnan.v2f64(<2 x double> %negx, <2 x double> %negy) + ret <2 x double> %r +} + +; 1 extra use of an intermediate value should still allow the fold, +; but 2 would require more instructions than we started with. + +declare void @use(double) +define double @neg_neg_extra_use_x(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_x( +; CHECK-NEXT: [[NEGX:%.*]] = fsub double -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X]], double [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(double [[NEGX]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negx) + ret double %r +} + +define double @neg_neg_extra_use_y(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_y( +; CHECK-NEXT: [[NEGY:%.*]] = fsub double -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X:%.*]], double [[Y]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(double [[NEGY]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negy) + ret double %r +} + +define double @neg_neg_extra_use_x_and_y(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_x_and_y( +; CHECK-NEXT: [[NEGX:%.*]] = fsub double -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[NEGY:%.*]] = fsub double -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call double @llvm.minnan.f64(double [[NEGX]], double [[NEGY]]) +; CHECK-NEXT: call void @use(double [[NEGX]]) +; CHECK-NEXT: call void @use(double [[NEGY]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negx) + call void @use(double %negy) + ret double %r +} Index: test/Transforms/InstSimplify/floating-point-arithmetic.ll =================================================================== --- test/Transforms/InstSimplify/floating-point-arithmetic.ll +++ test/Transforms/InstSimplify/floating-point-arithmetic.ll @@ -790,6 +790,322 @@ ret float %val } +declare float @llvm.minnan.f32(float, float) +declare float @llvm.maxnan.f32(float, float) +declare double @llvm.minnan.f64(double, double) +declare double @llvm.maxnan.f64(double, double) +declare <2 x double> @llvm.minnan.v2f64(<2 x double>, <2 x double>) +declare <2 x double> @llvm.maxnan.v2f64(<2 x double>, <2 x double>) + +; From the LangRef for minnan/maxnan: +; "If either operand is a NaN, returns NaN." + +define double @maxnan_nan_op0(double %x) { +; CHECK-LABEL: @maxnan_nan_op0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %r = call double @llvm.maxnan.f64(double 0x7ff8000000000000, double %x) + ret double %r +} + +define double @maxnan_nan_op1(double %x) { +; CHECK-LABEL: @maxnan_nan_op1( +; CHECK-NEXT: ret double 0x7FF800000000DEAD +; + %r = call double @llvm.maxnan.f64(double %x, double 0x7ff800000000dead) + ret double %r +} + +define double @minnan_nan_op0(double %x) { +; CHECK-LABEL: @minnan_nan_op0( +; CHECK-NEXT: ret double 0x7FF8000DEAD00000 +; + %r = call double @llvm.minnan.f64(double 0x7ff8000dead00000, double %x) + ret double %r +} + +define double @minnan_nan_op1(double %x) { +; CHECK-LABEL: @minnan_nan_op1( +; CHECK-NEXT: ret double 0x7FF800DEAD00DEAD +; + %r = call double @llvm.minnan.f64(double %x, double 0x7ff800dead00dead) + ret double %r +} + +define <2 x double> @maxnan_nan_op0_vec(<2 x double> %x) { +; CHECK-LABEL: @maxnan_nan_op0_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.maxnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +define <2 x double> @maxnan_nan_op1_vec(<2 x double> %x) { +; CHECK-LABEL: @maxnan_nan_op1_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.maxnan.v2f64(<2 x double> %x, <2 x double> ) + ret <2 x double> %r +} + +define <2 x double> @minnan_nan_op0_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_nan_op0_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +define <2 x double> @minnan_nan_op1_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_nan_op1_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> %x, <2 x double> ) + ret <2 x double> %r +} + +define float @maxnan_undef_op1(float %x) { +; CHECK-LABEL: @maxnan_undef_op1( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.maxnan.f32(float %x, float undef) + ret float %val +} + +define float @maxnan_undef_op0(float %x) { +; CHECK-LABEL: @maxnan_undef_op0( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.maxnan.f32(float undef, float %x) + ret float %val +} + +define float @minnan_undef_op1(float %x) { +; CHECK-LABEL: @minnan_undef_op1( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.minnan.f32(float %x, float undef) + ret float %val +} + +define float @minnan_undef_op0(float %x) { +; CHECK-LABEL: @minnan_undef_op0( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.minnan.f32(float undef, float %x) + ret float %val +} + +define float @minnan_undef_undef(float %x) { +; CHECK-LABEL: @minnan_undef_undef( +; CHECK-NEXT: ret float undef +; + %val = call float @llvm.minnan.f32(float undef, float undef) + ret float %val +} + +define float @maxnan_undef_undef(float %x) { +; CHECK-LABEL: @maxnan_undef_undef( +; CHECK-NEXT: ret float undef +; + %val = call float @llvm.maxnan.f32(float undef, float undef) + ret float %val +} + +define float @minnan_same_args(float %x) { +; CHECK-LABEL: @minnan_same_args( +; CHECK-NEXT: ret float [[X:%.*]] +; + %y = call float @llvm.minnan.f32(float %x, float %x) + ret float %y +} + +define float @maxnan_same_args(float %x) { +; CHECK-LABEL: @maxnan_same_args( +; CHECK-NEXT: ret float [[X:%.*]] +; + %y = call float @llvm.maxnan.f32(float %x, float %x) + ret float %y +} + +define float @minnan_x_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %x, float %a) + ret float %b +} + +define float @minnan_y_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_y_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %y, float %a) + ret float %b +} + +define float @minnan_x_y_minnan_x(float %x, float %y) { +; CHECK-LABEL: @minnan_x_y_minnan_x( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %x) + ret float %b +} + +define float @minnan_x_y_minnan_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_y_minnan_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %y) + ret float %b +} + +; negative test + +define float @minnan_z_minnan_x_y(float %x, float %y, float %z) { +; CHECK-LABEL: @minnan_z_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[Z:%.*]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %z, float %a) + ret float %b +} + +; negative test + +define float @minnan_x_y_minnan_z(float %x, float %y, float %z) { +; CHECK-LABEL: @minnan_x_y_minnan_z( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[A]], float [[Z:%.*]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %z) + ret float %b +} + +; minnan(X, -INF) --> -INF + +define float @minnan_neginf(float %x) { +; CHECK-LABEL: @minnan_neginf( +; CHECK-NEXT: ret float 0xFFF0000000000000 +; + %val = call float @llvm.minnan.f32(float %x, float 0xFFF0000000000000) + ret float %val +} + +define <2 x double> @minnan_neginf_commute_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_neginf_commute_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +; negative test + +define float @minnan_inf(float %x) { +; CHECK-LABEL: @minnan_inf( +; CHECK-NEXT: [[VAL:%.*]] = call float @llvm.minnan.f32(float 0x7FF0000000000000, float [[X:%.*]]) +; CHECK-NEXT: ret float [[VAL]] +; + %val = call float @llvm.minnan.f32(float 0x7FF0000000000000, float %x) + ret float %val +} +define float @maxnan_x_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %x, float %a) + ret float %b +} + +define float @maxnan_y_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_y_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %y, float %a) + ret float %b +} + +define float @maxnan_x_y_maxnan_x(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_y_maxnan_x( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %x) + ret float %b +} + +define float @maxnan_x_y_maxnan_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_y_maxnan_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %y) + ret float %b +} + +; negative test + +define float @maxnan_z_maxnan_x_y(float %x, float %y, float %z) { +; CHECK-LABEL: @maxnan_z_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[Z:%.*]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %z, float %a) + ret float %b +} + +; negative test + +define float @maxnan_x_y_maxnan_z(float %x, float %y, float %z) { +; CHECK-LABEL: @maxnan_x_y_maxnan_z( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[A]], float [[Z:%.*]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %z) + ret float %b +} + +; maxnan(X, INF) --> INF + +define <2 x double> @maxnan_inf(<2 x double> %x) { +; CHECK-LABEL: @maxnan_inf( +; CHECK-NEXT: ret <2 x double> +; + %val = call <2 x double> @llvm.maxnan.v2f64(<2 x double> %x, <2 x double>) + ret <2 x double> %val +} + +define float @maxnan_inf_commute(float %x) { +; CHECK-LABEL: @maxnan_inf_commute( +; CHECK-NEXT: ret float 0x7FF0000000000000 +; + %val = call float @llvm.maxnan.f32(float 0x7FF0000000000000, float %x) + ret float %val +} + ; Y - (Y - X) --> X define float @fsub_fsub_common_op(float %x, float %y) { Index: test/Transforms/InstSimplify/floating-point-compare.ll =================================================================== --- test/Transforms/InstSimplify/floating-point-compare.ll +++ test/Transforms/InstSimplify/floating-point-compare.ll @@ -179,6 +179,7 @@ declare float @llvm.exp.f32(float) declare float @llvm.minnum.f32(float, float) declare float @llvm.maxnum.f32(float, float) +declare float @llvm.maxnan.f32(float, float) declare double @llvm.exp2.f64(double) declare float @llvm.fma.f32(float,float,float) @@ -282,6 +283,18 @@ ret i1 %uge } +; But using maxnan, we can simplify, since the NaN would be propagated + +define i1 @orderedLessZeroMaxNaN(float, float) { +; CHECK-LABEL: @orderedLessZeroMaxNaN( +; CHECK-NEXT: ret i1 true +; + %a = call float @llvm.exp.f32(float %0) + %b = call float @llvm.maxnan.f32(float %a, float %1) + %uge = fcmp uge float %b, 0.000000e+00 + ret i1 %uge +} + define i1 @known_positive_olt_with_negative_constant(double %a) { ; CHECK-LABEL: @known_positive_olt_with_negative_constant( ; CHECK-NEXT: ret i1 false Index: test/Transforms/LICM/hoist-round.ll =================================================================== --- test/Transforms/LICM/hoist-round.ll +++ test/Transforms/LICM/hoist-round.ll @@ -4,8 +4,8 @@ target datalayout = "E-m:e-p:32:32-i8:8:8-i16:16:16-i64:32:32-f64:32:32-v64:32:32-v128:32:32-a0:0:32-n32" ; This test verifies that ceil, floor, nearbyint, trunc, rint, round, -; copysign, minnum, maxnum and fabs intrinsics are considered safe -; to speculate. +; copysign, minnum, maxnum minnan, maxnan, and fabs intrinsics are +; considered safe to speculate. ; CHECK-LABEL: @test ; CHECK: call float @llvm.ceil.f32 @@ -41,8 +41,10 @@ %tmp.8 = call float @llvm.copysign.f32(float %tmp.7, float %arg2) %tmp.9 = call float @llvm.minnum.f32(float %tmp.8, float %arg2) %tmp.10 = call float @llvm.maxnum.f32(float %tmp.9, float %arg2) - %tmp.11 = call float @llvm.powi.f32(float %tmp.10, i32 4) - call void @consume(float %tmp.11) + %tmp.11 = call float @llvm.minnan.f32(float %tmp.10, float %arg2) + %tmp.12 = call float @llvm.maxnan.f32(float %tmp.11, float %arg2) + %tmp.13 = call float @llvm.powi.f32(float %tmp.12, i32 4) + call void @consume(float %tmp.13) %IND.new = add i32 %IND, 1 br label %for.head @@ -62,4 +64,6 @@ declare float @llvm.copysign.f32(float, float) declare float @llvm.minnum.f32(float, float) declare float @llvm.maxnum.f32(float, float) +declare float @llvm.minnan.f32(float, float) +declare float @llvm.maxnan.f32(float, float) declare float @llvm.powi.f32(float, i32) Index: test/Transforms/LoopVectorize/intrinsic.ll =================================================================== --- test/Transforms/LoopVectorize/intrinsic.ll +++ test/Transforms/LoopVectorize/intrinsic.ll @@ -1247,3 +1247,59 @@ for.end: ; preds = %for.body, %entry ret void } + +declare float @llvm.minnan.f32(float, float) nounwind readnone + +;CHECK-LABEL: @minnan_f32( +;CHECK: llvm.minnan.v4f32 +;CHECK: ret void +define void @minnan_f32(i32 %n, float* noalias %y, float* noalias %x, float* noalias %z) nounwind uwtable { +entry: + %cmp9 = icmp sgt i32 %n, 0 + br i1 %cmp9, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float, float* %y, i64 %indvars.iv + %0 = load float, float* %arrayidx, align 4 + %arrayidx2 = getelementptr inbounds float, float* %z, i64 %indvars.iv + %1 = load float, float* %arrayidx2, align 4 + %call = tail call float @llvm.minnan.f32(float %0, float %1) nounwind readnone + %arrayidx4 = getelementptr inbounds float, float* %x, i64 %indvars.iv + store float %call, float* %arrayidx4, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} + +declare float @llvm.maxnan.f32(float, float) nounwind readnone + +;CHECK-LABEL: @maxnan_f32( +;CHECK: llvm.maxnan.v4f32 +;CHECK: ret void +define void @maxnan_f32(i32 %n, float* noalias %y, float* noalias %x, float* noalias %z) nounwind uwtable { +entry: + %cmp9 = icmp sgt i32 %n, 0 + br i1 %cmp9, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float, float* %y, i64 %indvars.iv + %0 = load float, float* %arrayidx, align 4 + %arrayidx2 = getelementptr inbounds float, float* %z, i64 %indvars.iv + %1 = load float, float* %arrayidx2, align 4 + %call = tail call float @llvm.maxnan.f32(float %0, float %1) nounwind readnone + %arrayidx4 = getelementptr inbounds float, float* %x, i64 %indvars.iv + store float %call, float* %arrayidx4, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} Index: test/Transforms/Scalarizer/intrinsics.ll =================================================================== --- test/Transforms/Scalarizer/intrinsics.ll +++ test/Transforms/Scalarizer/intrinsics.ll @@ -5,6 +5,8 @@ ; Binary fp declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>) +declare <2 x float> @llvm.minnan.v2f32(<2 x float>, <2 x float>) +declare <2 x float> @llvm.maxnan.v2f32(<2 x float>, <2 x float>) ; Ternary fp declare <2 x float> @llvm.fma.v2f32(<2 x float>, <2 x float>, <2 x float>) @@ -40,6 +42,28 @@ ret <2 x float> %minnum } +; CHECK-LABEL: @scalarize_minnan_v2f32( +; CHECK: %minnan.i0 = call float @llvm.minnan.f32(float %x.i0, float %y.i0) +; CHECK: %minnan.i1 = call float @llvm.minnan.f32(float %x.i1, float %y.i1) +; CHECK: %minnan.upto0 = insertelement <2 x float> undef, float %minnan.i0, i32 0 +; CHECK: %minnan = insertelement <2 x float> %minnan.upto0, float %minnan.i1, i32 1 +; CHECK: ret <2 x float> %minnan +define <2 x float> @scalarize_minnan_v2f32(<2 x float> %x, <2 x float> %y) #0 { + %minnan = call <2 x float> @llvm.minnan.v2f32(<2 x float> %x, <2 x float> %y) + ret <2 x float> %minnan +} + +; CHECK-LABEL: @scalarize_maxnan_v2f32( +; CHECK: %maxnan.i0 = call float @llvm.maxnan.f32(float %x.i0, float %y.i0) +; CHECK: %maxnan.i1 = call float @llvm.maxnan.f32(float %x.i1, float %y.i1) +; CHECK: %maxnan.upto0 = insertelement <2 x float> undef, float %maxnan.i0, i32 0 +; CHECK: %maxnan = insertelement <2 x float> %maxnan.upto0, float %maxnan.i1, i32 1 +; CHECK: ret <2 x float> %maxnan +define <2 x float> @scalarize_maxnan_v2f32(<2 x float> %x, <2 x float> %y) #0 { + %maxnan = call <2 x float> @llvm.maxnan.v2f32(<2 x float> %x, <2 x float> %y) + ret <2 x float> %maxnan +} + ; CHECK-LABEL: @scalarize_fma_v2f32( ; CHECK: %fma.i0 = call float @llvm.fma.f32(float %x.i0, float %y.i0, float %z.i0) ; CHECK: %fma.i1 = call float @llvm.fma.f32(float %x.i1, float %y.i1, float %z.i1) Index: test/Transforms/SimplifyCFG/speculate-math.ll =================================================================== --- test/Transforms/SimplifyCFG/speculate-math.ll +++ test/Transforms/SimplifyCFG/speculate-math.ll @@ -7,6 +7,8 @@ declare float @llvm.fabs.f32(float) nounwind readonly declare float @llvm.minnum.f32(float, float) nounwind readonly declare float @llvm.maxnum.f32(float, float) nounwind readonly +declare float @llvm.minnan.f32(float, float) nounwind readonly +declare float @llvm.maxnan.f32(float, float) nounwind readonly ; ALL-LABEL: @fdiv_test( ; EXPENSIVE: select i1 %cmp, double %div, double 0.0 @@ -127,3 +129,37 @@ store float %cond.i, float addrspace(1)* %out, align 4 ret void } + +; ALL-LABEL: @minnan_test( +; ALL: select +define void @minnan_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_minnan.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.minnan.f32(float %a, float %b) nounwind readnone + br label %test_minnan.exit + +test_minnan.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +} + +; ALL-LABEL: @maxnan_test( +; ALL: select +define void @maxnan_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_maxnan.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.maxnan.f32(float %a, float %b) nounwind readnone + br label %test_maxnan.exit + +test_maxnan.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +} Index: unittests/ADT/APFloatTest.cpp =================================================================== --- unittests/ADT/APFloatTest.cpp +++ unittests/ADT/APFloatTest.cpp @@ -555,6 +555,28 @@ EXPECT_EQ(1.0, maxnum(nan, f1).convertToDouble()); } +TEST(APFloatTest, MinNan) { + APFloat f1(1.0); + APFloat f2(2.0); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); + + EXPECT_EQ(1.0, minnan(f1, f2).convertToDouble()); + EXPECT_EQ(1.0, minnan(f2, f1).convertToDouble()); + EXPECT_TRUE(std::isnan(minnan(f1, nan).convertToDouble())); + EXPECT_TRUE(std::isnan(minnan(nan, f1).convertToDouble())); +} + +TEST(APFloatTest, MaxNan) { + APFloat f1(1.0); + APFloat f2(2.0); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); + + EXPECT_EQ(2.0, maxnan(f1, f2).convertToDouble()); + EXPECT_EQ(2.0, maxnan(f2, f1).convertToDouble()); + EXPECT_TRUE(std::isnan(maxnan(f1, nan).convertToDouble())); + EXPECT_TRUE(std::isnan(maxnan(nan, f1).convertToDouble())); +} + TEST(APFloatTest, Denormal) { APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven; Index: unittests/IR/IRBuilderTest.cpp =================================================================== --- unittests/IR/IRBuilderTest.cpp +++ unittests/IR/IRBuilderTest.cpp @@ -64,6 +64,14 @@ II = cast(Call); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnum); + Call = Builder.CreateMinNan(V, V); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minnan); + + Call = Builder.CreateMaxNan(V, V); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnan); + Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter); II = cast(Call); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);