Skip to content

Commit 16c349d

Browse files
committed
[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
1 parent 0ff82ac commit 16c349d

File tree

8 files changed

+173
-2
lines changed

8 files changed

+173
-2
lines changed

llvm/docs/LangRef.rst

+76
Original file line numberDiff line numberDiff line change
@@ -11560,6 +11560,82 @@ NaN, the intrinsic lowering is responsible for quieting the inputs to
1156011560
correctly return the non-NaN input (e.g. by using the equivalent of
1156111561
``llvm.canonicalize``).
1156211562

11563+
'``llvm.minimum.*``' Intrinsic
11564+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11565+
11566+
Syntax:
11567+
"""""""
11568+
11569+
This is an overloaded intrinsic. You can use ``llvm.minimum`` on any
11570+
floating-point or vector of floating-point type. Not all targets support
11571+
all types however.
11572+
11573+
::
11574+
11575+
declare float @llvm.minimum.f32(float %Val0, float %Val1)
11576+
declare double @llvm.minimum.f64(double %Val0, double %Val1)
11577+
declare x86_fp80 @llvm.minimum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
11578+
declare fp128 @llvm.minimum.f128(fp128 %Val0, fp128 %Val1)
11579+
declare ppc_fp128 @llvm.minimum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
11580+
11581+
Overview:
11582+
"""""""""
11583+
11584+
The '``llvm.minimum.*``' intrinsics return the minimum of the two
11585+
arguments, propagating NaNs and treating -0.0 as less than +0.0.
11586+
11587+
11588+
Arguments:
11589+
""""""""""
11590+
11591+
The arguments and return value are floating-point numbers of the same
11592+
type.
11593+
11594+
Semantics:
11595+
""""""""""
11596+
If either operand is a NaN, returns NaN. Otherwise returns the lesser
11597+
of the two arguments. -0.0 is considered to be less than +0.0 for this
11598+
intrinsic. Note that these are the semantics specified in the draft of
11599+
IEEE 754-2018.
11600+
11601+
'``llvm.maximum.*``' Intrinsic
11602+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11603+
11604+
Syntax:
11605+
"""""""
11606+
11607+
This is an overloaded intrinsic. You can use ``llvm.maximum`` on any
11608+
floating-point or vector of floating-point type. Not all targets support
11609+
all types however.
11610+
11611+
::
11612+
11613+
declare float @llvm.maximum.f32(float %Val0, float %Val1)
11614+
declare double @llvm.maximum.f64(double %Val0, double %Val1)
11615+
declare x86_fp80 @llvm.maximum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
11616+
declare fp128 @llvm.maximum.f128(fp128 %Val0, fp128 %Val1)
11617+
declare ppc_fp128 @llvm.maximum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
11618+
11619+
Overview:
11620+
"""""""""
11621+
11622+
The '``llvm.maximum.*``' intrinsics return the maximum of the two
11623+
arguments, propagating NaNs and treating -0.0 as less than +0.0.
11624+
11625+
11626+
Arguments:
11627+
""""""""""
11628+
11629+
The arguments and return value are floating-point numbers of the same
11630+
type.
11631+
11632+
Semantics:
11633+
""""""""""
11634+
If either operand is a NaN, returns NaN. Otherwise returns the greater
11635+
of the two arguments. -0.0 is considered to be less than +0.0 for this
11636+
intrinsic. Note that these are the semantics specified in the draft of
11637+
IEEE 754-2018.
11638+
1156311639
'``llvm.copysign.*``' Intrinsic
1156411640
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1156511641

llvm/include/llvm/ADT/APFloat.h

+26
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,32 @@ inline APFloat maxnum(const APFloat &A, const APFloat &B) {
12431243
return (A.compare(B) == APFloat::cmpLessThan) ? B : A;
12441244
}
12451245

1246+
/// Implements IEEE 754-2018 minimum semantics. Returns the smaller of 2
1247+
/// arguments, propagating NaNs and treating -0 as less than +0.
1248+
LLVM_READONLY
1249+
inline APFloat minimum(const APFloat &A, const APFloat &B) {
1250+
if (A.isNaN())
1251+
return A;
1252+
if (B.isNaN())
1253+
return B;
1254+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1255+
return A.isNegative() ? A : B;
1256+
return (B.compare(A) == APFloat::cmpLessThan) ? B : A;
1257+
}
1258+
1259+
/// Implements IEEE 754-2018 maximum semantics. Returns the larger of 2
1260+
/// arguments, propagating NaNs and treating -0 as less than +0.
1261+
LLVM_READONLY
1262+
inline APFloat maximum(const APFloat &A, const APFloat &B) {
1263+
if (A.isNaN())
1264+
return A;
1265+
if (B.isNaN())
1266+
return B;
1267+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1268+
return A.isNegative() ? B : A;
1269+
return (A.compare(B) == APFloat::cmpLessThan) ? B : A;
1270+
}
1271+
12461272
} // namespace llvm
12471273

12481274
#undef APFLOAT_DISPATCH_ON_SEMANTICS

llvm/include/llvm/CodeGen/ISDOpcodes.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,9 @@ namespace ISD {
560560
///
561561
/// The return value of (FMINNUM 0.0, -0.0) could be either 0.0 or -0.0.
562562
FMINNUM, FMAXNUM,
563-
/// FMINNAN/FMAXNAN - Behave identically to FMINNUM/FMAXNUM, except that
564-
/// when a single input is NaN, NaN is returned.
563+
/// FMINNAN/FMAXNAN - NaN-propagating minimum/maximum that also treat -0.0
564+
/// as less than 0.0. While FMINNUM/FMAXNUM follow IEEE 754-2008 semantics,
565+
/// FMINNAN/FMAXNAN follow IEEE 754-2018 draft semantics.
565566
FMINNAN, FMAXNAN,
566567

567568
/// FSINCOS - Compute both fsin and fcos as a single operation.

llvm/include/llvm/IR/IRBuilder.h

+10
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,16 @@ class IRBuilderBase {
705705
return CreateBinaryIntrinsic(Intrinsic::maxnum, LHS, RHS, nullptr, Name);
706706
}
707707

708+
/// Create call to the minimum intrinsic.
709+
CallInst *CreateMinimum(Value *LHS, Value *RHS, const Twine &Name = "") {
710+
return CreateBinaryIntrinsic(Intrinsic::minimum, LHS, RHS, nullptr, Name);
711+
}
712+
713+
/// Create call to the maximum intrinsic.
714+
CallInst *CreateMaximum(Value *LHS, Value *RHS, const Twine &Name = "") {
715+
return CreateBinaryIntrinsic(Intrinsic::maximum, LHS, RHS, nullptr, Name);
716+
}
717+
708718
private:
709719
/// Create a call to a masked intrinsic with given Id.
710720
CallInst *CreateMaskedIntrinsic(Intrinsic::ID Id, ArrayRef<Value *> Ops,

llvm/include/llvm/IR/Intrinsics.td

+8
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,14 @@ def int_maxnum : Intrinsic<[llvm_anyfloat_ty],
453453
[LLVMMatchType<0>, LLVMMatchType<0>],
454454
[IntrNoMem, IntrSpeculatable, Commutative]
455455
>;
456+
def int_minimum : Intrinsic<[llvm_anyfloat_ty],
457+
[LLVMMatchType<0>, LLVMMatchType<0>],
458+
[IntrNoMem, IntrSpeculatable, Commutative]
459+
>;
460+
def int_maximum : Intrinsic<[llvm_anyfloat_ty],
461+
[LLVMMatchType<0>, LLVMMatchType<0>],
462+
[IntrNoMem, IntrSpeculatable, Commutative]
463+
>;
456464

457465
// NOTE: these are internal interfaces.
458466
def int_setjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty]>;

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -5584,6 +5584,18 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
55845584
getValue(I.getArgOperand(1))));
55855585
return nullptr;
55865586
}
5587+
case Intrinsic::minimum:
5588+
setValue(&I, DAG.getNode(ISD::FMINNAN, sdl,
5589+
getValue(I.getArgOperand(0)).getValueType(),
5590+
getValue(I.getArgOperand(0)),
5591+
getValue(I.getArgOperand(1))));
5592+
return nullptr;
5593+
case Intrinsic::maximum:
5594+
setValue(&I, DAG.getNode(ISD::FMAXNAN, sdl,
5595+
getValue(I.getArgOperand(0)).getValueType(),
5596+
getValue(I.getArgOperand(0)),
5597+
getValue(I.getArgOperand(1))));
5598+
return nullptr;
55875599
case Intrinsic::copysign:
55885600
setValue(&I, DAG.getNode(ISD::FCOPYSIGN, sdl,
55895601
getValue(I.getArgOperand(0)).getValueType(),

llvm/unittests/ADT/APFloatTest.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,36 @@ TEST(APFloatTest, MaxNum) {
555555
EXPECT_EQ(1.0, maxnum(nan, f1).convertToDouble());
556556
}
557557

558+
TEST(APFloatTest, Minimum) {
559+
APFloat f1(1.0);
560+
APFloat f2(2.0);
561+
APFloat zp(0.0);
562+
APFloat zn(-0.0);
563+
APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
564+
565+
EXPECT_EQ(1.0, minimum(f1, f2).convertToDouble());
566+
EXPECT_EQ(1.0, minimum(f2, f1).convertToDouble());
567+
EXPECT_EQ(-0.0, minimum(zp, zn).convertToDouble());
568+
EXPECT_EQ(-0.0, minimum(zn, zp).convertToDouble());
569+
EXPECT_TRUE(std::isnan(minimum(f1, nan).convertToDouble()));
570+
EXPECT_TRUE(std::isnan(minimum(nan, f1).convertToDouble()));
571+
}
572+
573+
TEST(APFloatTest, Maximum) {
574+
APFloat f1(1.0);
575+
APFloat f2(2.0);
576+
APFloat zp(0.0);
577+
APFloat zn(-0.0);
578+
APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
579+
580+
EXPECT_EQ(2.0, maximum(f1, f2).convertToDouble());
581+
EXPECT_EQ(2.0, maximum(f2, f1).convertToDouble());
582+
EXPECT_EQ(0.0, maximum(zp, zn).convertToDouble());
583+
EXPECT_EQ(0.0, maximum(zn, zp).convertToDouble());
584+
EXPECT_TRUE(std::isnan(maximum(f1, nan).convertToDouble()));
585+
EXPECT_TRUE(std::isnan(maximum(nan, f1).convertToDouble()));
586+
}
587+
558588
TEST(APFloatTest, Denormal) {
559589
APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;
560590

llvm/unittests/IR/IRBuilderTest.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ TEST_F(IRBuilderTest, Intrinsics) {
6868
II = cast<IntrinsicInst>(Call);
6969
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnum);
7070

71+
Call = Builder.CreateMinimum(V, V);
72+
II = cast<IntrinsicInst>(Call);
73+
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minimum);
74+
75+
Call = Builder.CreateMaximum(V, V);
76+
II = cast<IntrinsicInst>(Call);
77+
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maximum);
78+
7179
Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter, {}, {});
7280
II = cast<IntrinsicInst>(Call);
7381
EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);

0 commit comments

Comments
 (0)