Skip to content

Intrinsic: introduce minimumnum and maximumnum for IR and SelectionDAG #96649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16131,6 +16131,96 @@ The returned value is completely identical to the input except for the sign bit;
in particular, if the input is a NaN, then the quiet/signaling bit and payload
are perfectly preserved.

.. _i_fminmax_family:

'``llvm.min.*``' Intrinsics Comparation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Standard:
"""""""""

IEEE754 and ISO C define some min/max operations, and they have some differences
on working with qNaN/sNaN and +0.0/-0.0. Here is the list:

.. list-table::
:header-rows: 2

* - ``ISO C``
- fmin/fmax
- fmininum/fmaximum
- fminimum_num/fmaximum_num

* - ``IEEE754``
- minNum/maxNum (2008)
- minimum/maximum (2019)
- minimumNumber/maximumNumber (2019)

* - ``+0.0 vs -0.0``
- either one
- +0.0 > -0.0
- +0.0 > -0.0

* - ``NUM vs sNaN``
- qNaN, invalid exception
- qNaN, invalid exception
- NUM, invalid exception

* - ``qNaN vs sNaN``
- qNaN, invalid exception
- qNaN, invalid exception
- qNaN, invalid exception

* - ``NUM vs qNaN``
- NUM, no exception
- qNaN, no exception
- NUM, no exception

LLVM Implementation:
""""""""""""""""""""

LLVM implements all ISO C flavors as listed in this table, except in the
default floating-point environment exceptions are ignored. The constrained
versions of the intrinsics respect the exception behavior.

.. list-table::
:header-rows: 1
:widths: 16 28 28 28

* - Operation
- minnum/maxnum
- minimum/maximum
- minimumnum/maximumnum

* - ``NUM vs qNaN``
- NUM, no exception
- qNaN, no exception
- NUM, no exception

* - ``NUM vs sNaN``
- qNaN, invalid exception
- qNaN, invalid exception
- NUM, invalid exception

* - ``qNaN vs sNaN``
- qNaN, invalid exception
- qNaN, invalid exception
- qNaN, invalid exception

* - ``sNaN vs sNaN``
- qNaN, invalid exception
- qNaN, invalid exception
- qNaN, invalid exception

* - ``+0.0 vs -0.0``
- either one
- +0.0(max)/-0.0(min)
- +0.0(max)/-0.0(min)

* - ``NUM vs NUM``
- larger(max)/smaller(min)
- larger(max)/smaller(min)
- larger(max)/smaller(min)

.. _i_minnum:

'``llvm.minnum.*``' Intrinsic
Expand Down Expand Up @@ -16312,6 +16402,98 @@ 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-2019.

.. _i_minimumnum:

'``llvm.minimumnum.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

This is an overloaded intrinsic. You can use ``llvm.minimumnum`` on any
floating-point or vector of floating-point type. Not all targets support
all types however.

::

declare float @llvm.minimumnum.f32(float %Val0, float %Val1)
declare double @llvm.minimumnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.minimumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.minimumnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.minimumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)

Overview:
"""""""""

The '``llvm.minimumnum.*``' intrinsics return the minimum of the two
arguments, not 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 both operands are NaNs (including sNaN), returns qNaN. If one operand
is NaN (including sNaN) and another operand is a number, return the number.
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 of minimumNumber specified in IEEE 754-2019.

It has some differences with '``llvm.minnum.*``':
1)'``llvm.minnum.*``' will return qNaN if either operand is sNaN.
2)'``llvm.minnum*``' may return either one if we compare +0.0 vs -0.0.

.. _i_maximumnum:

'``llvm.maximumnum.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

This is an overloaded intrinsic. You can use ``llvm.maximumnum`` on any
floating-point or vector of floating-point type. Not all targets support
all types however.

::

declare float @llvm.maximumnum.f32(float %Val0, float %Val1)
declare double @llvm.maximumnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.maximumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.maximumnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.maximumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)

