Skip to content

cmd/compile: create GOARCH=wasm32 #63131

Open
@johanbrandhorst

Description

@johanbrandhorst

Background

The GOARCH=wasm architecture was first introduced alongside GOOS=js in 2019. The choice was made to use a 64 bit architecture because of WebAssembly’s (Wasm) native support for 64 bit integer types and the existing Wasm proposal to introduce a 64 bit memory address space, as detailed in the original Go Wasm design doc. The assumption at the time was that the Wasm ecosystem was going to switch to a 64 bit address space over time, and that Go would benefit from having used a 64 bit architecture from the start.

On the browser side, Firefox and Chrome both have experimental support for 64 bit memory, and it seems poised to become stable in the coming year. However, on the server side, all existing hosts use a 32 bit architecture, and it has become clear that it is here to stay.

In most uses of Wasm with Go, the architecture is transparent to the user, but with the introduction of go:wasmimport in #38248 and #59149, knowing the memory layout of input and output parameters becomes very important, as described in #59156. A short term solution of restricting the types of input and output parameters to scalar types and unsafe.Pointer was adopted to make this clear to users, but this user experience is not a desirable long term solution.

As an example, the fd_write import from wasi_snapshot_preview1 accepts an *iovec of this type:

type iovec struct {
    buf *byte
    len uint32
}

Because GOARCH=wasm has 64 bit pointers we have currently defined this type as:

type iovec struct {
    buf uint32
    len uint32
}

which gives the correct alignment but has issues:

  • The conversion from *byte to uint32 causes the compiler to lose track of the pointer, potentially causing the GC to reclaim the memory before the imported function is called. It requires using runtime.KeepAlive on inner pointers passed to imported function calls to ensure that they remain alive.

  • It creates a poor and error prone user experience, as the conversion of the pointer type looks like this:

    iov := &iovec{
        buf: uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buf)))),
        len: uint32(len(buf)),
    }
    
    

This issue is already affecting early adopters of the wasip1 port: Fastly SDK.

Performance

A 32 bit architecture would allow the compiled Go code to be more performant, due to the effects of locality, the larger size of certain structs and the difficulty of avoiding bounds checking. It would also use less memory.

Proposal

Create a new GOARCH=wasm32 architecture which uses 32 bit pointers and integers. The architecture would only be supported in a new wasip1/wasm32 port. int, uint and uintptr would all be 32 bit in length.

The maintainers of the existing wasip1/wasm port (@johanbrandhorst , @Pryz, @evanphx) volunteer to become maintainers of this new port.

Discussion

The introduction of GOARCH=wasm32 would allow us to write safer code, because the pointer size matches what the host expects:

type iovec struct {
    buf *byte // pointers are 32 bits
    len int   // int is 32 bits
}

iov := &iovec{
    buf: unsafe.SliceData(buf),
    len: len(buf),
}

In this case, the compiler can track the use of the slice pointer, and there is no risk that the GC will collect the objects before calling the imported function. There is also stronger type safety since there is no need to bypass the compiler to perform unsafe type conversions.

This provides a much improved user experience for users writing wrappers for host provided functions.

The existing wasip1/wasm port would remain and retain the go:wasmimport type restrictions. It may eventually become deprecated and removed, in accordance with the Go porting policy. Such a change would be subject to a separate proposal.

There are no plans for introducing a js/wasm32 port.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Accepted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions