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
[wgsl] continuing block #579
Comments
Some notes:
|
I believe option 3: Comma operator is the only solution that:
|
The comma operator doesn't allow you to do more complicated things like if and loops inside the continuing block. |
It does allow that if it's a statement separator and not an expression separator. (Which is admittedly weird. But such continue blocks are likely to be uncommon, both in WGSL and in SPIR-V, except perhaps as artifacts of code motion optimizations done at the SPIR-V level.) |
Edit 2: This entire post is in response to option 3 in the original post. Is there a possibility to allow something that unifies
Could allow something goofy like this: # common (human-generated) case
for (i = 0; i < 10; i = i + 1) {
# loop body
}
# loop with complex initial and continuing blocks
for ({
i = 0;
j = 0;
}; i < 10; {
i = i + 1;
j = j + 1;
}) {
# advanced loop body
} Alternate whitespace formatting example# minified
for({i=0;j=0;};i<10;{i=i+1;j=j+1;}){
# advanced loop body
}
# expanded
for (
{
i = 0;
j = 0;
};
i < 10;
{
i = i + 1;
j = j + 1;
}
) {
# advanced loop body
} Note: I'm unsure of the implications of this beyond the superficial parsing/syntax in this comment. Edit: Here's some slightly less subjective arguments for a style like this:
|
That looks a lot worse then just |
Unfortunately I'm not sure if there's an objectively "best" answer, given the nature of the problem... I've updated my post above with (hopefully) some more objective reasoning, for future consideration. |
A while loop with a break statement could almost entirely capture the semantics of |
You have no way of knowing the continuation block for a |
I think As in the example I posted in #569, I think the current wgsl translation of the for-loop concept isn't that bad. |
Nitpick: There is no "OpContinue" or "OpBreak" in SPIR-V. break and continue are statements in C, and in WGSL. In SPIR-V whether a CFG edge (from a branch branch) is a (valid) edge depends on a bunch of things (see the spec). First off, you are only required to have a "continuing" block for a particular loop if there actually is a "continue" statement for that loop. Sorry if that's not clear. So @litherum that first example can be written as:
Which seems fine to me. In fact, you can get better, and I suggest you follow the lead of Turing and Perl, and write the break first and its firing condition afterward:
But here's where "unless" comes to the rescue:
Or if you really dislike unless, push the inversion through the "unless":
Seems clear enough to me. That's super readable for beginners. (Turing is a teaching langauage widely used for a couple of decades. It would have "exit when" instead of "break if" in this example. Perl is swiss-army chainsaw and it would have "last if" in this example.) Now let's assume there is at least one continue statement in a given loop. That's what forces us to have a place to jump to. About @litherum's options: Option 1: This is what we put into the WGSL proposal. You've mostly intuited the reasons why. In C, GLSL, and HLSL the "update" portion of the for loop normally only contains an increment, or something like that. It can have a function call, or several separated by commas. In GPU compilers it is extraordinarily common to fully in inline every function down from the entry point. That's true for a lot of reasons I won't get into now, but that's just how it works. (It's also why shader languages disallow cycles in the call graph.) For HLSL compilation, it's even stronger than that. We have found through experience with a broad corpus of shaders that full inlining is the first step in a recipe for "legalizing" HLSL shaders so they actually map to a valid thing that can run on the GPU. I refer you to https://www.khronos.org/assets/uploads/developers/library/2018-gdc-webgl-and-gltf/2-Vulkan-HLSL-There-and-Back-Again_Mar18.pdf for an overview, and https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIRV-Cookbook.rst for a bunch of true-to-life examples of source code that are on the face of it totally invalid but made to work via the legalization recipe we put into DXC's HLSL -> SPIR-V flow. (The same recipe is used for Glslang's HLSL --> SPIR-V flow.) Now, that said, a lot of shaders you'll see coming to WebGPU will be fully inlined. We better have a general solution to this "complex thing in the continue construct" challenge. Because a lot of people are going to be staring at their especially complex code having been pushed through an often complex toolchain. Second, the "continuing" construct means the "continue" statement is a forward branch. It's not a backward branch to the middle of the "for" statement. That means the loop backedge in WGSL is the only branch in control flow that goes backward (from bottom of source to top of source). This is exceedingly clear when you think of control flow from first principles. Do this with a friend who hasn't programmed before: Trace your finger through the control flow for a C-style for loop with all 3 clauses active. In each regular iteration, your finger reverses direction twice, and when leaving the test clause, jumps over the update/continue clause to reach the body. That's just confusing when you think about it. The "continuing" construct avoids that. You always go forward, down in the source text, except for that one backedge at the end of the loop that takes up to the top. Easy peasy. Option 2: implicit functions, also called "outlining". That doesn't cut it, I hope that's clear. Yes, you'd have to chase down all the names that cross boundaries into and out of that continuing clause. So we reject it. Option 3: Doesn't cut it. It's not general enough. Option 4: Restrict the possible program. But why? That's giving up. |
Why is Option 3 not general enough? What can it not do? (Note that in Myles's proposal, comma is a statement separator, not just an expression separator, so it is as general as |
Whoops, you're right! I've updated the original post. |
Also Option 6 as described by @tangmi. This is as powerful as option 3 (which, I agree with @othermaciej, is powerful enough if you consider the comma to be a statement separator, rather than an expression separator). I actually agree with your "trace your finger" analogy: to a new programmer, It also makes sense for the language to be designed such that, where possible, a compiler emitting WGSL wouldn't use an entirely distinct set of facilities from a human author. In general, we should try to avoid creating two distinct languages inside WGSL (one for compilers and one for humans). I understand this is not possible in all cases, but I believe |
It's certainly unfamiliar, but it's not totally clear from that post that
This is OK to me (though I am about to argue for it to be occasionally handwritten). I actually agree that However I'm opposed to using I'm going to make up some numbers now for the sake of argument.
From this I arrive at the opinion that:
(BTW, I'm potentially okay with commas in the long run, but strongly against the comma operator which requires things like |
Completely orthogonal to the above post, I've been toying with this idea:
This would be a totally special syntax - I think it has three very minor benefits: it's immediately readable to someone who has seen |
What if we went more primitive?
Now without
I like primitives, so I feel like it Worth mentioning, but you can also desugar continuing without hidden local variables if you have a
In this case the inner loop is really a do{}while(false) block, which fwiw I think is the |
I think if we removed the I was thinking about multi-level breaks at one point too, and I'm hesitant to add them. But now that I think about it... are they needed to express some SPIR-V? |
Actually, now I see your interesting idea over in #581 (comment) about whether |
(Aside: I wanted to know more about where the idea of I think I see what continuing is about now, for instance:
Currently:
But without continuing...:
Consider these classifications:
Too crazy? I think it's worth a thought, though it definitely pushes the bounds of 'directly translatable' back a bit. Simpler on the WGSL side, though. Honestly if |
I think this obscures the structured control flow and start to look like programming with goto. If only one back edge is allowed, this makes it hard to explain what branches are allowed. |
Hey @jdashg This is what I get:
Note that there is no "continue" statement in the original, so there is no "continue" statement in the final. I could work harder to remove the "continuing" and have the the last two statements be free floating at the end. I'll repeat: you only need a continuing clause if you have a continue statement targeting it edited to fix a copy-paste error: added the terminating return and brace |
And before anyone says "oh my that's wordy", my prototype puts each load and each store in its own statement. That explains the plethora of temporaries. |
FYI: If you're trying to understand control flow graphs in SPIR-V, use spirv-cfg from SPIRV-Tools. It emits a GraphViz graph for the CFG of each function, with annotations of merge and continue targets for merge instructions.
The result: It looks a little weird because of how GraphiViz plots the final block (number 52 with the return) as higher than the loop backedge block (number 52). |
Discussed at 2020-03-10 Teleconference |
There's another proposal (I think we're up to number 7 now?): Allow the third clause of the
This requires one of:
|
I don't think WGSL has to do any such scoping/hoisting. Won't a compiler generating WGSL from SPIR-V |
|
Ah. |
In option 3 above, none of the SPIR-V representations would have them hoisted. They would only be hoisted in WGSL. When compiling WGSL to SPIR-V, the reverse transformation would sink them into the loop and demote them to SSA Ids. |
Gotcha. |
FYI. I posted #711 which fills in a lot of missing info from the original draft. |
The |
WGSL meeting minutes 2022-02-08
|
* createRenderPipelineAsync validation Refactor the valitadion of createRenderPipeline, and add validation tests for createRenderPipelineAsync. * Code improve Improve the code, and merge sample_count_must_be_equal_to_the_one_of_every_attachment_in_the_render_pass in createRenderPipeline.spec.ts into render_pass_or_bundle_and_pipeline,sample_count in attachment_compatibility.spec.ts.
The Problem
All loops in SPIR-V have this structure:
(
continue
will jump you to%52
, andbreak
will jump you just after theloop back
.)Note that the loop increment code can contain arbitrary SPIR-V. In fact, it can even contain another whole loop! The only requirement is that the continue label dominate the backedge.
However, in human-writable languages, the contents of a
for
loop increment can't contain arbitrary code. They can only contain a single statement. Therefore, this is one of the numerous places where SPIR-V is more expressive than human writable languages.WGSL strives to be a faithful vessel for SPIR-V source (among other things it strives for). Therefore, there's a desire for it to be able to represent this kind of control flow.
Option 1:
continuing
This is what WGSL has today. You represent arbitrary code in the loop increment by giving the loop increment a whole block, with
{}
s.This is the most faithful to the original SPIR-V. On the other hand, it's confusing to authors.
If additional loop types are added, authors need never write
continuing
. It can be relegated to "things that are only present to support SPIR-V compilers."Option 2: Implicit functions
Force compilers producing WGSL to put the loop increment in a new function, which wasn't present in the original SPIR-V source.
This is the most familiar to authors. On the other hand, scanning for variable references to build the argument list for
invisibleFunction
is a nonzero amount of work.Option 3: Comma operator
If we allow all statements to be separated by commas, then we can represent arbitrary code by just a concatenation of the statements.
This means something like the following would be valid:
The comma operator is already somewhat familiar to programmers of the C family of languages. Just like in those languages, its use would be fairly rare, and could perhaps be relegated to "things that are only present to support SPIR-V compilers." Authors writing normal code need not use it.
Option 4: Restrict the set of acceptable programs
Even though SPIR-V is more expressive than human-writable languages, it is being used as a vessel to represent source code written in those human-writable languages. Therefore, most SPIR-V programs wouldn't actually put multiple statements in the loop increment, because the original source code doesn't put multiple statements in the loop increment. Therefore, we could put restrictions on what we are willing to accept in the loop increment, such that it would only contain a single WGSL statement, and most, but not all, SPIR-V code would be compatible.
SPIR-V optimizers might make this analysis difficult, because who knows what code they will move into the loop increment.
There is already precedent for not representing all SPIR-V programs. There are certain operations which simply can't be representable in WGSL because they aren't representable in either HLSL or Metal Shading Language (for example,
OpImageTexelPointer
or puttingvolatile
on loads/stores instead of variables). Therefore, any SPIR-V tool will already necessarily have to have a mode switch for WGSL.The downside is we lose compatibility with more SPIR-V programs. The upside is tools can be simpler.
Option 5: Introduce invisible variables
This would move the loop increment inside the loop body, and add additional control flow around it.
If the original source looked like
This could be represented as
The text was updated successfully, but these errors were encountered: