-
Notifications
You must be signed in to change notification settings - Fork 193
spv-in miscompilation of inlined if statement with return #940
Comments
I also just noticed that the That seems bad, should I file another issue for that? |
Thank you for filing this!
You can see that the @MatusT looks like something is not right with the CFG again here. Would you be able to peek at this for the initial diagnostics? |
I'll take a look on friday. |
I don't believe this has been fixed. Naga now panics with:
Admittedly, it's no longer a miscompilation, but the compilation still fails. Let me know if you'd like me to open a new issue for this new error. |
This is still broken. SPIR-V file attached (gzipped to make GitHub let me attach it). |
It is not yet fixed entirely. There are still 2 issues remaining:
I don't know when I can find time to fix any of those. Currently, It's only me who is working on CFG code and occasionally @kvark. My contributions are a total side project for me, I am into computer graphics, not compilers. You will have to either wait until I find time to fix all the issues or somebody else has to do it. |
@khyperia I take this is a blocker for you? We understand the issue is a serious one. The tentative plan was to augment the
|
Sorry, I should have been more clear, this isn't directly related to my professional rust-gpu work - well, it is in the sense that someone could write this code, use wgpu, and so be broken, but nobody has yet. I discovered this just through my own personal projects rather than rust-gpu, and from a user perspective rather than compiler developer, there are workarounds I can do in my own personal stuff. The biggest importance is probably just a desire for naga to be a robust shader translation tool (e.g. spirv-cross correctly handles this) to not block potential future adoption by us in our own render engine. |
@MatusT something is missing here. I resolved the validation issue with #993, but the generated IR is looking wrong, even without any patching:
This is taken from What we see here is the |
1518: Make spirv an optional feature r=cwfitzgerald a=kvark **Connections** gfx-rs/naga#940 shows how much SPIR-V parsing can be a pain. **Description** Keep it supported natively, but put it behind a feature flag. This allows to skip compilation of parts of Naga as well as dependencies like `petgraph`. On my machine, compiling `wgpu-core` time is reduced from 40.87s to 35.36s, which is about 13% improvement. **Testing** Just compiling Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This seems to have been fixed by something, perhaps the new GLSL front end. The following input:
produces the following SPIR-V for
This seems to reach the
|
Oops, sorry for the noise. I wasn't using the right input. |
Okay, the interesting SPIR-V input is this:
Roughly, this is:
The Naga IR generated for this has completely failed to pack up the
That |
Oh, apparently the empty
into
|
Aaaaand the issue is that that transformation is invalid if the |
As discussed on Matrix, the plan for this specific issue is to handle the degenerate conditions a bit better. |
I'm not sure the approach of simply eliminating degenerate control flow can work. To sum things up: The whole reason we're playing these games at all is that Naga IR and SPIR-V have different rules for where expressions / instructions can be used. Whereas Naga IR scopes expressions according to the statement tree, SPIR-V lets you use an instruction's value as long as the instruction dominates the use - and then further gives you phi nodes that can use its value even in code it doesn't dominate. Since SPIR-V is more forgiving than Naga IR, translation from the former to the latter entails introducing temporary variables to hold values that would otherwise be out of scope. Our SPIR-V front end depends on phi nodes to tell us when these variables need to be introduced: a value must be stashed in an introduced temporary variable exactly if it is the input to a phi. But this approach fails in the presence of "degenerate control flow": any SPIR-V control flow like a loop, selection, or switch that appears to be conditional, but which actually contains blocks that do dominate the following code. The example at hand is:
In this case, despite being part of a "conditional" structure (the switch), block Another example of degenerate control flow might be the SPIR-V equivalent of:
In this case, block Each of these cases can be corrected to remove the degenerate control flow. The
and the second is equivalent to:
But at this point it's getting hard to feel confident that we've covered all cases. |
Another case to consider:
In this case,
If |
@jimblandy thank you for an excellent description of what we are trying to solve. Based on your examples, I'm not seeing why the proposed patching strategy wouldn't work. It would be really nice if we do all this without uplifting any expressions to variables, just from the "perfectionist" position here :) |
How would we handle this one?
In this case
|
Here's another degenerate case:
|
I think what this shows is that, as we walk a degenerate switch's default block, we need to ask both:
Both situations could introduce phi-less references to expressions from nested statements. |
Here's another case we don't handle, for similar reasons, that has nothing to do with switch statements. We get:
This SPIR-V is basically:
Because of the Here's the SPIR-V:
The IR is:
|
Here's how I think this "concealed dominator" transformation has to work. Since
First of all, as we consider the default of a degenerate switch, there are two possible regions that could have concealed dominators:
So we need to watch out for both the code after the switch, and the tail of the default block. We can inspect each statement in the default block to determine if it never breaks out of the switch, always breaks, or sometimes breaks. This is decidable because we don't care about data at all, only control flow: SPIR-V's SSA only cares about the shape of the graph, so Starting at the head of the default block, all statements that never break dominate both the tail and outside. Since outside will use them without phis, they must be moved before the switch:
After the first statement that sometimes or always breaks, nothing in the default block dominates outside, so we don't need to think about moving that out. We only need to worry about the tail. Nested loops and switches capture any So suppose we have:
Each of
And symmetrically, if
Then, processing of the tail must continue starting with the code moved out of the Otherwise, An else-less Suppose we apply this algorithm to a variant of the tangle I showed earlier. Assume the letter statements never break.
Since Since In
This looks like a right answer for that input: all the dominators that need to stay in the I haven't thought about how to implement this. The 'never/sometimes/always breaks' analysis needs to be computed once and retained for statements even when they're moved, because otherwise we'll have to re-analyze subtrees each time we encounter them, and the algorithm will be quadratic. |
This kind of analysis is really different from what I've usually heard people talking about. We're kind of being led around by the nose by IR restrictions. But I'm pursuing it because the alternative seems to be to make deep changes to Naga's IR, which would be disruptive. |
This doesn't make the original SPIR-V input work, unfortunately, since it seems like there's a spurious
The |
Another lovely case:
Here, A, B, and C dominate E. Only C can be moved out of the loop. [fixed] |
|
More simply, I think this can't be transformed into Naga IR without introducing temporary variables, even though it requires no phis in SPIR-V:
|
We now generate the following SPIR-V for the original input file:
Excessive temporaries aside, this looks correct to me. If there's still a problem, feel free to re-open. |
This is a follow-up to #939, when I looked at the output of naga's GLSL backend on that code.
The SPIR-V input (and the GLSL that created it) can be found in this shader-playground link. Check the input GLSL program vs. the result GLSL program: if
thingy > 5
, then I would expect101010
to be returned. But,0
is returned unconditionally!Input GLSL:
Disassembled SPIR-V, click to expand
Output GLSL, generated using
cargo run --features spv-in,glsl-out -- naga-issue.spv main.frag
:MSL output looks very similar, hence me thinking it's a problem in the SPIR-V frontend, rather than the GLSL backend:
cargo run --features spv-in,msl-out -- naga-issue.spv main.metal
The text was updated successfully, but these errors were encountered: