summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2018-10-13 07:21:44 +0000
committerThomas Lively <tlively@google.com>2018-10-13 07:21:44 +0000
commit16c349d892d4e2870c8bf73f943e37310a0d45ff (patch)
treea3b17f77a79102e265d226159eee584adbfd9e02
parent0ff82ac154560a0bb2f30f97484fb41aeb4cc884 (diff)
downloadbcm5719-llvm-16c349d892d4e2870c8bf73f943e37310a0d45ff.tar.gz
bcm5719-llvm-16c349d892d4e2870c8bf73f943e37310a0d45ff.zip
[Intrinsic] Add llvm.minimum and llvm.maximum instrinsic functions
Summary: These new intrinsics have the semantics of the `minimum` and `maximum` operations specified by the latest draft of IEEE 754-2018. Unlike llvm.minnum and llvm.maxnum, these new intrinsics propagate NaNs and always treat -0.0 as less than 0.0. `minimum` and `maximum` lower directly to the existing `fminnan` and `fmaxnan` ISel DAG nodes. It is safe to reuse these DAG nodes because before this patch were only emitted in situations where there were known to be no NaN arguments or where NaN propagation was correct and there were known to be no zero arguments. I know of only four backends that lower fminnan and fmaxnan: WebAssembly, ARM, AArch64, and SystemZ, and each of these lowers fminnan and fmaxnan to instructions that are compatible with the IEEE 754-2018 semantics. Reviewers: aheejin, dschuff, sunfish, javed.absar Subscribers: kristof.beyls, dexonsmith, kristina, llvm-commits Differential Revision: https://reviews.llvm.org/D52764 llvm-svn: 344437
-rw-r--r--llvm/docs/LangRef.rst76
-rw-r--r--llvm/include/llvm/ADT/APFloat.h26
-rw-r--r--llvm/include/llvm/CodeGen/ISDOpcodes.h5
-rw-r--r--llvm/include/llvm/IR/IRBuilder.h10
-rw-r--r--llvm/include/llvm/IR/Intrinsics.td8
-rw-r--r--llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp12
-rw-r--r--llvm/unittests/ADT/APFloatTest.cpp30
-rw-r--r--llvm/unittests/IR/IRBuilderTest.cpp8
8 files changed, 173 insertions, 2 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9fcfd29a6e8..e977657d1cb 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -11560,6 +11560,82 @@ NaN, the intrinsic lowering is responsible for quieting the inputs to
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 5c59af4c04b..52ed183c78a 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1243,6 +1243,32 @@ inline APFloat maxnum(const APFloat &A, const APFloat &B) {
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
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index ec9c46140d7..d9a513fe247 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -560,8 +560,9 @@ namespace ISD {
///
/// 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.
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index 0af53c5b3f4..e89c44380d0 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -705,6 +705,16 @@ public:
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,
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index b405e86ef40..410e35f9acb 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -453,6 +453,14 @@ def int_maxnum : Intrinsic<[llvm_anyfloat_ty],
[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]>;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 868160c77a3..f7866665bcb 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -5584,6 +5584,18 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
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(),
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 1212b45fb57..b739e857849 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -555,6 +555,36 @@ TEST(APFloatTest, MaxNum) {
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;
diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp
index 42c0393d382..713c0a14f66 100644
--- a/llvm/unittests/IR/IRBuilderTest.cpp
+++ b/llvm/unittests/IR/IRBuilderTest.cpp
@@ -68,6 +68,14 @@ TEST_F(IRBuilderTest, Intrinsics) {
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);
OpenPOWER on IntegriCloud