-
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
Flesh out calls, indirect calls, and function pointers. #278
Conversation
This looks reasonable to me from a first glance. I'll try to review it again in depth later. FWIW, this does seem to conflict with multiple past assumptions and ideas about our expression trees. In the past we assumed assignments have a single target with an expression on the RHS. This introduces the idea of assignments to N targets, where right now we're constraining N to 0 or 1, but eventually it will be more.
We probably need a way to indicate that one or more of a function's return values are unused. This will be important for optimization and general codegen, I think. With return values as out-pointer parameters, this is relatively simple because you just pass a nullptr and that turns into an easily eliminated |
The idea with multiple return values (in FutureFeatures.md) is that they'd only be able to participate in expression trees if they have exactly one result (a mild extension of the existing rule that they can only participate in expression trees if they have exactly one user). Consequently, the only things that would have to accept multiple results would be special forms of |
Maybe we narrow this down and have a concept of 'most recent function return values'? So instead of (strawman):
something like:
Maybe we layer some validation rules onto this, like that retvals can only be used once, and accessing a retval that isn't currently valid traps. I think those can be trivially verified because they're sequential, as long as they don't cross a branch. Given a model like this, for a call with a single return value, you can easily decompose it:
->
This would potentially eliminate a lot of mostly-wasted local slots and eliminate the need for some liveness analysis on locals that are used once. That's probably good. It also makes it easier to tell whether a given return value is ever used. |
As we discussed on IRC, let's move the discussion of how to efficiently encode multiple return values into a different PR, because it's an interesting but separate topic. |
Nice work capturing all of this. |
|
||
* `void`: no 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.
I'd like this document to explain why there's no void type. I'd also like to understand what a function does return if it doesn't return anything in source form. Maybe this can be in the "calls" section, when you explain that a function can return 0 elements?
I'd suggest:
@titzer What are the open issues you're thinking about? |
I agree with Luke's suggestion that function ids ought to be typed. What is the motivation for having runtime signature checks instead? |
Can we move discussions to separate issues instead of this PR, as discussed in #282? I think we have a few forks to make :-) |
@jfbastien This isn't a diff; it's the main conversation of a PR and thus won't disappear if the branch is updated which was the problem identified by #282. |
On further thought, I'm realizing that there is a lot more nuance to func-ptr types: The big question is: what happens if you The other option is to have a non-faulting Perhaps this is the same "need more impl experience" situation you were thinking about @titzer? |
On Fri, Aug 14, 2015 at 12:03 PM, Luke Wagner notifications@github.com
Yeah, I think we can move quickly to get some experience if we take one of a.) punt: have the producer generate their own dispatcher routines, just to Those are in rough order of personal palatability. The check in c.) could The advantage of fully typed funcptrs I see here is that dispatch is 1-2 Obviously, any of the strategies above could be sped up with an IC
|
On Fri, Aug 14, 2015 at 12:41 PM, Ben L. Titzer titzer@google.com wrote:
Also, long-term I think we will want to have typed function pointers that
|
True, but couldn't C++ vtables still benefit, as well as dispatch mechanisms in many other languages? (the overall workflow of how wasm code will use The C compiler might be able to help too. At least as a global optimizaton, it could use knowledge about whether "garbage" was casted to a function pointer type or not, and generate slower code if yes. |
@lukewagner Imho, that's not questionably legal, that's seriously illegal. I don't want to penalize everyone just to optimize for that case. C compilers wishing to support that can always just be conservative. If we agree that signature-parameterized types are something we'll be ok with long-term anyway, what are the downsides to just doing that in the MVP? We can figure out the vtable/etc. optimization parts after the MVP. |
@sunfishcode It doesn't matter if it's illegal if many codebases do it and expects it to work. (E.g., SpiderMonkey does this all over the place via @titzer Interestingly, even if (b) is spec'd, real code depends on functions having unique addresses across different signatures. This was enough of a problem in practice that Emscripten has a workaround flag that manually pads the tables in the same manner described above. The difference is that this padding (and memory usage) gets baked into the semantics, instead of being an internal optimization. Agreed on eventually wanting func-ptr types as GC object field types for implementing vtables; maybe that's reason enough to add func-ptr types now? |
@lukewagner |
There is also The alias analysis necessary to use |
(JS_FUNC_TO_DATA_PTR is for putting a function pointer in an Object*, and JS_DATA_TO_FUNC_PTR is for getting it out again. From a quick scan through SpiderMonkey, I don't think they're ever used in the reverse to put arbitrary bits into a function pointer type.) Making placement of Overall, what is the overall feeling here? Are people interested in having signature-parameterized |
@sunfishcode Assuming the heap is much larger than the func-ptr table, casting an Object* to a func-ptr might as well be an arbitrary integer: both are invalid func-ptr indices and will spuriously fault if we have faulting
This question seems to boil down to: is there any perf win from |
The function pointer type and its operations (conversions, comparisons) were previously only implied. This patch documents them like regular types and operations. It also lays the groundwork for a future with multiple-return-value functions. This is done by eliminating the `void` type and defining return types as a sequence. In the MVP, the sequence can have at most one value.
This replaces the `void` type with the concept of a return type sequence which may be empty, and it defines *signature*. This brings in the parts of #278 not connected to function pointers.
The function pointer type and its operations (conversions, comparisons)
were previously only implied. This patch documents them like regular
types and operations.
It also lays the groundwork for a future with multiple-return-value
functions. This is done by eliminating the
void
type and definingreturn types as a sequence. In the MVP, the sequence can have at most
one value.