-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[RFC][SPIR-V] Add intrinsics to convert to/from ap.float #164252
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
|
||
| Example: | ||
| """""""" | ||
|
|
||
| :: | ||
|
|
||
| ; Convert half to FP8 E4M3 format | ||
| %fp8bits = call i8 @llvm.convert.to.arbitrary.fp.i8.f16( | ||
| half %value, metadata !"Float8E4M3", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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>) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For fp-to-fp conversions, what does the "saturation" bit do? For formats that have inf/-inf, the "representable range" is infinite. Do some of the formats not have infinity? For such formats, is it really useful to allow non-saturating conversions?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be "clamp" is a better name, though OCP also uses "saturate". Basically, if a format have representation of inf, we still should be able to specify, that rounding of out of range values clamps to max/min normal value instead of +/- inf. See https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf Table 3:
Note, that E5M2 format has infinity representation.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please fix the documentation to state more clearly what happens when "saturation" is false, for destination formats that have infinity, and formats that don't have infinity, since the spec has specific requirements. |
||
|
|
||
| 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 | ||
| ---------------------- | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -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)) { | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This came up in another review recently; why does this need to wrap metadata in MDNode?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather keep the codegen implementation separate from the patch which introduces the intrinsic definition, but that should include DAG glue too |
||||||||
| Metadata *Ops[] = {MDS}; | ||||||||
| MDN = MDNode::get(MF->getFunction().getContext(), Ops); | ||||||||
|
Comment on lines
+2846
to
+2847
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| } else | ||||||||
| return false; | ||||||||
| } | ||||||||
| MIB.addMetadata(MDN); | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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" | ||||||
|
|
@@ -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) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| 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 | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| bool IsKnown = Interp == "Float8E5M2" || Interp == "Float8E5M2FNUZ" || | ||||||
| Interp == "Float8E4M3" || Interp == "Float8E4M3FN" || | ||||||
| Interp == "Float8E4M3FNUZ" || Interp == "Float8E4M3B11FNUZ" || | ||||||
| Interp == "Float8E3M4" || Interp == "Float8E8M0FNU" || | ||||||
| Interp == "Float6E3M2FN" || Interp == "Float6E2M3FN" || | ||||||
| Interp == "Float4E2M1FN"; | ||||||
|
Comment on lines
+5865
to
+5870
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in a more accessible utility somewhere, rather than buried in this one verifier function |
||||||
| Check(IsKnown, "unsupported interpretation metadata string", Call); | ||||||
|
|
||||||
| // Check rounding mode metadata (argoperand 2) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| 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); | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you need to manually verify consistent element counts |
||||||
| 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 | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.