-
Notifications
You must be signed in to change notification settings - Fork 696
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
Add a block1 operator that returns the values of the first expression and discards the rest. #650
Comments
It wouldn't be difficult to use this from LLVM, however it's limited to single-use values, and there are limitations on what order things can happen in, so I wouldn't expect it to make a big difference. Also, I'm generally disinclined with multiple-value designs that require tuple types on the value stack, because that requires additional linguistic support for constructing and deconstructing tuples, and ultimately, tuples aren't a concept we want to emphasize in wasm, in general, because it's a low-level language. This is why I've been investigating the approach of expanding multiple-value results into one value per stack entry, an experiment which has so far had mixed results. The obvious alternative is to just have instructions that define extra results by setting local variables, which is conceptually very simple. |
@sunfishcode Thank you for the feedback. The thought was that I am also 'disinclined with multiple-value designs that require tuple types on the value stack', which seems to be where CIL has ended up (using a struct) and perhaps by not planning ahead. But what characteristics make a multiple-value design 'require tuple types on the value stack'? So long as it works in a functional programming style it seems possible to avoid having a tuple on the values stack. So long as the tuple can not be stored in local variables and it is not possible to destructively modify it without creating a new tuple. Do you have any other characteristics in mind? Please consider the wip proposal at WebAssembly/wabt#66 which compiles the wasm to a very simple stack machine code with one value per value stack entry, and does validation and compilation in a single pass and does it very cleanly using the pre-order encoding. I am building it up to have a range of values operators that fit the type system and avoid a tuple/struct object, if only to demonstrate the design space. It does need to track the multiple values types during compilation but these could all be unbundled in the first pass while decoding and validating and doing the SSA conversion. Or are you saying that you don't support multiple value support in wasm? That you don't support expressions having multiple values? I agree they are not necessary, but neither are single value expressions. But I would still like support for returning multiple values from functions even if the caller needs to immediately destructure them into local variables. But then efficient code generation is very dependant on the runtime compilers being able to optimize local variable copying, and wasm might then just use a register based instruction set and avoid expressions entirely. I understand you are exploring post-order encoding, and it might be interesting that with the pre-order encoding this compiler knows the end of each block top-level expression just after reading it so can emit the code as it goes, and with the block expression count knows which expressions discard values so can discard after each expression. With a post-order encoding I expect the code generation will need to be delayed until the end of each block and such a compiler would be significantly more complex (this practical wasm compilers will emit code in a second pass so this is not a significant matter). |
I think everyone agrees that returning multiple values from calls is valuable functionality. The question is how to do it. I think the Also, I don't expect it will be complex to write a post-order one-pass code generator. There are limitations on how much optimization can be done this way, but it won't need to wait until the end of a block. |
@sunfishcode Sorry I am getting a little lost, could you please point me to 'the call_multiple idea discussed in other issues'? I was aware of a prior multiple-value proposal in the ml-proto, which was removed in WebAssembly/spec#53 but it extended I have revised the description of my proposal and if some area of 'how it interacts with other language feature' is not clear then please let me know and I will clarify this too? Also do you have something concrete in mind for a 'post-order one-pass code generator.' While I can certainly imagine an code generator it is not obvious to me how it could generate code anywhere near competitive with the pre-order one-pass code generator, so if you have some insights I would really appreciate some help understanding this? In particular, it seems to me that this interpreter would need to emulate the expression stack of the post-order decoder, that is keep a stack of expression results at runtime and could only discard at the end of a block? Also for multiple-value expressions it would need to explicitly represent them on the expression stack, and even without multiple-value expression it would already need to represent zero-values (void) on the expression stack, whereas the pre-order one pass compiler unbundles them and compiles to a stack machine. |
The proposed operator seems rather ad-hoc and limited to specific cases. If we want support for more flexible local data flow then it would be much more useful and general to simply introduce a simple let construct. |
@rossberg-chromium Yes, I have suggest let too, but it was not well received. Another thought was that perhaps special local variables could solve the problem in another more general way, and I tried to put some thoughts together in #653 but clearly it's a little beyond me at present. For example if there were local variables that could only be assigned once per block and were not live beyond the end of a block then these might be light weight temps, but probably someone with a good command of the SSA etc could come up with something much more general and useful. The multiple-value expressions and block1 support is trying to solve a problem of efficiently passing around values, but perhaps there is another solution. |
We have talked about let a few times, and there's some sympathy, but it's clearly a post-MVP matter. |
@rossberg-chromium I don't see it as a post-MVP issue, rather the core scaffolding of the language and there should be a plan and implementations from the start. The objections to The use of
It's not really needed as local variables could be used. In the following the parent accesses the values via
The issue then is if the SSA decoder could be more efficient in passing around these values. It leads to considering a substantial design change, dropping the expressions and having all operators read/write to local variables. Doing so would also clear the |
I've been meaning to say, this feature strikes me as useful in any situation where "destructor" or "finally" logic has to run:
Once inlining is done, this will often happen inside functions, not just at the edges. Plus, a compiler that optimizes for size it might find more ways to use Where did the discussion of |
@qwertie For the discussion and objections to While I am exploring some ideas that could still bias a register based encoding towards efficiently encoding the expression patterns. For example specialized unary opcodes that read and write to the last register written and binary opcodes that read from the last register written and the register before and write to the register before (just as if there were an expression value stack in the registers). This might just be another crazy idea, but something like this might bias wasm to efficiently encoding expressions and encourage developers to emit code in this pattern, and this might help presenting code as expressions. |
Thanks, but something called " |
@qwertie See 'block scoped variables' aka |
The
block
operator returns the values of the last expression but it is a common pattern to need to return values from other expressions in a block and this current requires the use of local variables. Adding ablock1
operator that returns the values of the first expression would allow these pattern to be expressed as expressions without using local variables. The combination ofblock
andblock1
allows the results of an arbitrary expression to be returned.For example to return the values of the second expression in an effective block of expressions the following is currently required:
With a
block1
operator this could be:When the result expression has multiple values, using local variables in this pattern becomes even more verbose making
block1
more compelling.@sunfishcode Can you see an obvious way for the llvm backend to exploit this?
The text was updated successfully, but these errors were encountered: