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
proposal: cmd/compile: relax wasm32 function import signature type constraints #66984
Comments
"This follows the C struct value semantics" is just a hair vague; are 8-byte quantities (float64, int64, uint64) stored at a 4-byte or 8-byte alignment? It was my understanding (and the purpose of #66408) to specify a (edited to note error, the host alignment for 8-byte integers and floats is 8 bytes). |
Ideally 8-byte values would always be 8-byte aligned in the |
@dr2chase Looking at what clang does, it uses 8-byte alignment on 64bit quantities so we'd match that. |
You are right, I got it backwards. But that is what you are expecting for anything that has pointers-to-it passed to the wasm host platform, yes? |
Thanks for the proposal! A few questions:
Besides, for structs, arrays of structs, and pointer to structs, I would suggest we allow only structs with Thanks. |
Two other questions, first:
Is this laid out Second, passing pointers to 8-byte primitive types to the host will be tricky unless those references come from fields in structures tagged with HostLayout -- otherwise, they may not be aligned. So
|
Thanks for the quick feedback! I've tried to answer each question:
The specification falls out of the table of transformations (I think?). There current plan isn't to introduce any sort of magic around large structs or field packing. Structs fields are added as call parameters, from the first field to the last, according to the conversion rules for the type of the field. Examples: type foo struct {
a int
b string
c [2]float32
} With a function signature of //go:wasmimport some_module some_function
func wasmFunc(in foo) int Would roughly translate to (in WAT format) // $a is of type `i32` holding the value of `a`
// $b_addr is of type `i32` and is a pointer to the start of the bytes for the Go string `b`
// $b_len is of type `i32` and is the length in bytes to read from `$b_addr` to get the whole string
// $c_0 is of type `f32` and is the value of `c[0]`
// $c_1 is of type `f32` and is the value of `c[1]`
call $some_function (local.get $a) (local.get $b_addr) (local.get $b_len) (local.get $c_0) (local.get $c_1) Struct fields would be expanded into call parameters before subsequent fields at the same level.
For
This sounds like a great idea, and we should also extend it to pointers to 8 byte sized primitive types to guarantee alignment, as suggested by @dr2chase's last question. This would avoid any question around alignment issues for pointers. It hurts the ergonomics a little bit but that's a price worth paying, I think.
I'm a little confused by the question to be honest. If this type was used as an input to a Wasm call, it would look like this: // $a is of type `i32`
// $b is of type `i32`
call $some_function (local.get $a) (local.get $b) I suppose that might mean the memory looks like this: |
Thanks for the response!
This sounds like a reasonable choice. Is this ABI specified anywhere in Wasm/WASI docs? Or the Wasm side has to define the function taking parameters element-wise?
This sounds reasonable as well. Is it specified anywhere in Wasm/WASI docs? Thanks. |
I don't know about this being an official ABI so much as just a consequence of the Wasm spec around function calls and how we can apply Go semantics to it. We're limited to the
Not sure there's a doc anywhere, but practically, definitions like |
Background
#59149 removed the package restrictions on the use of
go:wasmimport
, but established strict constraints on the types that can be used as input and result parameters. The motivation for this was that supporting rich types between the host and the client would require sophisticated and expensive runtime type conversions because of the mismatch between the 64 bit architecture of the client and the 32 bit architecture of the host.With the upcoming 32 bit wasm port, this problem goes away, as both client and host will use 32 bit pointers.
Proposal
Relax the constraints on types that can be used as input and result parameters with the
go:wasmimport
compiler directive, on ports using thewasm32
architecture only. This currently limits this proposal towasip1/wasm32
.The following types would be allowed as input parameters:
bool
int
,uint
,int8
,uint8
,int16
,uint16
,int32
,uint32
,int64
,uint64
float32
,float64
string
struct
where all fields are allowed types[...]T
array whereT
is an allowed typeuintptr
,unsafe.Pointer
,*T
whereT
is an allowed typeThe following types would remain disallowed:
chan T
complex64
,complex128
func
interface
map[T]U
[]T
Only simple scalar types (
bool
,(u|)int(|8|16|32|64)
,float(32|64)
,uintptr
,unsafe.Pointer
) would be allowed as the result parameter type.Discussion
Compatibility guarantees
The Go spec does not specify the struct layout and leaves it up to implementations to decide. As such, we cannot provide a guaranteed ABI without having to change the spec or force future layout changes to provide runtime conversion of data. This proposal suggests making it clear to users through documentation that there are no guarantees of compatibility across versions of the Go compiler.
Type conversion rules
The following conversion rules would be automatically applied by the compiler for the respective parameter type:
bool
i32
i32
int, uint, int8, uint8, int16, uint16, int32, uint32
int64, uint64
i32, i32, i32, i32, i32, i32, i32, i32
i64, i64
i32, i32, i32, i32, i32, i32, i32, i32
i64, i64
float32, float64
f32, f64
f32, f64
string
(i32, i32)
tuple of (pointer, len).struct
[...]T
uintptr
,unsafe.Pointer
,*T
i32, i32, i32
i32, i32
, N/AResult parameters
Result parameters are more restricted since pointer values from the host cannot be managed safely by the GC, and Wasm practically does not allow more than 1 result parameter. Only basic scalar values and
unsafe.Pointer
are allowed as the result parameter type.Supporting slices, maps
Both slices and maps are disallowed because of the uncertainty around the memory underlying these types and interactions with struct and array rules. Users who wish to use slices can manually use
(&slice, len(slice))
orunsafe.Pointer
. There is no clear way to support passing or returning map data from the host other than by usingunsafe.Pointer
and making assumptions about the underlying data.Related proposals
struct.Hostlayout
#66408 proposes a way for users to request that struct layout is host compatible. It does not create a conflict with the ideas put forth in this proposal.
go:wasmexport
The proposed relaxing of constraints would also apply to uses of go:wasmexport, as described in #65199.
Future work
WASI Preview 2 (AKA WASI 0.2)
WASI Preview 2 defines its API in terms of the Component Model, with a rich type system and an IDL language, WIT. The Component Model also defines a Canonical ABI with a specification for lifting and lowering Component Model types into and out of linear memory. This proposal does not attempt to define the ABI for any hypothetical wasip2 target, and would leave such decisions for any future wasip2 proposal.
Contributors
@johanbrandhorst, @evanphx, @achille-roussel, @dgryski, @ydnar
CC @cherrymui @golang/wasm
The text was updated successfully, but these errors were encountered: