Skip to content

Commit fb9e685

Browse files
authored
Intrinsic: introduce minimumnum and maximumnum for IR and SelectionDAG (#96649)
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 with fcanonical and fmax/fmin. Aarch64/PowerPC64 can use the same login as MIPSr6 and LoongArch, while they have no fcanonical support yet. I will add it in future patches. The FMIN/FMAX of RISC-V instructions follows the minimumNumber/maximumNumber of IEEE754-2019. We can just add it in future patch. Background https://discourse.llvm.org/t/rfc-fix-llvm-min-f-and-llvm-max-f-intrinsics/79735 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 07a8cba commit fb9e685

23 files changed

+1036
-7
lines changed

llvm/docs/LangRef.rst

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16131,6 +16131,96 @@ The returned value is completely identical to the input except for the sign bit;
1613116131
in particular, if the input is a NaN, then the quiet/signaling bit and payload
1613216132
are perfectly preserved.
1613316133

16134+
.. _i_fminmax_family:
16135+
16136+
'``llvm.min.*``' Intrinsics Comparation
16137+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16138+
16139+
Standard:
16140+
"""""""""
16141+
16142+
IEEE754 and ISO C define some min/max operations, and they have some differences
16143+
on working with qNaN/sNaN and +0.0/-0.0. Here is the list:
16144+
16145+
.. list-table::
16146+
:header-rows: 2
16147+
16148+
* - ``ISO C``
16149+
- fmin/fmax
16150+
- fmininum/fmaximum
16151+
- fminimum_num/fmaximum_num
16152+
16153+
* - ``IEEE754``
16154+
- minNum/maxNum (2008)
16155+
- minimum/maximum (2019)
16156+
- minimumNumber/maximumNumber (2019)
16157+
16158+
* - ``+0.0 vs -0.0``
16159+
- either one
16160+
- +0.0 > -0.0
16161+
- +0.0 > -0.0
16162+
16163+
* - ``NUM vs sNaN``
16164+
- qNaN, invalid exception
16165+
- qNaN, invalid exception
16166+
- NUM, invalid exception
16167+
16168+
* - ``qNaN vs sNaN``
16169+
- qNaN, invalid exception
16170+
- qNaN, invalid exception
16171+
- qNaN, invalid exception
16172+
16173+
* - ``NUM vs qNaN``
16174+
- NUM, no exception
16175+
- qNaN, no exception
16176+
- NUM, no exception
16177+
16178+
LLVM Implementation:
16179+
""""""""""""""""""""
16180+
16181+
LLVM implements all ISO C flavors as listed in this table, except in the
16182+
default floating-point environment exceptions are ignored. The constrained
16183+
versions of the intrinsics respect the exception behavior.
16184+
16185+
.. list-table::
16186+
:header-rows: 1
16187+
:widths: 16 28 28 28
16188+
16189+
* - Operation
16190+
- minnum/maxnum
16191+
- minimum/maximum
16192+
- minimumnum/maximumnum
16193+
16194+
* - ``NUM vs qNaN``
16195+
- NUM, no exception
16196+
- qNaN, no exception
16197+
- NUM, no exception
16198+
16199+
* - ``NUM vs sNaN``
16200+
- qNaN, invalid exception
16201+
- qNaN, invalid exception
16202+
- NUM, invalid exception
16203+
16204+
* - ``qNaN vs sNaN``
16205+
- qNaN, invalid exception
16206+
- qNaN, invalid exception
16207+
- qNaN, invalid exception
16208+
16209+
* - ``sNaN vs sNaN``
16210+
- qNaN, invalid exception
16211+
- qNaN, invalid exception
16212+
- qNaN, invalid exception
16213+
16214+
* - ``+0.0 vs -0.0``
16215+
- either one
16216+
- +0.0(max)/-0.0(min)
16217+
- +0.0(max)/-0.0(min)
16218+
16219+
* - ``NUM vs NUM``
16220+
- larger(max)/smaller(min)
16221+
- larger(max)/smaller(min)
16222+
- larger(max)/smaller(min)
16223+
1613416224
.. _i_minnum:
1613516225

1613616226
'``llvm.minnum.*``' Intrinsic
@@ -16312,6 +16402,98 @@ of the two arguments. -0.0 is considered to be less than +0.0 for this
1631216402
intrinsic. Note that these are the semantics specified in the draft of
1631316403
IEEE 754-2019.
1631416404

16405+
.. _i_minimumnum:
16406+
16407+
'``llvm.minimumnum.*``' Intrinsic
16408+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16409+
16410+
Syntax:
16411+
"""""""
16412+
16413+
This is an overloaded intrinsic. You can use ``llvm.minimumnum`` on any
16414+
floating-point or vector of floating-point type. Not all targets support
16415+
all types however.
16416+
16417+
::
16418+
16419+
declare float @llvm.minimumnum.f32(float %Val0, float %Val1)
16420+
declare double @llvm.minimumnum.f64(double %Val0, double %Val1)
16421+
declare x86_fp80 @llvm.minimumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
16422+
declare fp128 @llvm.minimumnum.f128(fp128 %Val0, fp128 %Val1)
16423+
declare ppc_fp128 @llvm.minimumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
16424+
16425+
Overview:
16426+
"""""""""
16427+
16428+
The '``llvm.minimumnum.*``' intrinsics return the minimum of the two
16429+
arguments, not propagating NaNs and treating -0.0 as less than +0.0.
16430+
16431+
16432+
Arguments:
16433+
""""""""""
16434+
16435+
The arguments and return value are floating-point numbers of the same
16436+
type.
16437+
16438+
Semantics:
16439+
""""""""""
16440+
If both operands are NaNs (including sNaN), returns qNaN. If one operand
16441+
is NaN (including sNaN) and another operand is a number, return the number.
16442+
Otherwise returns the lesser of the two arguments. -0.0 is considered to
16443+
be less than +0.0 for this intrinsic.
16444+
16445+
Note that these are the semantics of minimumNumber specified in IEEE 754-2019.
16446+
16447+
It has some differences with '``llvm.minnum.*``':
16448+
1)'``llvm.minnum.*``' will return qNaN if either operand is sNaN.
16449+
2)'``llvm.minnum*``' may return either one if we compare +0.0 vs -0.0.
16450+
16451+
.. _i_maximumnum:
16452+
16453+
'``llvm.maximumnum.*``' Intrinsic
16454+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16455+
16456+
Syntax:
16457+
"""""""
16458+
16459+
This is an overloaded intrinsic. You can use ``llvm.maximumnum`` on any
16460+
floating-point or vector of floating-point type. Not all targets support
16461+
all types however.
16462+
16463+
::
16464+
16465+
declare float @llvm.maximumnum.f32(float %Val0, float %Val1)
16466+
declare double @llvm.maximumnum.f64(double %Val0, double %Val1)
16467+
declare x86_fp80 @llvm.maximumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
16468+
declare fp128 @llvm.maximumnum.f128(fp128 %Val0, fp128 %Val1)
16469+
declare ppc_fp128 @llvm.maximumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
16470+
16471+
Overview:
16472+
"""""""""
16473+
16474+
The '``llvm.maximumnum.*``' intrinsics return the maximum of the two
16475+
arguments, not propagating NaNs and treating -0.0 as less than +0.0.
16476+
16477+
16478+
Arguments:
16479+
""""""""""
16480+
16481+
The arguments and return value are floating-point numbers of the same
16482+
type.
16483+
16484+
Semantics:
16485+
""""""""""
16486+
If both operands are NaNs (including sNaN), returns qNaN. If one operand
16487+
is NaN (including sNaN) and another operand is a number, return the number.
16488+
Otherwise returns the greater of the two arguments. -0.0 is considered to
16489+
be less than +0.0 for this intrinsic.
16490+
16491+
Note that these are the semantics of maximumNumber specified in IEEE 754-2019.
16492+
16493+
It has some differences with '``llvm.maxnum.*``':
16494+
1)'``llvm.maxnum.*``' will return qNaN if either operand is sNaN.
16495+
2)'``llvm.maxnum*``' may return either one if we compare +0.0 vs -0.0.
16496+
1631516497
.. _int_copysign:
1631616498

1631716499
'``llvm.copysign.*``' Intrinsic

llvm/include/llvm/Analysis/TargetLibraryInfo.def

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,39 @@ TLI_DEFINE_ENUM_INTERNAL(fminl)
13881388
TLI_DEFINE_STRING_INTERNAL("fminl")
13891389
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
13901390

1391+
// Calls to fmaximum_num and fminimum_num library functions expand to the llvm.maximumnum and
1392+
// llvm.minimumnum intrinsics with the correct parameter types for the arguments
1393+
// (all types must match).
1394+
/// double fmaximum_num(double x, double y);
1395+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_num)
1396+
TLI_DEFINE_STRING_INTERNAL("fmaximum_num")
1397+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1398+
1399+
/// float fmaximum_numf(float x, float y);
1400+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numf)
1401+
TLI_DEFINE_STRING_INTERNAL("fmaximum_numf")
1402+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1403+
1404+
/// long double fmaximum_numl(long double x, long double y);
1405+
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numl)
1406+
TLI_DEFINE_STRING_INTERNAL("fmaximum_numl")
1407+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1408+
1409+
/// double fminimum_num(double x, double y);
1410+
TLI_DEFINE_ENUM_INTERNAL(fminimum_num)
1411+
TLI_DEFINE_STRING_INTERNAL("fminimum_num")
1412+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1413+
1414+
/// float fminimum_numf(float x, float y);
1415+
TLI_DEFINE_ENUM_INTERNAL(fminimum_numf)
1416+
TLI_DEFINE_STRING_INTERNAL("fminimum_numf")
1417+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1418+
1419+
/// long double fminimum_numl(long double x, long double y);
1420+
TLI_DEFINE_ENUM_INTERNAL(fminimum_numl)
1421+
TLI_DEFINE_STRING_INTERNAL("fminimum_numl")
1422+
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)
1423+
13911424
/// double fmod(double x, double y);
13921425
TLI_DEFINE_ENUM_INTERNAL(fmod)
13931426
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
@@ -2037,6 +2037,12 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
20372037
case Intrinsic::maximum:
20382038
ISD = ISD::FMAXIMUM;
20392039
break;
2040+
case Intrinsic::minimumnum:
2041+
ISD = ISD::FMINIMUMNUM;
2042+
break;
2043+
case Intrinsic::maximumnum:
2044+
ISD = ISD::FMAXIMUMNUM;
2045+
break;
20402046
case Intrinsic::copysign:
20412047
ISD = ISD::FCOPYSIGN;
20422048
break;

llvm/include/llvm/CodeGen/ISDOpcodes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,11 @@ enum NodeType {
10471047
FMINIMUM,
10481048
FMAXIMUM,
10491049

1050+
/// FMINIMUMNUM/FMAXIMUMNUM - minimumnum/maximumnum that is same with
1051+
/// FMINNUM_IEEE and FMAXNUM_IEEE besides if either operand is sNaN.
1052+
FMINIMUMNUM,
1053+
FMAXIMUMNUM,
1054+
10501055
/// FSINCOS - Compute both fsin and fcos as a single operation.
10511056
FSINCOS,
10521057

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,6 +2908,8 @@ class TargetLoweringBase {
29082908
case ISD::FMAXNUM_IEEE:
29092909
case ISD::FMINIMUM:
29102910
case ISD::FMAXIMUM:
2911+
case ISD::FMINIMUMNUM:
2912+
case ISD::FMAXIMUMNUM:
29112913
case ISD::AVGFLOORS:
29122914
case ISD::AVGFLOORU:
29132915
case ISD::AVGCEILS:
@@ -5283,6 +5285,9 @@ class TargetLowering : public TargetLoweringBase {
52835285
/// Expand fminimum/fmaximum into multiple comparison with selects.
52845286
SDValue expandFMINIMUM_FMAXIMUM(SDNode *N, SelectionDAG &DAG) const;
52855287

5288+
/// Expand fminimumnum/fmaximumnum into multiple comparison with selects.
5289+
SDValue expandFMINIMUMNUM_FMAXIMUMNUM(SDNode *N, SelectionDAG &DAG) const;
5290+
52865291
/// Expand FP_TO_[US]INT_SAT into FP_TO_[US]INT and selects or min/max.
52875292
/// \param N Node to expand
52885293
/// \returns The expansion result

llvm/include/llvm/IR/IRBuilder.h

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

1018+
/// Create call to the minimumnum intrinsic.
1019+
Value *CreateMinimumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
1020+
return CreateBinaryIntrinsic(Intrinsic::minimumnum, LHS, RHS, nullptr,
1021+
Name);
1022+
}
1023+
1024+
/// Create call to the maximum intrinsic.
1025+
Value *CreateMaximumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
1026+
return CreateBinaryIntrinsic(Intrinsic::maximumnum, LHS, RHS, nullptr,
1027+
Name);
1028+
}
1029+
10181030
/// Create call to the copysign intrinsic.
10191031
Value *CreateCopySign(Value *LHS, Value *RHS,
10201032
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
@@ -1085,6 +1085,14 @@ def int_maximum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
10851085
[LLVMMatchType<0>, LLVMMatchType<0>],
10861086
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
10871087
>;
1088+
def int_minimumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
1089+
[LLVMMatchType<0>, LLVMMatchType<0>],
1090+
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
1091+
>;
1092+
def int_maximumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
1093+
[LLVMMatchType<0>, LLVMMatchType<0>],
1094+
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
1095+
>;
10881096

10891097
// Internal interface for object size checking
10901098
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
@@ -299,6 +299,16 @@ HANDLE_LIBCALL(FMAX_F64, "fmax")
299299
HANDLE_LIBCALL(FMAX_F80, "fmaxl")
300300
HANDLE_LIBCALL(FMAX_F128, "fmaxl")
301301
HANDLE_LIBCALL(FMAX_PPCF128, "fmaxl")
302+
HANDLE_LIBCALL(FMINIMUMNUM_F32, "fminimum_numf")
303+
HANDLE_LIBCALL(FMINIMUMNUM_F64, "fminimum_num")
304+
HANDLE_LIBCALL(FMINIMUMNUM_F80, "fminimum_numl")
305+
HANDLE_LIBCALL(FMINIMUMNUM_F128, "fminmum_numl")
306+
HANDLE_LIBCALL(FMINIMUMNUM_PPCF128, "fminimum_numl")
307+
HANDLE_LIBCALL(FMAXIMUMNUM_F32, "fmaximum_numf")
308+
HANDLE_LIBCALL(FMAXIMUMNUM_F64, "fmaximum_num")
309+
HANDLE_LIBCALL(FMAXIMUMNUM_F80, "fmaximum_numl")
310+
HANDLE_LIBCALL(FMAXIMUMNUM_F128, "fmaxmum_numl")
311+
HANDLE_LIBCALL(FMAXIMUMNUM_PPCF128, "fmaximum_numl")
302312
HANDLE_LIBCALL(LROUND_F32, "lroundf")
303313
HANDLE_LIBCALL(LROUND_F64, "lround")
304314
HANDLE_LIBCALL(LROUND_F80, "lroundl")

llvm/include/llvm/Target/TargetSelectionDAG.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,10 @@ def fminimum : SDNode<"ISD::FMINIMUM" , SDTFPBinOp,
517517
[SDNPCommutative, SDNPAssociative]>;
518518
def fmaximum : SDNode<"ISD::FMAXIMUM" , SDTFPBinOp,
519519
[SDNPCommutative, SDNPAssociative]>;
520+
def fminimumnum : SDNode<"ISD::FMINIMUMNUM" , SDTFPBinOp,
521+
[SDNPCommutative, SDNPAssociative]>;
522+
def fmaximumnum : SDNode<"ISD::FMAXIMUMNUM" , SDTFPBinOp,
523+
[SDNPCommutative, SDNPAssociative]>;
520524
def fgetsign : SDNode<"ISD::FGETSIGN" , SDTFPToIntOp>;
521525
def fcanonicalize : SDNode<"ISD::FCANONICALIZE", SDTFPUnaryOp>;
522526
def fneg : SDNode<"ISD::FNEG" , SDTFPUnaryOp>;

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,9 @@ SDValue DAGCombiner::visit(SDNode *N) {
19391939
case ISD::FMINNUM:
19401940
case ISD::FMAXNUM:
19411941
case ISD::FMINIMUM:
1942-
case ISD::FMAXIMUM: return visitFMinMax(N);
1942+
case ISD::FMAXIMUM:
1943+
case ISD::FMINIMUMNUM:
1944+
case ISD::FMAXIMUMNUM: return visitFMinMax(N);
19431945
case ISD::FCEIL: return visitFCEIL(N);
19441946
case ISD::FTRUNC: return visitFTRUNC(N);
19451947
case ISD::FFREXP: return visitFFREXP(N);
@@ -6068,6 +6070,7 @@ static bool arebothOperandsNotNan(SDValue Operand1, SDValue Operand2,
60686070
return DAG.isKnownNeverNaN(Operand2) && DAG.isKnownNeverNaN(Operand1);
60696071
}
60706072

6073+
// FIXME: use FMINIMUMNUM if possible, such as for RISC-V.
60716074
static unsigned getMinMaxOpcodeForFP(SDValue Operand1, SDValue Operand2,
60726075
ISD::CondCode CC, unsigned OrAndOpcode,
60736076
SelectionDAG &DAG,

0 commit comments

Comments
 (0)