Skip to content
Open
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
138 changes: 138 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21406,6 +21406,144 @@ environment <floatenv>` *except* for the rounding mode.
This intrinsic is not supported on all targets. Some targets may not support
all rounding modes.

'``llvm.convert.to.arbitrary.fp``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare <iNxM> @llvm.convert.to.arbitrary.fp.<iNxM>.<fNxM>(
<fNxM> <value>, metadata <interpretation>,
metadata <rounding mode>, i1 <saturation>)

Overview:
"""""""""

The ``llvm.convert.to.arbitrary.fp`` intrinsic converts a native LLVM
floating-point value to an arbitrary FP format, returning the result as an integer
containing the arbitrary FP bits. This intrinsic is overloaded on both its return
type and first argument.

Arguments:
""""""""""

``value``
The native LLVM floating-point value to convert (e.g., ``half``, ``float``, ``double``).

``interpretation``
A metadata string describing the target arbitrary FP format. Supported format names include:

- FP8 formats: ``"Float8E5M2"``, ``"Float8E5M2FNUZ"``, ``"Float8E4M3"``,
``"Float8E4M3FN"``, ``"Float8E4M3FNUZ"``, ``"Float8E4M3B11FNUZ"``, ``"Float8E3M4"``,
``"Float8E8M0FNU"``
- FP6 formats: ``"Float6E3M2FN"``, ``"Float6E2M3FN"``
- FP4 formats: ``"Float4E2M1FN"``

``rounding mode``
A metadata string specifying the rounding mode. The permitted strings match those
accepted by :ref:`llvm.fptrunc.round <int_fptrunc_round>` (for example,
``"round.tonearest"`` or ``"round.towardzero"``).

``saturation``
A compile-time constant boolean value (``i1``). When ``true``, values outside the
representable range of the target format are clamped to the minimum or maximum normal value.
When ``false``, no saturation is applied. This parameter must be an immediate constant.

Semantics:
""""""""""

The intrinsic converts the native LLVM floating-point value to the arbitrary FP
format specified by ``interpretation``, applying the requested rounding mode and
saturation behavior. The result is returned as an integer (e.g., ``i8`` for FP8,
``i6`` for FP6) containing the encoded arbitrary FP bits. When saturation is enabled,
values that exceed the representable range are clamped to the minimum or maximum
normal value of the target format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this intrinsic can do many-to-many conversions, it can cover cases already handled by instructions like fptoui or others right?
Is that something desirable to have multiple ways to do the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behaviour is something that I wouldn't desire. New version of the patch removes 'none'.

Example:
""""""""

::

; Convert half to FP8 E4M3 format
%fp8bits = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(
half %value, metadata !"Float8E4M3",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could really use some examples of different type combinations.

As I understand it, there are basically three overloads here a) IR FP type, b) IR integer type, c) IR integer type interpreted as FP type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 again, I was thinking that some examples would be helpful.

metadata !"round.tonearest", i1 false)

; Convert vector of float to FP8 E5M2 with saturation
%vec_fp8 = call <4 x i8> @llvm.convert.to.arbitrary.fp.v4i8.v4f32(
<4 x float> %values, metadata !"Float8E5M2",
metadata !"round.towardzero", i1 true)

'``llvm.convert.from.arbitrary.fp``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare <fNxM> @llvm.convert.from.arbitrary.fp.<fNxM>.<iNxM>(
<iNxM> <value>, metadata <interpretation>,
metadata <rounding mode>, i1 <saturation>)

Overview:
"""""""""

The ``llvm.convert.from.arbitrary.fp`` intrinsic converts an integer containing
arbitrary FP bits to a native LLVM floating-point value. This intrinsic is
overloaded on both its return type and first argument.

Arguments:
""""""""""

``value``
An integer value containing the arbitrary FP bits (e.g., ``i8`` for FP8, ``i6`` for FP6).

``interpretation``
A metadata string describing the source arbitrary FP format. Supported format names include:

- FP8 formats: ``"Float8E5M2"``, ``"Float8E5M2FNUZ"``, ``"Float8E4M3"``,
``"Float8E4M3FN"``, ``"Float8E4M3FNUZ"``, ``"Float8E4M3B11FNUZ"``, ``"Float8E3M4"``,
``"Float8E8M0FNU"``
- FP6 formats: ``"Float6E3M2FN"``, ``"Float6E2M3FN"``
- FP4 formats: ``"Float4E2M1FN"``

``rounding mode``
A metadata string specifying the rounding mode. The permitted strings match those
accepted by :ref:`llvm.fptrunc.round <int_fptrunc_round>` (for example,
``"round.tonearest"`` or ``"round.towardzero"``).

``saturation``
A compile-time constant boolean value (``i1``). When ``true``, values outside the
representable range of the target format are clamped to the minimum or maximum normal value.
When ``false``, no saturation is applied. This parameter must be an immediate constant.

Semantics:
""""""""""

The intrinsic interprets the integer value as arbitrary FP bits according to
``interpretation``, then converts to the native LLVM floating-point result type,
applying the requested rounding mode and saturation behavior. When saturation is
enabled, values that exceed the representable range of the target format are
clamped to the minimum or maximum normal value.

Example:
""""""""

::

; Convert FP8 E4M3 bits to half
%half_val = call half @llvm.convert.from.arbitrary.fp.f16.i8(
i8 %fp8bits, metadata !"Float8E4M3",
metadata !"round.tonearest", i1 false)

; Convert vector of FP8 E5M2 bits to float
%vec_float = call <4 x float> @llvm.convert.from.arbitrary.fp.v4f32.v4i8(
<4 x i8> %fp8_values, metadata !"Float8E5M2",
metadata !"round.tonearest", i1 false)

Convergence Intrinsics
----------------------

Expand Down
16 changes: 16 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,22 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable] in {
def int_fptrunc_round : DefaultAttrsIntrinsic<[ llvm_anyfloat_ty ],
[ llvm_anyfloat_ty, llvm_metadata_ty ]>;

// Convert from native LLVM floating-point to arbitrary FP format
// Returns an integer containing the arbitrary FP bits
def int_convert_to_arbitrary_fp
: DefaultAttrsIntrinsic<
[ llvm_anyint_ty ],
[ llvm_anyfloat_ty, llvm_metadata_ty, llvm_metadata_ty, llvm_i1_ty ],
[ IntrNoMem, IntrSpeculatable, ImmArg<ArgIndex<3>> ]>;

// Convert from arbitrary FP format to native LLVM floating-point
// Takes an integer containing the arbitrary FP bits
def int_convert_from_arbitrary_fp
: DefaultAttrsIntrinsic<
[ llvm_anyfloat_ty ],
[ llvm_anyint_ty, llvm_metadata_ty, llvm_metadata_ty, llvm_i1_ty ],
[ IntrNoMem, IntrSpeculatable, ImmArg<ArgIndex<3>> ]>;

def int_canonicalize : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>],
[IntrNoMem]>;
// Arithmetic fence intrinsic.
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2842,7 +2842,10 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
if (!MDN) {
if (auto *ConstMD = dyn_cast<ConstantAsMetadata>(MD))
MDN = MDNode::get(MF->getFunction().getContext(), ConstMD);
else // This was probably an MDString.
else if (auto *MDS = dyn_cast<MDString>(MD)) {
Metadata *Ops[] = {MDS};
MDN = MDNode::get(MF->getFunction().getContext(), Ops);
} else
return false;
}
MIB.addMetadata(MDN);
Expand Down
34 changes: 34 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#include "llvm/IR/Dominators.h"
#include "llvm/IR/EHPersonalities.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/FPEnv.h"
#include "llvm/IR/GCStrategy.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalValue.h"
Expand Down Expand Up @@ -5848,6 +5849,39 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
"unsupported rounding mode argument", Call);
break;
}
case Intrinsic::convert_to_arbitrary_fp:
case Intrinsic::convert_from_arbitrary_fp: {
// Check interpretation metadata (argoperand 1)
auto *InterpMAV = dyn_cast<MetadataAsValue>(Call.getArgOperand(1));
Check(InterpMAV, "missing interpretation metadata operand", Call);
auto *InterpStr = dyn_cast<MDString>(InterpMAV->getMetadata());
Check(InterpStr, "interpretation metadata operand must be a string", Call);
StringRef Interp = InterpStr->getString();

Check(!Interp.empty(), "interpretation metadata string must not be empty",
Call);

// Valid interpretation strings: mini-float format names
bool IsKnown = Interp == "Float8E5M2" || Interp == "Float8E5M2FNUZ" ||
Interp == "Float8E4M3" || Interp == "Float8E4M3FN" ||
Interp == "Float8E4M3FNUZ" || Interp == "Float8E4M3B11FNUZ" ||
Interp == "Float8E3M4" || Interp == "Float8E8M0FNU" ||
Interp == "Float6E3M2FN" || Interp == "Float6E2M3FN" ||
Interp == "Float4E2M1FN";
Check(IsKnown, "unsupported interpretation metadata string", Call);

// Check rounding mode metadata (argoperand 2)
auto *RoundingMAV = dyn_cast<MetadataAsValue>(Call.getArgOperand(2));
Check(RoundingMAV, "missing rounding mode metadata operand", Call);
auto *RoundingStr = dyn_cast<MDString>(RoundingMAV->getMetadata());
Check(RoundingStr, "rounding mode metadata operand must be a string", Call);

std::optional<RoundingMode> RM =
convertStrToRoundingMode(RoundingStr->getString());
Check(RM && *RM != RoundingMode::Dynamic,
"unsupported rounding mode argument", Call);
break;
}
#define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) case Intrinsic::VPID:
#include "llvm/IR/VPIntrinsics.def"
#undef BEGIN_REGISTER_VP_INTRINSIC
Expand Down
Loading
Loading