-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[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 1 commit
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,69 @@ environment <floatenv>` *except* for the rounding mode. | |
| This intrinsic is not supported on all targets. Some targets may not support | ||
| all rounding modes. | ||
|
|
||
| '``llvm.arbitrary.fp.convert``' Intrinsic | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
MrSidims marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Syntax: | ||
| """"""" | ||
|
|
||
| :: | ||
|
|
||
| declare <type> @llvm.arbitrary.fp.convert( | ||
|
||
| <type> <value>, metadata <result interpretation>, | ||
| metadata <input interpretation>, metadata <rounding mode>, | ||
| i32 <saturation>) | ||
|
|
||
| Overview: | ||
| """"""""" | ||
|
|
||
| The ``llvm.arbitrary.fp.convert`` intrinsic performs conversions | ||
| between values whose interpretation differs from their representation | ||
| in LLVM IR. The intrinsic is overloaded on both its return type and first | ||
| argument. Metadata operands describe how the raw bits should be interpreted | ||
| before and after the conversion. | ||
|
||
|
|
||
| Arguments: | ||
| """""""""" | ||
|
|
||
| ``value`` | ||
| The value to convert. Its interpretation is described by ``input | ||
| interpretation``. | ||
|
|
||
| ``result interpretation`` | ||
| A metadata string that describes the type of the result. The string | ||
| can be ``"none"`` (no conversion needed), ``"signed"`` or ``"unsigned"`` (for | ||
MrSidims marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| integer types), or any target-specific string for floating-point formats. | ||
| For example ``"spv.E4M3EXT"`` and ``"spv.E5M2EXT"`` stand for FP8 SPIR-V formats. | ||
|
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. As a lot of these formats are weird and may not have all the "usual" FP values (like NaN, Inf, etc), I think we need to specify how the conversions work in that case. If I have a float NaN and convert to Float6E3M2FN, what happens? Is this just considered invalid and returns poison? Or does it do something else?
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. I believe poison is good enough. I've changed the description for intrinsics, hope it address this and other questions. |
||
| Using ``"none"`` indicates the converted bits already have the desired LLVM IR type. | ||
|
||
|
|
||
| ``input interpretation`` | ||
| Mirrors ``result interpretation`` but applies to the first argument. The | ||
| interpretation is target-specific and describes how to interpret the raw bits | ||
| of the input value. | ||
|
|
||
| ``rounding mode`` | ||
| A metadata string. The permitted strings match those accepted by | ||
| :ref:`llvm.fptrunc.round <int_fptrunc_round>` (for example, | ||
|
||
| ``"round.tonearest"`` or ``"round.towardzero"``). The string ``"none"`` may be | ||
| used to indicate that the default rounding behaviour of the conversion should | ||
| be used. | ||
MrSidims marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ``saturation`` | ||
| An integer constant (0 or 1) indicating whether saturation should be applied | ||
| to the conversion. When set to 1, values outside the representable range of | ||
| the result type are clamped to the minimum or maximum representable value | ||
| instead of wrapping. When set to 0, no saturation is applied. | ||
MrSidims marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Semantics: | ||
Keenuts marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """""""""" | ||
|
|
||
| The intrinsic interprets the first argument according to ``input | ||
| interpretation``, applies the requested rounding mode and saturation behavior, | ||
| and produces a value whose type is described by ``result interpretation``. | ||
| When saturation is enabled, values that exceed the representable range of the target | ||
| format are clamped to the minimum or maximum representable value of that format. | ||
|
|
||
|
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. |
||
| 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)) { | ||
|
||
| Metadata *Ops[] = {MDS}; | ||
| MDN = MDNode::get(MF->getFunction().getContext(), Ops); | ||
MrSidims marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } 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,52 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { | |
| "unsupported rounding mode argument", Call); | ||
| break; | ||
| } | ||
| case Intrinsic::arbitrary_fp_convert: { | ||
| auto *ResultMAV = dyn_cast<MetadataAsValue>(Call.getArgOperand(1)); | ||
| Check(ResultMAV, "missing result interpretation metadata operand", Call); | ||
| auto *ResultStr = dyn_cast<MDString>(ResultMAV->getMetadata()); | ||
| Check(ResultStr, "result interpretation metadata operand must be a string", | ||
| Call); | ||
| StringRef ResultInterp = ResultStr->getString(); | ||
|
|
||
| auto *InputMAV = dyn_cast<MetadataAsValue>(Call.getArgOperand(2)); | ||
| Check(InputMAV, "missing input interpretation metadata operand", Call); | ||
| auto *InputStr = dyn_cast<MDString>(InputMAV->getMetadata()); | ||
| Check(InputStr, "input interpretation metadata operand must be a string", | ||
| Call); | ||
| StringRef InputInterp = InputStr->getString(); | ||
|
|
||
| auto *RoundingMAV = dyn_cast<MetadataAsValue>(Call.getArgOperand(3)); | ||
| 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); | ||
| StringRef RoundingInterp = RoundingStr->getString(); | ||
|
||
|
|
||
| // Check that interpretation strings are not empty. The actual interpretation | ||
| // values are target-specific and not validated here. | ||
| Check(!ResultInterp.empty(), | ||
| "result interpretation metadata string must not be empty", Call); | ||
| Check(!InputInterp.empty(), | ||
| "input interpretation metadata string must not be empty", Call); | ||
|
|
||
| if (RoundingInterp != "none") { | ||
| std::optional<RoundingMode> RM = | ||
| convertStrToRoundingMode(RoundingInterp); | ||
| Check(RM && *RM != RoundingMode::Dynamic, | ||
| "unsupported rounding mode argument", 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
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. Added |
||
| // Check saturation parameter (must be 0 or 1) | ||
| auto *SaturationOp = dyn_cast<ConstantInt>(Call.getArgOperand(4)); | ||
| Check(SaturationOp, "saturation operand must be a constant integer", Call); | ||
| if (SaturationOp) { | ||
| uint64_t SatVal = SaturationOp->getZExtValue(); | ||
| Check(SatVal == 0 || SatVal == 1, | ||
| "saturation operand must be 0 or 1", Call); | ||
| } | ||
| break; | ||
| } | ||
| #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) case Intrinsic::VPID: | ||
| #include "llvm/IR/VPIntrinsics.def" | ||
| #undef BEGIN_REGISTER_VP_INTRINSIC | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd add a note to both intrinsics that the supported conversions are target dependent. (These aren't going to get generic legalization support, right?)