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,24 @@ return (A.compare(B) == APFloat::cmpLessThan) ? B : A; } +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; +} + +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/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/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5588,6 +5588,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: 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 +}