Skip to content

Commit b8a9c21

Browse files
committed
Intrinsic: introduce minimumnum and maximumnum for IR and SelectionDAG
C23 introduced new functions fminimum_num and fmaximum_num, and they follow the minimumNumber and maximumNumber of IEEE754-2019. Let's introduce new intrinsics to support them. This patch introduces support only support for scalar values. The support of vector (vp, vp.reduce, vector.reduce), experimental.constrained will be added in future patches. With this patch, MIPSr6 and LoongArch can work out of box. Other architectures will fallback to libc call. The support of them will be added in future patches. Background Currently we have fminnum/fmaxnum, which have different behavior on different platform for NUM vs sNaN: 1) Fallback to fmin(3)/fmax(3): return qNaN. 2) ARM64/ARM32+Neon: same as libc. 3) MIPSr6/LoongArch/RISC-V: return NUM. And the fix of fminnum/fmaxnum to follow minNUM/maxNUM of IEEE754-2008 will submit as separated patches.
1 parent eb76bc3 commit b8a9c21

25 files changed

+811
-7
lines changed

llvm/docs/LangRef.rst

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16101,6 +16101,100 @@ The returned value is completely identical to the input except for the sign bit;
1610116101
in particular, if the input is a NaN, then the quiet/signaling bit and payload
1610216102
are perfectly preserved.
1610316103

