- Where: Virtual meeting
- When: May 20, 16:00-17:00 UTC (9am-10am PDT, 18:00-19:00 CEST)
- Location: link on W3C calendar or Google Calendar invitation
No registration is required for VC meetings. The meeting is open to CG members only.
- Opening
- Proposals and discussions
- Vote: Custom Descriptors and JS Interop to phase 2 (Thomas Lively, 45 minutes)
- Closure
None
- Conrad Watt
- Derek Schuff
- Sean Jensen-Grey
- Sébastien Doeraene
- Daniel Lehmann
- Erik Rose
- Michael Ficarra
- Sean Isom
- Oscar Spencer
- Colin Murphy
- Shu-yu Guo
- Robin Freyler
- Andreas Rossberg
- Paolo Severini
- Yury Delendik
- Benjamin Titzer
- Andrew Brown
- Rahul Chaphalkar
- Jakob Kummerow
- Yan Chen
- Matthias Liedtke
- Sam Clegg
- Jeff Charles
- Brendan Dahl
- Ryan Hunt
- Emanuel Ziegler
- Alex Crichton
- Ricky Vetter
- Sehyo Chang
- Deepti Gandluri
- Johnnie Birch
- Nuno Pereira
- David Degazio
- Dean
- Thomas Trenner
- JJ
- Julien Pages
- Luke Wagner
- Yuri Iozzelli
Vote: Custom Descriptors and JS Interop to phase 2 (Thomas Lively, 45 minutes)
TL presenting slides
CW: should I now think of wasm objects with custom descriptors as having one more indirection to get to the final RTT?
TL: the hope is that fast-path optimizations can remove that, and in the long term, we would hope that there won’t be an extra layer. But today in the V8 prototype there is, with some optimization
JK (chat): Conrad: the indirection is the other way round though: the object points at its RTT, which points at the Custom Descriptor
CW: TL, this is different from what the diagram shows [in the slides]?
TL: yes
JK (chat): the mental model is that the Custom Descriptor and the actual RTT are one thing. Whether they're actually represented in memory as one object or as two (or even more) objects is an implementation detail.
DD (chat): @Jacob Kummerow is the intent that to implement this without a double-indirection, you'd allocate the custom descriptor and RTT in one contiguous block?
BT(chat): AFAIU the main intent is to avoid copying the supertype vector per descriptor
JK (chat): my intent is to keep the split we have for the foreseeable future :)
But it would be conceivable to merge the two objects into one. That's probably more robust than ensuring that they're allocated right behind each other.
TL: (slides)
CW: When we talked about this in Feb, there were 2 kinds of casts: the one comparing pointers and the other more clever and semantic. Is that still the case?
TL: not sure what you mean, I thought this was everything. In userspace, I think there will be much more interesting casts that will be built, to implement nominal types. But this is it for core wasm.
SD: isn’t there something about struct.new?
TL: yes we have struct.new and struct.new_default today. We aren’t adding new ones but augmenting those. When you do those on a struct type with a descriptor, you have to provide the descriptor at allocation time. To avoid soundness issues, those new operands must be exact references to the right descriptor types.
TL: JS API slides
AR: to wasm, this proto slot on the scriptor looks like a regular slot field, but the engine will treat it differently?
TL: yes, it could be any externref, but it all comes together only if I install a DescriptorOptions on the first field.
AR: aren;t prototype fields represented differently in V8/ does that require a dynamic check on access?
TL: no, when we allocate the vtable and pass the externref in, it knows that we’re allocating a custom descriptor, it will look to see that the first field is an immutable externref, look at the value, and see if its’ a DescriptorOptions, then copy the prototype from there to the prototype slot on the map. At runtime when you call, the prototype slot on the map has already been filled out, no dynamic check or lookups.
AR: if i do a struct.get in wasm on the proto field, it doesn’t know if it’s a proper proto in advance.
TL: if you do a struct.get on it, you just get the externref. The prototype is stored on the normal field 0, and also copied to the internal slot.
JK (chat): Andreas: we do have a more efficient design idea that doesn't need that "weird" $proto field. Stay tuned :)
TL: Declarative Custom Section slides
AR: on the function ABI: you say it gets the receiver as the first param, but that’s not usually the case for web exported wasm functions? They get a null receiver. How does that match?
TL: The custom section isn’t doing anything that you can’t do in handwritten code. Slide 34 shows it. Yes the wrapper is important.
TL: JS Interop Questions slide
SC: did you run experiments to see if the JS really was slow and the binary format is needed?
TL: yes, coming up….
TL: slides 37-40
AR: Are all these options still polyfillable? Maybe the direct ones aren't?
TL: you mean, given capabilities today, with wrapper objects? Today you could create wrapper objects with prototypes that forward to wasm functions. The first 4 are just optimizations over that approach, polyfillable. The direct ones… if you wanted to polyfill, you’d have to parse and rewrite the binary, and then use the polyfill from the first 4. So everything is polyfillable if you try hard enough 🙂
RH: in the direct approach you could have a function def in the module, with no export references it, but because of the custom section you could get a function ref that flows out to JS. that’s the semantic abstraction-punching that you’re talking about.
TL: yes exactly.
AR: since you rewrite the exports, i would argue that that’s not polyfillable.
TL: yes, drawing the line at rewriting the module, doesn’t count as polyfilable, is a reasonable definition.
AR: yeah, keeping it internal is a grey area, but rewriting the external interface seems over the line.
JK: (chat): with today's Wasm, none of these (well, except "Baseline") are polyfillable, because you have no prototypes on Wasm objects
MF (chat): what happens if you setPrototypeOf on a wasm object today?
SD (chat): TypeError, it's frozen from the JS point of view
SD: I agree that none of it is really polyfillable today. Unless you proxy everything, but you’d have to do that to all the ways wasm objects can flow into JS and maintain identities, etc. so for all practical purposes it’s not polifillable.
AR: yeah I guess I was thinking about polyfilling with the imperative API.
TL: yeah you could remove the custom sections and replace with the JS code.
JK: One way to think of the direct approach is that it introduces an extension to the export section. So technically it punches through abstractions, but from the POV of e.g. a tool that needs to know what is exported, fundamentally it doesn’t change if you just have to look in 2 places. This is also why the wire byte size doesn’t change much. It has much of the same info that's in the export section, just encoded in a different way. A large part of the performance benefit is that we don’t build up a big JS object that has all that information. So extending the export section directly would lose that. Also it’s the only one that can be faster than baseline.
TL: We also had an idea from SD to mitigate some of the overhead of attaching all the exports to the export object. For even the modular custom section approaches, if the custom descriptor is used as a prototype, we can avoid putting it on the export object.
JK: one other thing to mention, none of these results allocate the wrapper function, they assume we get a feature where we can just stamp the functions with receivers. If we had to allocate these tiny functions, they would be even slower. Also. with the protofield on the custom descriptors from core wasm perspective it doesn’t make sense, you just carry around a useless externref field, youd never do any struct gets or anything. With the direct approach we don’t need that field at all, the association is maintained by the engine. The result is that if you have a core wasm deployment, you ignore the custom section, then the wasm module is just what you would expect from a core wasm perspective with no weird unused fields. So yeah it’s a little abstraction punching, but it has the flip side that if you don’t care about JS interop, it leaves you with a nicer thing
TL: but having that field gives you more flexibility. You can create them at runtime as long as you have a DescriptorOptions you can set it up. But if you’re using this distinguished values, …. The direct custom sections only support instantiation time setup of globals. If you want to set them up dynamically you’d have to do something else.
TL: slides 42-46
CW: Do we have any sense from other implementers what they think?
RH: We are interested in this, closes an expressivity gap. Not necessarily as interested in the custom descriptor part for saving space, but if it is required for JS interop, then that's fine. But we care about the JS interop part especially. No implementation plans yet but we’ll figure something out as we go.
DD: I think this is something that we haven’t put a ton of planning implementation for, but discussed. We’re broadly in favor, it seems useful. We need to talk about some of the JS API stuff internally. But also there seems like there’s some double indirections happening, there are lots of different ways you could implement, we would like to see multiple people have a chance to implement and explore what’s possible there before it goes too far forward. But we think it’s generally good.
TL: yeah the question of whether the interdirection has performance downside is something we’d hope to learn more about in an origin trial.
CW: What about non-web? Is there any expectation that it could be useful for that?
AC: I don't know of use cases yet, we still have more work to do to make GC work. No specific concerns though.
TL: some more data on core Wasm part. We measured the potential memory savings for Sheets in V8 for just custom descriptors, it looked like 9-10% of the wasm heap size can be saved.
AR: there are use cases that are unrelated to JS, elg. You can move static fields from the object to the descriptor, e.g. if you implement Java you can move the VTable from the instance to the descriptor, that ill save memory. So that looks useful even if you don’t care about the JS interop part.
CW: We should move to the vote. Any comments related to that?
Let’s go ahead with the wording on the slide. Standard 5-way poll
| SF | 9 |
| F | 13 |
| N | 4 |
| A | 0 |
| SA | 0 |
Poll passes
