-
Notifications
You must be signed in to change notification settings - Fork 695
Have br_if return its value #703
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
This PR should not be bundling in a controversial change discussed in length elsewhere, and it should not be bundling in the Expressions fit some restricted but common patterns. They do not address the general cases of passing around values. I see two different paths to addressing the general cases:
I just don't see a plan or the stats to evaluate these changes? |
@@ -243,6 +242,7 @@ of the arguments passed to the function. | |||
|
|||
* `get_local`: read the current value of a local variable | |||
* `set_local`: set the current value of a local variable | |||
* `tee_local`: like `set_local`, but also returns the set value |
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.
tee_local
- I'm guessing this is meant to be in a different PR? Edit - sorry for the noise, I see it's "based on #694"
@lukewagner The points you've made make sense, but if |
I agree with this PR (as discussed previously). Can we separate out just the |
@@ -285,13 +284,16 @@ before any others. | |||
|
|||
### Yielding values from control constructs | |||
|
|||
The `nop`, `if`, `br`, `br_if`, and `return` constructs do not yield values. | |||
The `nop`, `br`, `br_if`, `br_table`, and `return` constructs do not yield values. |
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.
Should drop br_if
from this sentence.
It might be worth gathering some data first. I assume that the case of actually using the return value is not very common, such that the majority of uses of |
I only based on #694 to avoid the pending merge conflict (assuming people would click "Commits" and then look at the one new commit), but happy to rebase to master if that makes it simpler for people. |
(Sorry, GH shenanigans when I "rebased" by recreating a same-named branch) |
At a high level, I don't think we should be worried about the extra Measuring the AngryBots demo right now (which is 12M bytes), there are 32K So overall I don't think we need to worry about code size issues here. This mostly feels like a symmetry/regularity fix. |
@lukewagner Without a plan for multiple values it is surely not possible to judge 'symmetry/regularity'. An 'arity' count has been added, so lets say there are three values to pass to the target, and lets say that the target and the fall-through need different sets, then what?? There is just no plan here.
|
Honestly, I'd be very surprised if even 10% of br_if's with value wanted to pass that value to both their continuations. The two continuations are highly asymmetric! The usual pattern for br_if is to occur statement-like in the middle of some block:
If the target requires a value, the operator will take an extra argument. But that's almost always entirely independent from the local context. In fact, it would be rather irregular if you had to wrap br_if into a drop only because the target takes an argument -- to see how weird that is, just imagine we did something analogous with calls (somehow necessitating a drop if a call takes arguments). Also, we should avoid a proliferation of duplicated void/non-void operators. If that happened then I'd regard explicit drop semantics a failure. ;) So for most relevant operators, we should pick the semantics that matches the majority use case (instead of penalizing it). We decided to do so for stores, why choose differently here? |
@kripken was actually making a similar point (having performed an optimization in Binaryen that used branch values to optimize branches to join points); still waiting to see what % of those branch-with-values were Really what motivates me here is symmetry (why pop the already-computed-and-pushed value only along one path?), not size wins. But, talking about this more, there is also a symmetry with |
@lukewagner, fully agreed. |
To me, |
It seems symmetric to me to have it produce a value in both situations, but if it means drops in the common case, then it feels awkward somehow. |
@sunfishcode The value might not even be live in the fall-through path, this is just the reality of the data flow and control flow - I just don't see this 'symmetric' argument. There might be a lot of definitions on the values stack that are discarded if the branch is taken, and these are values 'that transmits values along one edge and not along the other edge', and I don't see people complaining about this not being 'symmetric'. |
I definitely agree it's symmetric to think of But here's the thing, I don't think we even know what the common case is yet. In previous discussions it had sounded like there were a bunch of We could be conservative here by simply requiring the arity of |
Yes, the 4% figure was for return value optimizations in general, which includes So I agree that we don't know yet for sure if returning the value or not is the more efficient thing to do. I can look into doing more work on the binaryen optimizer for this very soon, so if we want to wait on data it shouldn't be long. However, I believe @rossberg-chromium is right, and not returning the value is the more efficient form (similar to
|
It'd be useful to see how often
That makes sense for |
@lukewagner: Sorry, maybe I wasn't clear: Whereas if the value is returned, it could correspond to two phis, one on the branch target and one on the fallthrough block after the branch, where both phis happen to have the exact same value. |
@kripken It will not create a phi on the fall-through because it is not a control flow merge point. In this respect the paths are not symmetric. http://ssabook.gforge.inria.fr/latest/book.pdf |
I analyzed all I agree with what others said that more data could be useful here. Overall, I think it's fair to say we don't know for sure if having a return value or not is the best choice, even if the data so far does show not having a return value is better. So I guess we could either work on gathering more data now, or defer the decision, by not allowing I'm ok to defer since we'd be fine for the MVP: if it would be important to have a specific |
I also did a similar analysis on libc++ but on the wasm backend, here is what it looks like after binaryen's block return value opts: 1249 Most of those differences from asm2wasm, and that there are more The values returned from |
Closing in favor of #709. |
Currently, AstSemantics.md and ml-proto say that
br_if
doesn't return a value. However, V8 and SM wasm impls currently both havebr_if
return its value (mostly as an oversight, but also b/c that's what happens with an stack-based iterative algorithm unless you take special steps to pop and void the value). I thinkbr_if
-returning its value is more natural for a couple of reasons:br_if
to conditionally pop the valuebr_if
to pass along its value to the parent expression(This PR is currently based on #694.)