-
Notifications
You must be signed in to change notification settings - Fork 251
Implement extension SPV_KHR_float_controls2 #3475
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?
Implement extension SPV_KHR_float_controls2 #3475
Conversation
We shouldn't, as you have quoted: "This rule implies that a function appearing in both call graphs of two distinct entry points may behave differently in each case.". Runtime should be able to pass fast math controls from a caller to a callee.
I'm a bit worried about bloating size of SPIR-V modules in this case. In general I'd suggest to align behaviour of the translator and SPIR-V backend in areas where it's possible. So I'd expect llvm-spirv's implementation resulting in the same SPIR-V as llvm/llvm-project#146941 aka there should be FPFastMathDefault set. |
|
I'll go on vacation in a few hours, and I'm afraid I will not have time to review this before I leave. Feel free to merge this without my approval, and I'll make sure I review when I'm back, even if it's a post-merge review. I did want to bring up a couple of related issues, though. Hopefully they can be resolved by this PR. |
Then the current implementation should be good, since it doesn't propagate anything.
I see. Then I should fix this implementation to always emit a |
I've addressed this in b691977 . This commit emits an |
|
This one is tricky. I've added a commit related to this, but I'll file a separate patch since this issue is not related to the |
cd6a10d to
57840f3
Compare
Fine with me. Most (if not all) of the folks working on the translator are currently on holidays (including myself), so guess review will be done a bit later :) (unless there is a super urgency - in this case I can take a look before New Year) |
No problem! It's not urgent. |
test/extensions/KHR/SPV_KHR_float_controls2/execution_mode_default.ll
Outdated
Show resolved
Hide resolved
14519f7 to
8fa049e
Compare
MrSidims
left a comment
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.
LGTM
I'd like to hear from @maarquitos14 before merging.
|
Just in case, I'd like to bring the attention to one of my previous messages about the issue #3125 : Currently, this PR maps LLVM's In the issue it is suggested that we'd better translate Then, if we map LLVM's to SPIRV and back to LLVM we end up with different semantics: To avoid this, we could translate |
Thanks for bringing the attention back. I believe we should do one thing at a time and fix behaviour in unrelated to this PR patch. |
With this extension, the execution modes `ContractionOff and `SignedZeroInfNanPreserve` are deprecated and we should use `FPFastMathDefault` instead. Additionally, the `FPFastMathMode` mode `Fast` bit is also deprecated.
Before, the extension would be used only when an operation having fast-math flags that can only be represented using float_controls2 was enabled. Afer this patch, the extension is added if floating-point types are used in the module.
8fa049e to
dd4806c
Compare
I plan to look at this today/tomorrow. |
That works for me, thanks. Just highlighted it here to make sure it worked well with the current implementation. |
@jmmartinez ping me if you do create a separate patch for this. |
maarquitos14
left a comment
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.
First pass. I'll do a second pass to check tests.
|
|
||
| case spv::ExecutionModeSignedZeroInfNanPreserve: | ||
| // With SPV_KHR_float_controls2 this is deprecated | ||
| if (BM->hasCapability(CapabilityFloatControls2)) |
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.
Don't we need to add FPFastMathDefault execution mode too? It is required to set the equivalent of SignedZeroInfNanPreserve, isn't it?
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.
At the moment, since the default fast-math flags are all disabled, both are preserved (ContractionOff/SignedZeroInfNanPreserve disable the contract/nsz ninf nnan flags).
I should add a comment explaining how these are preserved.
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.
Okay, I see what you mean. However, I vaguely recall that having no flags isn't the same as having all flags set to zero from my implementation of this extension in the SPIRV BE. Let me try and find that again.
Also, a comment would help anyway :)
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.
If an operation is decorated with FPFastMathMode then the flags from that decoration apply. Otherwise, if the current entry point sets any FPFastMathDefault execution mode then all flags specified for any operand type or for the result type of the operation apply. If the operation is not decorated with FPFastMathMode and the entry point sets no FPFastMathDefault execution modes then the flags to be applied are determined by the client API and not by SPIR-V.
My understanding of this quote from the spec is that no decoration is not the same than decoration with all flags set to zero: all flags set to zero clearly specify the fast math mode, while no decoration means the client API can decide. Do you agree?
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 agree.
Currently, if float_controls2 is available, we enable it always with all the flags set to zero. Then an LLVM floating-point operation with no flags has the same semantics in SPIRV.
However, I think there is a problem in my implementation: functions getting called by kernels.
Currently the FastMathModeDefault are not preserved when doing spirv->llvm-ir->spirv. When doing spirv->llvm-ir we set the FastMathModeDefault into the kernel operations, but we cannot do that on the called functions. Then, when doing llvm-ir->spirv we end up with the right flags on the kernel, but stricter flags (all set to 0 propagated through the new FastMathModeDefault) on the called function.
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.
Just a second contradicting thought. In fact, depending on how you see it, setting no flags in SPIRV can also be seen as enabling all rewrite flags in LLVM: contract / reassociate / ... are all permitted and is up to the client to decide if it optimizes it or not.
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.
Exactly, setting everything to zero might prevent possible client optimizations. I think we shouldn't do that.
| entry: | ||
| ; IR-LABEL: define {{.*}} @foo | ||
| ; IR-NEXT: entry: | ||
| ; IR-NEXT: %rh = fadd contract half %ah, %bh |
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.
My understanding is that you don't check decorations in SPIRV because you assume that they have to be present in SPIRV if they are present in the reverse translation. Am I right?
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.
Sort of. I wanted to check only that the ExecutionModeId was set correctly (the flags set on the instructions are verified in other tests). And reverse translated it to ensure the contract flag doesn't get overridden by it.
I can add the checks for the individual instructions if it make more sense.
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.
As long as the intent is clearly specified in the test, I'm happy with that. Can you add a comment explaining this?
test/extensions/KHR/SPV_KHR_float_controls2/execution_mode_id.spvasm
Outdated
Show resolved
Hide resolved
|
|
||
| // We encode an fp-operaiton with no FPFastMathMode flags set as an | ||
| // fp-operation with all the flags set to 0. Instead of setting the flag for | ||
| // every individual operation, we set it once, for the entry-point. |
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'm not really sure if this is correct. Like I said in a different thread, I don't think it's the same having all flags set to 0 than not having the decoration.
| ; SPIRV-DAG: TypeFloat [[#double:]] 64 | ||
| ; | ||
| ; 6028 is FPFastMathDefault | ||
| ; SPIRV-DAG: ExecutionModeId [[#foo]] 6028 [[#half]] [[#zero]] |
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.
It's not clear to me where is the OpExecutionModeId coming from. I don't see any !spirv.ExecutionMode metadata.
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.
At the moment this patch is not setting !spirv.ExecutionMode and it always sets the FastMathDefaultFlags to 0 (which is probably too strict).
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.
So where does the OpExecutionMode come from, then?
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.
At the moment, it's always setting it to 0 by default in SPIRV. So even if the decoration doesn't appear in the LLVM-IR, if we allow SPV_KHR_float_controls2, it is added to every kernel.
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.
Okay, I understand. I have doubts about such an approach for the reasons mentioned in another thread: we would be setting constraints to kernels that actually don't come from the original source. Without those constraints, client APIs have more freedom to optimize. @MrSidims what's your opinion on this?
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.
My understanding, is that None FP Fast Math Mode set in FPFastMathDefault is not overriding default controls of client API. @bashbaug @alan-baker is my understanding correct?
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.
Any default specified overrides the client. None is saying the default is no fast math (which may match what the client requires).
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.
Thanks for clarification, in such case current behaviour is undesired,
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.
Sounds good. Then, I'll modify the code such that when translating LLVM-IR to SPIRV we should avoid setting the FPFastMathDefault (unless there is metadata specifying it).
In that case a fp-operation without flags in LLVM, fadd %a %b will map to a fp-operation without flags in SPIRV (instead of having the flags set to 0 through FPFastMathDefault).
One case that I still have to reflect more on how to translate ContractionOff/SignedZeroInfNanPreserve.
We cannot set an FPFastMathDefault or flags on individual instructions such that only some flags are set and the rest are left to the client API.
From what I understand, float_controls2 deprecates those execution modes, but doesn't forbid them. We could still set them and print a warning as a first step.
What do you think?
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.
Then, I'll modify the code such that when translating LLVM-IR to SPIRV we should avoid setting the FPFastMathDefault (unless there is metadata specifying it).
I think that is what we want, yes. You can reuse, or at least cross-check with, the tests in the SPIRV BE: https://github.com/llvm/llvm-project/tree/main/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_float_controls2
From what I understand, float_controls2 deprecates those execution modes, but doesn't forbid them. We could still set them and print a warning as a first step.
What we did in the SPIRV BE was to replace them with the appropriate flags through FPFastMathDefault. In that case, I think it's okay to do it, because it comes explicitly from the source. They set ContractionOff/SignedZeroInfNanPreserve, and we need to translate that.
| ; RUN: llvm-spirv --spirv-ext=+SPV_KHR_float_controls2 %s -o %t.spv | ||
| ; RUN: spirv-val %t.spv | ||
|
|
||
| ; Do not add extension if no floating-point type is used in this module. |
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.
What if there is an fp type but it doesn't have any flags, or there is none of the FP operations? Does it enable the extension in that case? Should it enable the extension in that case?
| ; SPIRV-DAG: TypeFloat [[#float:]] 32 | ||
| ; | ||
| ; 6028 is FPFastMathDefault | ||
| ; SPIRV-DAG: ExecutionModeId [[#foo]] 6028 [[#float]] [[#zero]] |
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'm a bit lost here: the test is named extension_not_needed_but_used, however, you expect FPFastMathDefault execution mode, which does need the extension, I think?
At the same time, similarly to a previous test, I don't see any !spirv.ExecutionMode metadata, so I'm not sure why we expect this execution mode.
First attempt at implementing SPV_KHR_float_controls2.
Some highlights:
ExecutionModeFPFastMathDefaultfor every kernel, and if instructions in that kernel do not specify a particularFPFastMathMode, we use the kernel one (question below).afnflag. If we mapfadd fast float %a, %bto SPIRV and back, it becomesfadd reassoc nnan ninf nsz arcp contract float %a, %blosing theafnflag.Some questions:
FPFastMathMode? Should we propagate the attribute down to the callees?ExecutionModeFPFastMathDefaultwhen writing SPIRV. Instead it writes the appropriateFPFastMathModefor every instruction. In that case, should we emit a "zero"FPFastMathModefor instructions without any fast-math-flags ?