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.minimum.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.minimum`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare float @llvm.minimum.f32(float %Val0, float %Val1) + declare double @llvm.minimum.f64(double %Val0, double %Val1) + declare x86_fp80 @llvm.minimum.f80(x86_fp80 %Val0, x86_fp80 %Val1) + declare fp128 @llvm.minimum.f128(fp128 %Val0, fp128 %Val1) + declare ppc_fp128 @llvm.minimum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1) + +Overview: +""""""""" + +The '``llvm.minimum.*``' intrinsics return the minimum of the two +arguments, propagating NaNs and treating -0.0 as less than +0.0. + + +Arguments: +"""""""""" + +The arguments and return value are floating-point numbers of the same +type. + +Semantics: +"""""""""" +If either operand is a NaN, returns NaN. Otherwise returns the lesser +of the two arguments. -0.0 is considered to be less than +0.0 for this +intrinsic. Note that these are the semantics specified in the draft of +IEEE 754-2018. + +'``llvm.maximum.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use ``llvm.maximum`` on any +floating-point or vector of floating-point type. Not all targets support +all types however. + +:: + + declare float @llvm.maximum.f32(float %Val0, float %Val1) + declare double @llvm.maximum.f64(double %Val0, double %Val1) + declare x86_fp80 @llvm.maximum.f80(x86_fp80 %Val0, x86_fp80 %Val1) + declare fp128 @llvm.maximum.f128(fp128 %Val0, fp128 %Val1) + declare ppc_fp128 @llvm.maximum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1) + +Overview: +""""""""" + +The '``llvm.maximum.*``' intrinsics return the maximum of the two +arguments, propagating NaNs and treating -0.0 as less than +0.0. + + +Arguments: +"""""""""" + +The arguments and return value are floating-point numbers of the same +type. + +Semantics: +"""""""""" +If either operand is a NaN, returns NaN. Otherwise returns the greater +of the two arguments. -0.0 is considered to be less than +0.0 for this +intrinsic. Note that these are the semantics specified in the draft of +IEEE 754-2018. + '``llvm.copysign.*``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: include/llvm/ADT/APFloat.h =================================================================== --- include/llvm/ADT/APFloat.h +++ include/llvm/ADT/APFloat.h @@ -1243,6 +1243,32 @@ return (A.compare(B) == APFloat::cmpLessThan) ? B : A; } +/// Implements IEEE 754-2018 minimum semantics. Returns the smaller of 2 +/// arguments, propagating NaNs and treating -0 as less than +0. +LLVM_READONLY +inline APFloat minimum(const APFloat &A, const APFloat &B) { + if (A.isNaN()) + return A; + if (B.isNaN()) + return B; + if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative())) + return A.isNegative() ? A : B; + return (B.compare(A) == APFloat::cmpLessThan) ? B : A; +} + +/// Implements IEEE 754-2018 maximum semantics. Returns the larger of 2 +/// arguments, propagating NaNs and treating -0 as less than +0. +LLVM_READONLY +inline APFloat maximum(const APFloat &A, const APFloat &B) { + if (A.isNaN()) + return A; + if (B.isNaN()) + return B; + if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative())) + return A.isNegative() ? B : A; + return (A.compare(B) == APFloat::cmpLessThan) ? B : A; +} + } // namespace llvm #undef APFLOAT_DISPATCH_ON_SEMANTICS Index: include/llvm/CodeGen/ISDOpcodes.h =================================================================== --- include/llvm/CodeGen/ISDOpcodes.h +++ include/llvm/CodeGen/ISDOpcodes.h @@ -560,8 +560,9 @@ /// /// The return value of (FMINNUM 0.0, -0.0) could be either 0.0 or -0.0. FMINNUM, FMAXNUM, - /// FMINNAN/FMAXNAN - Behave identically to FMINNUM/FMAXNUM, except that - /// when a single input is NaN, NaN is returned. + /// FMINNAN/FMAXNAN - NaN-propagating minimum/maximum that also treat -0.0 + /// as less than 0.0. While FMINNUM/FMAXNUM follow IEEE 754-2008 semantics, + /// FMINNAN/FMAXNAN follow IEEE 754-2018 draft semantics. FMINNAN, FMAXNAN, /// FSINCOS - Compute both fsin and fcos as a single operation. 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 minimum intrinsic. + CallInst *CreateMinimum(Value *LHS, Value *RHS, const Twine &Name = "") { + return CreateBinaryIntrinsic(Intrinsic::minimum, LHS, RHS, Name); + } + + /// Create call to the maximum intrinsic. + CallInst *CreateMaximum(Value *LHS, Value *RHS, const Twine &Name = "") { + return CreateBinaryIntrinsic(Intrinsic::maximum, 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_minimum : Intrinsic<[llvm_anyfloat_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable, Commutative] +>; +def int_maximum : 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/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5584,6 +5584,18 @@ getValue(I.getArgOperand(1)))); return nullptr; } + case Intrinsic::minimum: + setValue(&I, DAG.getNode(ISD::FMINNAN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; + case Intrinsic::maximum: + 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: unittests/ADT/APFloatTest.cpp =================================================================== --- unittests/ADT/APFloatTest.cpp +++ unittests/ADT/APFloatTest.cpp @@ -555,6 +555,36 @@ EXPECT_EQ(1.0, maxnum(nan, f1).convertToDouble()); } +TEST(APFloatTest, Minimum) { + APFloat f1(1.0); + APFloat f2(2.0); + APFloat zp(0.0); + APFloat zn(-0.0); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); + + EXPECT_EQ(1.0, minimum(f1, f2).convertToDouble()); + EXPECT_EQ(1.0, minimum(f2, f1).convertToDouble()); + EXPECT_EQ(-0.0, minimum(zp, zn).convertToDouble()); + EXPECT_EQ(-0.0, minimum(zn, zp).convertToDouble()); + EXPECT_TRUE(std::isnan(minimum(f1, nan).convertToDouble())); + EXPECT_TRUE(std::isnan(minimum(nan, f1).convertToDouble())); +} + +TEST(APFloatTest, Maximum) { + APFloat f1(1.0); + APFloat f2(2.0); + APFloat zp(0.0); + APFloat zn(-0.0); + APFloat nan = APFloat::getNaN(APFloat::IEEEdouble()); + + EXPECT_EQ(2.0, maximum(f1, f2).convertToDouble()); + EXPECT_EQ(2.0, maximum(f2, f1).convertToDouble()); + EXPECT_EQ(0.0, maximum(zp, zn).convertToDouble()); + EXPECT_EQ(0.0, maximum(zn, zp).convertToDouble()); + EXPECT_TRUE(std::isnan(maximum(f1, nan).convertToDouble())); + EXPECT_TRUE(std::isnan(maximum(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.CreateMinimum(V, V); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minimum); + + Call = Builder.CreateMaximum(V, V); + II = cast(Call); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maximum); + Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter); II = cast(Call); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);