16104+
.. _i_fminmax_family:
16105+
16106+
'``llvm.min.*``' Intrinsics Comparation
16107+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16108+
16109+
Standard:
16110+
"""""""""
16111+
16112+
IEEE754 and ISO C define some min/max operations, and they have some differences
16113+
on working with qNaN/sNaN and +0.0/-0.0. Here is the list:
16114+
16115+
.. list-table::
16116+
:header-rows: 2
16117+
16118+
* - ``ISO C``
16119+
- fmin/fmax
16120+
- fmininum/fmaximum
16121+
- fminimum_num/fmaximum_num
16122+
16123+
* - ``IEEE754``
16124+
- minNum/maxNum (2008)
16125+
- minimum/maximum (2019)
16126+
- minimumNumber/maximumNumber (2019)
16127+
16128+
* - ``+0.0 vs -0.0``
16129+
- either one
16130+
- +0.0 > -0.0
16131+
- +0.0 > -0.0
16132+
16133+
* - ``NUM vs sNaN``
16134+
- qNaN, invalid exception
16135+
- qNaN, invalid exception
16136+
- NUM, invalid exception
16137+
16138+
* - ``qNaN vs sNaN``
16139+
- qNaN, invalid exception
16140+
- qNaN, invalid exception
16141+
- qNaN, invalid exception
16142+
16143+
* - ``NUM vs qNaN``
16144+
- NUM, no exception
16145+
- qNaN, no exception
16146+
- NUM, no exception
16147+
16148+
LLVM Implementation:
16149+
""""""""""""""""""""
16150+
16151+
LLVM implements all ISO C flavors as listed in this table.
16152+
Only basic intrinsics list here. The constrained version
16153+
ones may have different behaivor on exception.
16154+
16155+
Since some architectures implement minNum/maxNum with +0.0>-0.0,
16156+
so we define internal ISD::MINNUM_IEEE and ISD::MAXNUM_IEEE.
16157+
They will be helpful to implement minimumnum/maximumnum.
16158+
16159+
.. list-table::
16160+
:header-rows: 1
16161+
:widths: 16 28 28 28
16162+
16163+
* - Operation
16164+
- minnum/maxnum
16165+
- minimum/maximum
16166+
- minimumnum/maximumnum
16167+
16168+
* - ``NUM vs qNaN``
16169+
- NUM, no exception
16170+
- qNaN, no exception
16171+
- NUM, no exception
16172+
16173+
* - ``NUM vs sNaN``
16174+
- qNaN, invalid exception
16175+
- qNaN, invalid exception
16176+
- NUM, invalid exception
16177+
16178+
* - ``qNaN vs sNaN``
16179+
- qNaN, invalid exception
16180+
- qNaN, invalid exception
16181+
- qNaN, invalid exception
16182+
16183+
* - ``sNaN vs sNaN``
16184+
- qNaN, invalid exception
16185+
- qNaN, invalid exception
16186+
- qNaN, invalid exception
16187+
16188+
* - ``+0.0 vs -0.0``
16189+
- either one
16190+
- +0.0(max)/-0.0(min)
16191+
- +0.0(max)/-0.0(min)
16192+
16193+
* - ``NUM vs NUM``
16194+
- larger(max)/smaller(min)
16195+
- larger(max)/smaller(min)
16196+
- larger(max)/smaller(min)
16197+
1610416198
.. _i_minnum:
1610516199

1610616200
'``llvm.minnum.*``' Intrinsic
@@ -16282,6 +16376,98 @@ of the two arguments. -0.0 is considered to be less than +0.0 for this
1628216376
intrinsic. Note that these are the semantics specified in the draft of
1628316377
IEEE 754-2019.
1628416378

16379+
.. _i_minimumnum:
16380+
16381+
'``llvm.minimumnum.*``' Intrinsic
16382+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16383+
16384+
Syntax:
16385+
"""""""
16386+
16387+
This is an overloaded intrinsic. You can use ``llvm.minimumnum`` on any
16388+
floating-point or vector of floating-point type. Not all targets support
16389+
all types however.
16390+
16391+
::
16392+
16393+
declare float @llvm.minimumnum.f32(float %Val0, float %Val1)
16394+
declare double @llvm.minimumnum.f64(double %Val0, double %Val1)
16395+
declare x86_fp80 @llvm.minimumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
16396+
declare fp128 @llvm.minimumnum.f128(fp128 %Val0, fp128 %Val1)
16397+
declare ppc_fp128 @llvm.minimumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
16398+
16399+
Overview:
16400+
"""""""""
16401+
16402+
The '``llvm.minimumnum.*``' intrinsics return the minimum of the two
16403+
arguments, not propagating NaNs and treating -0.0 as less than +0.0.
16404+
16405+
16406+
Arguments:
16407+
""""""""""
16408+
16409+
The arguments and return value are floating-point numbers of the same
16410+
type.
16411+
16412+
Semantics:
16413+
""""""""""
16414+
If both operands are NaNs (including sNaN), returns qNaN. If one operand
16415+
is NaN (including sNaN) and another operand is a number, return the number.
16416+
Otherwise returns the lesser of the two arguments. -0.0 is considered to
16417+
be less than +0.0 for this intrinsic.
16418+
16419+
Note that these are the semantics of minimumNumber specified in IEEE 754-2019.
16420+
16421+
It has some different with '``llvm.minnum.*``':
16422+
1)'``llvm.minnum.*``' will return qNaN if either operand is sNaN.
16423+
2)'``llvm.minnum*``' may return either one if we compare +0.0 vs -0.0.
16424+
16425+
.. _i_maximumnum:
16426+
16427+
'``llvm.maximumnum.*``' Intrinsic
16428+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16429+
16430+
Syntax:
16431+
"""""""
16432+
16433+
This is an overloaded intrinsic. You can use ``llvm.maximumnum`` on any
16434+
floating-point or vector of floating-point type. Not all targets support
16435+
all types however.
16436+
16437+
::
16438+
16439+
declare float @llvm.maximumnum.f32(float %Val0, float %Val1)
16440+
declare double @llvm.maximumnum.f64(double %Val0, double %Val1)
16441+
declare x86_fp80 @llvm.maximumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
16442+
declare fp128 @llvm.maximumnum.f128(fp128 %Val0, fp128 %Val1)
16443+
declare ppc_fp128 @llvm.maximumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
16444+
16445+
Overview:
16446+
"""""""""
16447+
16448+
The '``llvm.maximumnum.*``' intrinsics return the maximum of the two
16449+
arguments, not propagating NaNs and treating -0.0 as less than +0.0.
16450+
16451+
16452+
Arguments:
16453+
""""""""""
16454+
16455+
The arguments and return value are floating-point numbers of the same
16456+
type.
16457+
16458+
Semantics:
16459+
""""""""""
16460+
If both operands are NaNs (including sNaN), returns qNaN. If one operand
16461+
is NaN (including sNaN) and another operand is a number, return the number.
16462+
Otherwise returns the greater of the two arguments. -0.0 is considered to
16463+
be less than +0.0 for this intrinsic.
16464+
16465+
Note that these are the semantics of maximumNumber specified in IEEE 754-2019.
16466+
16467+
It has some different with '``llvm.maxnum.*``':
16468+
1)'``llvm.maxnum.*``' will return qNaN if either operand is sNaN.
16469+
2)'``llvm.maxnum*``' may return either one if we compare +0.0 vs -0.0.
16470+
1628516471
.. _int_copysign:
1628616472

1628716473
'``llvm.copysign.*``' Intrinsic

llvm/include/llvm/ADT/APFloat.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,19 @@ inline APFloat minimum(const APFloat &A, const APFloat &B) {
14831483
return B < A ? B : A;
14841484
}
14851485

1486+
/// Implements IEEE 754-2019 minimumNumber semantics. Returns the smaller
1487+
/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
1488+
LLVM_READONLY
1489+
inline APFloat minimumnum(const APFloat &A, const APFloat &B) {
1490+
if (A.isNaN())
1491+
return B.isNaN() ? B.makeQuiet() : B;
1492+
if (B.isNaN())
1493+
return A;
1494+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1495+
return A.isNegative() ? A : B;
1496+
return B < A ? B : A;
1497+
}
1498+
14861499
/// Implements IEEE 754-2019 maximum semantics. Returns the larger of 2
14871500
/// arguments, propagating NaNs and treating -0 as less than +0.
14881501
LLVM_READONLY
@@ -1496,6 +1509,19 @@ inline APFloat maximum(const APFloat &A, const APFloat &B) {
14961509
return A < B ? B : A;
14971510
}
14981511

1512+
/// Implements IEEE 754-2019 maximumNumber semantics. Returns the larger
1513+
/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
1514+
LLVM_READONLY
1515+
inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
1516+
if (A.isNaN())
1517+
return B.isNaN() ? B.makeQuiet() : B;
1518+
if (B.isNaN())
1519+
return A;
1520+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1521+
return A.isNegative() ? B : A;
1522+
return A < B ? B : A;
1523+
}
1524+
14991525
// We want the following functions to be available in the header for inlining.
15001526
// We cannot define them inline in the class definition of `DoubleAPFloat`
15011527
// because doing so would instantiate `std::unique_ptr<APFloat[]>` before

llvm/include/llvm/Analysis/TargetLibraryInfo.def

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,39 @@ TLI_DEFINE_ENUM_INTERNAL(fminl)
13471347
TLI_DEFINE_STRING_INTERNAL("fminl")
13481348
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
13491349

1350+
// Calls to fmaximum_num and fminimum_num library functions expand to the llvm.maximumnum and
1351+
// llvm.minimumnum intrinsics with the correct parameter types for the arguments
1352+
// (all types must match).
1353+
/// double fmaximum_num(double x, double y);
1354+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_num)
1355+
TLI_DEFINE_STRING_INTERNAL("fmaximum_num")
1356+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1357+
1358+
/// float fmaximum_numf(float x, float y);
1359+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numf)
1360+
TLI_DEFINE_STRING_INTERNAL("fmaximum_numf")
1361+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1362+
1363+
/// long double fmaximum_numl(long double x, long double y);
1364+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numl)
1365+
TLI_DEFINE_STRING_INTERNAL("fmaximum_numl")
1366+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1367+
1368+
/// double fminimum_num(double x, double y);
1369+
TLI_DEFINE_ENUM_INTERNAL(fminimum_num)
1370+
TLI_DEFINE_STRING_INTERNAL("fminimum_num")
1371+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1372+
1373+
/// float fminimum_numf(float x, float y);
1374+
TLI_DEFINE_ENUM_INTERNAL(fminimum_numf)
1375+
TLI_DEFINE_STRING_INTERNAL("fminimum_numf")
1376+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1377+
1378+
/// long double fminimum_numl(long double x, long double y);
1379+
TLI_DEFINE_ENUM_INTERNAL(fminimum_numl)
1380+
TLI_DEFINE_STRING_INTERNAL("fminimum_numl")
1381+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1382+
13501383
/// double fmod(double x, double y);
13511384
TLI_DEFINE_ENUM_INTERNAL(fmod)
13521385
TLI_DEFINE_STRING_INTERNAL("fmod")

llvm/include/llvm/CodeGen/BasicTTIImpl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,6 +2016,12 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
20162016
case Intrinsic::maximum:
20172017
ISD = ISD::FMAXIMUM;
20182018
break;
2019+
case Intrinsic::minimumnum:
2020+
ISD = ISD::FMINIMUMNUM;
2021+
break;
2022+
case Intrinsic::maximumnum:
2023+
ISD = ISD::FMAXIMUMNUM;
2024+
break;
20192025
case Intrinsic::copysign:
20202026
ISD = ISD::FCOPYSIGN;
20212027
break;

llvm/include/llvm/CodeGen/ISDOpcodes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,11 @@ enum NodeType {
999999
FMINIMUM,
10001000
FMAXIMUM,
10011001

1002+
/// FMINIMUMNUM/FMAXIMUMNUM - minimumnum/maximumnum that is same with
1003+
/// FMINNUM_IEEE and FMAXNUM_IEEE besides if either operand is sNaN.
1004+
FMINIMUMNUM,
1005+
FMAXIMUMNUM,
1006+
10021007
/// FSINCOS - Compute both fsin and fcos as a single operation.
10031008
FSINCOS,
10041009

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2910,6 +2910,8 @@ class TargetLoweringBase {
29102910
case ISD::FMAXNUM_IEEE:
29112911
case ISD::FMINIMUM:
29122912
case ISD::FMAXIMUM:
2913+
case ISD::FMINIMUMNUM:
2914+
case ISD::FMAXIMUMNUM:
29132915
case ISD::AVGFLOORS:
29142916
case ISD::AVGFLOORU:
29152917
case ISD::AVGCEILS:
@@ -5130,6 +5132,8 @@ class TargetLowering : public TargetLoweringBase {
51305132
/// through to the default expansion/soften to libcall, we might introduce a
51315133
/// link-time dependency on libm into a file that originally did not have one.
51325134
SDValue createSelectForFMINNUM_FMAXNUM(SDNode *Node, SelectionDAG &DAG) const;
5135+
SDValue createSelectForFMINIMUMNUM_FMAXIMUMNUM(SDNode *Node,
5136+
SelectionDAG &DAG) const;
51335137

51345138
/// Return a reciprocal estimate value for the input operand.
51355139
/// \p Enabled is a ReciprocalEstimate enum with value either 'Unspecified' or
@@ -5261,6 +5265,9 @@ class TargetLowering : public TargetLoweringBase {
52615265
/// Expand fminimum/fmaximum into multiple comparison with selects.
52625266
SDValue expandFMINIMUM_FMAXIMUM(SDNode *N, SelectionDAG &DAG) const;
52635267

5268+
/// Expand fminimumnum/fmaximumnum into multiple comparison with selects.
5269+
SDValue expandFMINIMUMNUM_FMAXIMUMNUM(SDNode *N, SelectionDAG &DAG) const;
5270+
52645271
/// Expand FP_TO_[US]INT_SAT into FP_TO_[US]INT and selects or min/max.
52655272
/// \param N Node to expand
52665273
/// \returns The expansion result

llvm/include/llvm/IR/IRBuilder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,18 @@ class IRBuilderBase {
10161016
return CreateBinaryIntrinsic(Intrinsic::maximum, LHS, RHS, nullptr, Name);
10171017
}
10181018

1019+
/// Create call to the minimumnum intrinsic.
1020+
Value *CreateMinimumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
1021+
return CreateBinaryIntrinsic(Intrinsic::minimumnum, LHS, RHS, nullptr,
1022+
Name);
1023+
}
1024+
1025+
/// Create call to the maximum intrinsic.
1026+
Value *CreateMaximumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
1027+
return CreateBinaryIntrinsic(Intrinsic::maximumnum, LHS, RHS, nullptr,
1028+
Name);
1029+
}
1030+
10191031
/// Create call to the copysign intrinsic.
10201032
Value *CreateCopySign(Value *LHS, Value *RHS,
10211033
Instruction *FMFSource = nullptr,

llvm/include/llvm/IR/IntrinsicInst.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class IntrinsicInst : public CallInst {
7676
case Intrinsic::minnum:
7777
case Intrinsic::maximum:
7878
case Intrinsic::minimum:
79+
case Intrinsic::maximumnum:
80+
case Intrinsic::minimumnum:
7981
case Intrinsic::smax:
8082
case Intrinsic::smin:
8183
case Intrinsic::umax:

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,14 @@ def int_maximum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
10861086
[LLVMMatchType<0>, LLVMMatchType<0>],
10871087
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
10881088
>;
1089+
def int_minimumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
1090+
[LLVMMatchType<0>, LLVMMatchType<0>],
1091+
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
1092+
>;
1093+
def int_maximumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
1094+
[LLVMMatchType<0>, LLVMMatchType<0>],
1095+
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
1096+
>;
10891097

10901098
// Internal interface for object size checking
10911099
def int_objectsize : DefaultAttrsIntrinsic<[llvm_anyint_ty],

llvm/include/llvm/IR/RuntimeLibcalls.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,16 @@ HANDLE_LIBCALL(FMAX_F64, "fmax")
269269
HANDLE_LIBCALL(FMAX_F80, "fmaxl")
270270
HANDLE_LIBCALL(FMAX_F128, "fmaxl")
271271
HANDLE_LIBCALL(FMAX_PPCF128, "fmaxl")
272+
HANDLE_LIBCALL(FMINIMUMNUM_F32, "fminimum_numf")
273+
HANDLE_LIBCALL(FMINIMUMNUM_F64, "fminimum_num")
274+
HANDLE_LIBCALL(FMINIMUMNUM_F80, "fminimum_numl")
275+
HANDLE_LIBCALL(FMINIMUMNUM_F128, "fminmum_numl")
276+
HANDLE_LIBCALL(FMINIMUMNUM_PPCF128, "fminimum_numl")
277+
HANDLE_LIBCALL(FMAXIMUMNUM_F32, "fmaximum_numf")
278+
HANDLE_LIBCALL(FMAXIMUMNUM_F64, "fmaximum_num")
279+
HANDLE_LIBCALL(FMAXIMUMNUM_F80, "fmaximum_numl")
280+
HANDLE_LIBCALL(FMAXIMUMNUM_F128, "fmaxmum_numl")
281+
HANDLE_LIBCALL(FMAXIMUMNUM_PPCF128, "fmaximum_numl")
272282
HANDLE_LIBCALL(LROUND_F32, "lroundf")
273283
HANDLE_LIBCALL(LROUND_F64, "lround")
274284
HANDLE_LIBCALL(LROUND_F80, "lroundl")

0 commit comments

Comments
 (0)