Overview:
"""""""""

The '``llvm.maximumnum.*``' intrinsics return the maximum of the two
arguments, not 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 both operands are NaNs (including sNaN), returns qNaN. If one operand
is NaN (including sNaN) and another operand is a number, return the number.
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 of maximumNumber specified in IEEE 754-2019.

It has some differences with '``llvm.maxnum.*``':
1)'``llvm.maxnum.*``' will return qNaN if either operand is sNaN.
2)'``llvm.maxnum*``' may return either one if we compare +0.0 vs -0.0.

.. _int_copysign:

'``llvm.copysign.*``' Intrinsic
Expand Down
33 changes: 33 additions & 0 deletions llvm/include/llvm/Analysis/TargetLibraryInfo.def
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,39 @@ TLI_DEFINE_ENUM_INTERNAL(fminl)
TLI_DEFINE_STRING_INTERNAL("fminl")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

// Calls to fmaximum_num and fminimum_num library functions expand to the llvm.maximumnum and
// llvm.minimumnum intrinsics with the correct parameter types for the arguments
// (all types must match).
/// double fmaximum_num(double x, double y);
TLI_DEFINE_ENUM_INTERNAL(fmaximum_num)
TLI_DEFINE_STRING_INTERNAL("fmaximum_num")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// float fmaximum_numf(float x, float y);
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numf)
TLI_DEFINE_STRING_INTERNAL("fmaximum_numf")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// long double fmaximum_numl(long double x, long double y);
TLI_DEFINE_ENUM_INTERNAL(fmaximum_numl)
TLI_DEFINE_STRING_INTERNAL("fmaximum_numl")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// double fminimum_num(double x, double y);
TLI_DEFINE_ENUM_INTERNAL(fminimum_num)
TLI_DEFINE_STRING_INTERNAL("fminimum_num")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// float fminimum_numf(float x, float y);
TLI_DEFINE_ENUM_INTERNAL(fminimum_numf)
TLI_DEFINE_STRING_INTERNAL("fminimum_numf")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// long double fminimum_numl(long double x, long double y);
TLI_DEFINE_ENUM_INTERNAL(fminimum_numl)
TLI_DEFINE_STRING_INTERNAL("fminimum_numl")
TLI_DEFINE_SIG_INTERNAL(Floating, Same, Same)

/// double fmod(double x, double y);
TLI_DEFINE_ENUM_INTERNAL(fmod)
TLI_DEFINE_STRING_INTERNAL("fmod")
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/CodeGen/BasicTTIImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,12 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
case Intrinsic::maximum:
ISD = ISD::FMAXIMUM;
break;
case Intrinsic::minimumnum:
ISD = ISD::FMINIMUMNUM;
break;
case Intrinsic::maximumnum:
ISD = ISD::FMAXIMUMNUM;
break;
case Intrinsic::copysign:
ISD = ISD::FCOPYSIGN;
break;
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/ISDOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,11 @@ enum NodeType {
FMINIMUM,
FMAXIMUM,

/// FMINIMUMNUM/FMAXIMUMNUM - minimumnum/maximumnum that is same with
/// FMINNUM_IEEE and FMAXNUM_IEEE besides if either operand is sNaN.
FMINIMUMNUM,
FMAXIMUMNUM,

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

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -2908,6 +2908,8 @@ class TargetLoweringBase {
case ISD::FMAXNUM_IEEE:
case ISD::FMINIMUM:
case ISD::FMAXIMUM:
case ISD::FMINIMUMNUM:
case ISD::FMAXIMUMNUM:
case ISD::AVGFLOORS:
case ISD::AVGFLOORU:
case ISD::AVGCEILS:
Expand Down Expand Up @@ -5275,6 +5277,9 @@ class TargetLowering : public TargetLoweringBase {
/// Expand fminimum/fmaximum into multiple comparison with selects.
SDValue expandFMINIMUM_FMAXIMUM(SDNode *N, SelectionDAG &DAG) const;

/// Expand fminimumnum/fmaximumnum into multiple comparison with selects.
SDValue expandFMINIMUMNUM_FMAXIMUMNUM(SDNode *N, SelectionDAG &DAG) const;

/// Expand FP_TO_[US]INT_SAT into FP_TO_[US]INT and selects or min/max.
/// \param N Node to expand
/// \returns The expansion result
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/IR/IRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,18 @@ class IRBuilderBase {
return CreateBinaryIntrinsic(Intrinsic::maximum, LHS, RHS, nullptr, Name);
}

/// Create call to the minimumnum intrinsic.
Value *CreateMinimumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
return CreateBinaryIntrinsic(Intrinsic::minimumnum, LHS, RHS, nullptr,
Name);
}

/// Create call to the maximum intrinsic.
Value *CreateMaximumNum(Value *LHS, Value *RHS, const Twine &Name = "") {
return CreateBinaryIntrinsic(Intrinsic::maximumnum, LHS, RHS, nullptr,
Name);
}

/// Create call to the copysign intrinsic.
Value *CreateCopySign(Value *LHS, Value *RHS,
Instruction *FMFSource = nullptr,
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/IR/IntrinsicInst.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class IntrinsicInst : public CallInst {
case Intrinsic::minnum:
case Intrinsic::maximum:
case Intrinsic::minimum:
case Intrinsic::maximumnum:
case Intrinsic::minimumnum:
case Intrinsic::smax:
case Intrinsic::smin:
case Intrinsic::umax:
Expand Down
8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,14 @@ def int_maximum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
>;
def int_minimumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
>;
def int_maximumnum : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]
>;

// Internal interface for object size checking
def int_objectsize : DefaultAttrsIntrinsic<[llvm_anyint_ty],
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/IR/RuntimeLibcalls.def
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@ HANDLE_LIBCALL(FMAX_F64, "fmax")
HANDLE_LIBCALL(FMAX_F80, "fmaxl")
HANDLE_LIBCALL(FMAX_F128, "fmaxl")
HANDLE_LIBCALL(FMAX_PPCF128, "fmaxl")
HANDLE_LIBCALL(FMINIMUMNUM_F32, "fminimum_numf")
HANDLE_LIBCALL(FMINIMUMNUM_F64, "fminimum_num")
HANDLE_LIBCALL(FMINIMUMNUM_F80, "fminimum_numl")
HANDLE_LIBCALL(FMINIMUMNUM_F128, "fminmum_numl")
HANDLE_LIBCALL(FMINIMUMNUM_PPCF128, "fminimum_numl")
HANDLE_LIBCALL(FMAXIMUMNUM_F32, "fmaximum_numf")
HANDLE_LIBCALL(FMAXIMUMNUM_F64, "fmaximum_num")
HANDLE_LIBCALL(FMAXIMUMNUM_F80, "fmaximum_numl")
HANDLE_LIBCALL(FMAXIMUMNUM_F128, "fmaxmum_numl")
HANDLE_LIBCALL(FMAXIMUMNUM_PPCF128, "fmaximum_numl")
HANDLE_LIBCALL(LROUND_F32, "lroundf")
HANDLE_LIBCALL(LROUND_F64, "lround")
HANDLE_LIBCALL(LROUND_F80, "lroundl")
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Target/TargetSelectionDAG.td
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ def fminimum : SDNode<"ISD::FMINIMUM" , SDTFPBinOp,
[SDNPCommutative, SDNPAssociative]>;
def fmaximum : SDNode<"ISD::FMAXIMUM" , SDTFPBinOp,
[SDNPCommutative, SDNPAssociative]>;
def fminimumnum : SDNode<"ISD::FMINIMUMNUM" , SDTFPBinOp,
[SDNPCommutative, SDNPAssociative]>;
def fmaximumnum : SDNode<"ISD::FMAXIMUMNUM" , SDTFPBinOp,
[SDNPCommutative, SDNPAssociative]>;
def fgetsign : SDNode<"ISD::FGETSIGN" , SDTFPToIntOp>;
def fcanonicalize : SDNode<"ISD::FCANONICALIZE", SDTFPUnaryOp>;
def fneg : SDNode<"ISD::FNEG" , SDTFPUnaryOp>;
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,9 @@ SDValue DAGCombiner::visit(SDNode *N) {
case ISD::FMINNUM:
case ISD::FMAXNUM:
case ISD::FMINIMUM:
case ISD::FMAXIMUM: return visitFMinMax(N);
case ISD::FMAXIMUM:
case ISD::FMINIMUMNUM:
case ISD::FMAXIMUMNUM: return visitFMinMax(N);
case ISD::FCEIL: return visitFCEIL(N);
case ISD::FTRUNC: return visitFTRUNC(N);
case ISD::FFREXP: return visitFFREXP(N);
Expand Down Expand Up @@ -6068,6 +6070,7 @@ static bool arebothOperandsNotNan(SDValue Operand1, SDValue Operand2,
return DAG.isKnownNeverNaN(Operand2) && DAG.isKnownNeverNaN(Operand1);
}

// FIXME: use FMINIMUMNUM if possible, such as for RISC-V.
static unsigned getMinMaxOpcodeForFP(SDValue Operand1, SDValue Operand2,
ISD::CondCode CC, unsigned OrAndOpcode,
SelectionDAG &DAG,
Expand Down
Loading