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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
///
/// 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.
return CreateBinaryIntrinsic(Intrinsic::maxnum, LHS, RHS, nullptr, Name);
}
+ /// Create call to the minimum intrinsic.
+ CallInst *CreateMinimum(Value *LHS, Value *RHS, const Twine &Name = "") {
+ return CreateBinaryIntrinsic(Intrinsic::minimum, LHS, RHS, nullptr, Name);
+ }
+
+ /// Create call to the maximum intrinsic.
+ CallInst *CreateMaximum(Value *LHS, Value *RHS, const Twine &Name = "") {
+ return CreateBinaryIntrinsic(Intrinsic::maximum, LHS, RHS, nullptr, Name);
+ }
+
private:
/// Create a call to a masked intrinsic with given Id.
CallInst *CreateMaskedIntrinsic(Intrinsic::ID Id, ArrayRef<Value *> Ops,
[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]>;
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(),
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;
II = cast<IntrinsicInst>(Call);
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnum);
+ Call = Builder.CreateMinimum(V, V);
+ II = cast<IntrinsicInst>(Call);
+ EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minimum);
+
+ Call = Builder.CreateMaximum(V, V);
+ II = cast<IntrinsicInst>(Call);
+ EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maximum);
+
Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter, {}, {});
II = cast<IntrinsicInst>(Call);
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);