-
Notifications
You must be signed in to change notification settings - Fork 335
Remove break if
in favor of if () { break; }
.
#643
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
Conversation
These seem easy to identify, and if so we should consider having One True Way to do things. |
I strongly object. The if with brace-enclosed-statements is the introduction of a new level of divergence in the control flow. These are different concepts that the developer will have to deal with, in order to know when where uniformity holds. |
@dneto0 wait, you are saying these two constructs are not semantically equivalent? 😨 |
Here's a fun puzzle, in C: while (cond) {
if (cond2) {
stmtA();
break;
}
} Question: Is stmtA in the loop or not? It sure looks like it is. But look at the control flow graph and it's not; it's a common view among compiler writers (not just me) to recognize that stmtA is not in the loop. For a visual illustration, see @johnkslang's avatar. |
For a single threaded execution, there is no difference. If you force coders to write if (cond) { ..... ; break; } then you get two bad effects:
|
The way I (amateurishly) see it is that one could represent this with either a CFG where the |
It doesn't matter until you try to do a collective operation like a derivative or a subgroup operation (broadcast, reduction, ballot, etc.) Then you need to be a lot more careful. C doesn't need to tell you. SPIR-V (for Shader) tells you with the placement of the structured construct's merge block and the uniformity reconvergence rule. |
I understand the point you brought about uniform control flow of the |
I don’t think
I agree with @kvark that this makes sense, but because the |
@dneto0 - would the two constructs generate different SPIR-V? Would the SPIR-V -> WGSL converter generate |
So it sounds like the objection is that while |
A simple SPIR-V --> WGSL converter and simple WGSL --> SPIR-V converter would both generate different code. |
Discussed at the 2020-03-31 meeting. Resolution from that meeting was that we'd spend a week thinking about this and re-visit it. |
To serialize the thought I had from the meeting. If I understood correctly, I don't think the mental overhead is significant to determine that there's no uniformity concern for However I'd also be fine with keeping this, or with reordering it to |
I'm not entirely sure where we get the motivation to keep Is the semantics equivalent to Finally, from the practical standpoint, is it trivial for the shader compiler to turn one into another? Pretty much yes. So why are we still considering WebGPU still has to perform uniform control flow analysis and error in case anything is assuming uniformity (e.g. derivatives) when there is none, so it's not like if (xx) will introduce any hidden issues - the errors of this class would be triggered as early as at |
While having separate |
For something like (Example 1)...
...we naively get something like (Example 2)...
Because cond2_true doesn't branch back into top_of_loop, arguably stmtA() is outside the loop. The author could choose to use the semantic equivalent to make this more obvious: (Example 3)
FWIW I think the expressiveness isn't much worse as: (Example 4)
These show what one would actually want the original code to do, generating: (Example 5)
Unsurprisingly now stmtA is now clearly outside the loop. If uniform control flow enters the loop header, the loop's merge block will also be uniform. However, for something like... (Example 6)
...the author couldn't simply sink stmtA outside of the loop, as there are two exit paths from the loop, so we get almost exactly Example 2 instead of Example 5: (Example 7)
Given that as my understanding, I still think that requiring I can see how a clever compiler would, for Example 1, emit Example 5 instead of Example 2. FWIW, I think we should consider requiring Example 3 or 4 in order to get UCF guaranteed for stmtA as in Example 5. |
I found @dneto0's argument during the last call made sense to me. It seems valuable to allow people to clearly express their intention, even if there are other less-clear ways to express themselves. I'm now on the fence about this proposal: neither for it nor against it. The reason he didn't persuade me entirely is that it's not clear to me that authors are clamoring a way to express the idea presented above in this thread. I'm not sure how valuable it is to be able to express this particular concept, especially without also being able to somehow attach |
Do we have a limited budget for syntactic sugar? If yes, this doesn't seem like a good way to spend it. If our budget for syntactic sugar is not particularly limited, then the question naturally arises wether any statement BAR ought to have an |
Thanks everyone for the thoughtful considerations, given my dramatic pushback. :-) "Do we have a limited budget for syntactic sugar? " Is there ever a case where you want to do a break or continue as completely unconditional? (Setting aside for the moment that there may be other code that is also guarded by the same condition.) If a break is completely unconditional then it seems to me you would/should always rewrite the code in a simpler form in the first place. One suggestion I've heard is that you can avoid the stmtA confusion by rule: that break must appear alone in the braces: Why not have conditional-anything? E.g. Perl has I've been thinking about how I would describe "if" and "break if" to newcomers, keeping in mind uniformity consdierations Eg.
|
One more thought: This decision is not entirely independent. Having "break unless" makes for very idiomatic use of Where in C you'd have while (keep_going()) {
} You can have
|
There is no ambiguity about The confusion/trouble/trap begins because the syntax is highly suggestive that you can put other statements in with the break. |
Discussed at the 2020-04-14 meeting. |
During the meeting @litherum asked (more or less) how I would prefer to express the "stmtA" scenario. Let's say the C code fragment is:
Then my preference is for the developer to choose consciously where to put the cleanup code. First option:
Second option:
I like the style of the second option better. It clearly shows that the cleanup action is not part of the loop and should not be considered as taking up internal register space for the purposes of scheduling. |
The @jdashg proposal in #705 is a useful middle ground:
Yes, this can always be circumvented, e.g. |
|
Oh, oops, I didn't mean to ban unconditional break and continue, but you're right that I did! I can leave that one as is if you'd like, but my intent for that PR was purely additive. |
Just wanted to re-iterate in writing something I was saying on the calls.
There is no danger here that is immediately obvious to me. If they put something illegal, their shaders will stop building. This is true with and without "break if". |
This was discussed at the 2020-05-12 meeting. |
OpReturn | ||
OpReturnValue | ||
| if_stmt | ||
| unless_stmt |
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.
This seems like a separate change?
const a : i32 = 2; | ||
var i : i32 = 0; <1> | ||
loop { | ||
break if (i >= 4); |
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 lean towards the #705 solution here and we drop the {}
s in the limited cases.
Discussed at the 2020-05-19 meeting. |
It's already been stated that, if you write the "bad" code at #643 (comment) and It's worth considering the reverse: where Consider: for (...; condition(); ...) {
if (...) {
stmtA();
break;
}
...
} Now that we have multiple loop types, and I expect In this formulation, let's pretend that var exitPoint : i32 = 0;
for (...; condition(); ...) {
if (...) {
exitPoint = 1;
break;
}
...
}
if (exitPoint == 1) {
stmtA();
} This new code is worse than the original, for multiple reasons:
It's also worth noting that this argument holds even if there were no Therefore, neither |
Adding shorthand doesn't hinder authors. |
Adding shorthand doesn't hinder authors, but removing longhand (as some have suggested in this very PR thread) does. The shorthand also seems redundant, and of lesser value than other syntactic sugar that has been rejected by the group (such as I'm all for generously adding shorthand and syntactic sugar, but this should be applied consistently. I'm against removing the ability to do something between the |
We've talked about this internally and if we update this CL to put the We would then be depending on the uniformity analysis to make sure everything is correct which we think is fine. I'd suggest opening a new issue to make |
Discussed at the 2020-06-02 meeting. |
772a1bc
to
1c1f1f8
Compare
1c1f1f8
to
46ae574
Compare
This leaves
unless
unused, so it was also removed here.