WebAssembly / WASI

WebAssembly System Interface
Other
4.86k stars 253 forks source link

Add a new "abi" which supports the full type grammar #422

Closed alexcrichton closed 1 year ago

alexcrichton commented 3 years ago

This commit adds a new Abi variant called Abi::Next. The purpose of this new ABI is to support the full expressivity of the type grammar, making validation much simpler since there generally doesn't need to be a ton of validation. The goal of this ABI is to have the next WASI snapshot move to it. Old WASI snapshots will never use this ABI because it's a breaking change from the existing WASI ABI.

At a syntactical level functions are specified with the old ABI as:

(@interface func (export "foo") ...)

whereas the new ABI is recognized as:

(export "foo" (func ...))

This commit also prepares the next wasi snapshot to rely far less on @witx and custom types which are unlikely to be in interface types. To that end a few changes are made:

The new ABI doesn't have a ton of documentation yet but I hope to write that up in the future as necessary. For now the details can be mostly glossed over since code generators are just receiving primitive instructions to implement and the details of the ABI are all handled by this crate. At a high level though the ABI is:

The intention of this ABI is to be a sort of "canonical ABI" for interface types. This enables WASI (and everything else using witx) to use the full type grammar as intended by interface types while assigning meaning to what the host/wasm need to do to communicate with each other. In the limit interface types will allow each module to customize its precise ABI, but for now this gives the ability to today have modules start communicating while we wait for the customization pieces to all fall in place.

Currently I have not changed the ephemeral snapshot to use the new ABI, but depending on feedback on this that's the next thing I'd like to do. That should enable the ephemeral snapshot to have access to a much more rich type grammar than what it has access to today. Furthermore I'd also like to eventually "backport" the {in,out}-buffer types to the previous ABI in a simple fashion (just a pointer/length) to ideally remove the need for @witx if possible. I haven't attempted to do this yet.

jedisct1 commented 3 years ago

When using the new ABI here:

(typename $mystruct (record (field $member1 u8) (field $member2 u8) (field $member3 u8)))
(export "xyz" (func (param $a $mystruct) (param $b $mystruct) (result $error (error $errno))))

wasm_signature(CallMode::DefinedImport) returns 7 input parameters for the function signature. Is that expected? Even if the structure is flattened, this doesn't match the offsets of the members.

alexcrichton commented 3 years ago

@jedisct1 ah yeah that's expected, the record-by-value-parameter is "splatted" into its flattened form, so each of the two arguments takes up 3 literal parameter values each in the wasm signature. The return value is also represented as multiple values, however, and because C/Rust aren't super great about multi-value returns today that's represented as a return pointer. This means that in all it turns out as 7 parameters (3 for first arg, 3 for second, 1 for ret pointer)

jedisct1 commented 3 years ago

Thanks for clarifying, and for your amazing work on this, Alex!

The ret pointer totally makes sense. Multi-return values also require tuples in Zig, Swift and AssemblyScript, and having a single pointer is more convenient than the previous ABI.

However, the flattening of structures in function parameters is quite of a massive change. Does that mean that we will always need two completely different representations for the same type, even if properly padded structures are already used internally? That seems to make everything more complicated, including language support for using imported functions.

That flattening is certainly necessary. But if there is a way to avoid such a departure from the previous ABI and still use pointers instead, that would be immensely useful to ease the work of people maintaining languages, code generators and runtimes.

alexcrichton commented 3 years ago

It's definitely not intended to have two representations of the same type. The intention is that code generators would be based on this crate which abstracts away all the details of the ABI. This helps ensure that code generators all agree with one another and there's only one place that actually defines the ABI (this crate). In that sense the intention is to solve the problems you're mentioning, not create new problems.

This will require code generators to migrate to using this crate and the ABI definitions, which is a large change, but it's expected that the general amount of maintenance afterwards is no different from today.

jedisct1 commented 3 years ago

Thanks Alex,

Only code generators in Rust can use this crate :(

The two representations I was referring to is the fact that when using this crate, offsets returned by member_layout() don't match the splatted representation for function parameters.

Should these offsets be ignored and the splatted representation is always the correct one to store data as?

alexcrichton commented 3 years ago

Yes that's true, if you don't want to write Rust code you won't be able to use the crate. The intention is that this ABI is documented/canonicalized in documentation (like the wasm spec) so if other implementations would like to rebuild everything there's still a shared specification of what to do.

For the two representations you're talking about, I'm not sure what you mean. (sorry I haven't been sure for the past few comments but haven't addressed this point specifically). When a struct is passed as a parameter each of its fields recursively get expanded into individual function arguments. This has nothing to do with memory/layout/etc since nothing is stored in memory, everything is passed as function parameters and such.

Does that answer your question? Sorry I'm not entirely sure because the "splat to arguments" is not intended to be a representation, it's just an implementation detail of how you call a function with a struct argument

alexcrichton commented 3 years ago

This is now more formally specified at https://github.com/WebAssembly/interface-types/pull/132 in the context of interface types. The intention is that once that's settled this will be updated to match the specification there!

sunfishcode commented 1 year ago

This work has since been subsumed by the Canonical ABI and associated tooling.