diff options
| author | Cherry Mui <cherryyz@google.com> | 2024-08-05 13:40:18 -0400 |
|---|---|---|
| committer | Cherry Mui <cherryyz@google.com> | 2024-08-06 15:56:16 +0000 |
| commit | 03e5d83ca7323e354dfff6ba50720302ed835b7c (patch) | |
| tree | 1c6e1d4c01f98fe6cee93bd03648e5dadcde49c0 /src/cmd/internal/obj/wasm | |
| parent | a7c7ec5995b3901145f847c01f3e100b2b7e3421 (diff) | |
| download | go-03e5d83ca7323e354dfff6ba50720302ed835b7c.tar.xz | |
cmd/internal/obj: minor refactor of wasmimport code
This CL does some minor refactoring of the code handling
wasmimport.
- Put the WasmImport aux reading and writing code together for
symmetry.
- Define WasmFuncType, embedded in WasmImport. WasmFuncType could
also be used (later) for wasmexport.
- Move code generation code to a separate function. The containing
function is already pretty large.
- Simplify linker code a little bit. The loader convention is to
return the 0 Sym for nonexistent symbol, instead of a separate
boolean.
No change in generated code. Passes toolstash -cmp
(GOARCH=wasm GOOS=wasip1 go build -toolexec "toolstash -cmp" -a std cmd).
Change-Id: Idc2514f84a08621333841ae4034b81130e0ce411
Reviewed-on: https://go-review.googlesource.com/c/go/+/603135
Reviewed-by: Than McIntosh <thanm@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/cmd/internal/obj/wasm')
| -rw-r--r-- | src/cmd/internal/obj/wasm/wasmobj.go | 216 |
1 files changed, 111 insertions, 105 deletions
diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 23f51f8b42..dcbf35e886 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -188,111 +188,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { // If the function exits just to call out to a wasmimport, then // generate the code to translate from our internal Go-stack // based call convention to the native webassembly call convention. - if wi := s.Func().WasmImport; wi != nil { - s.Func().WasmImportSym = wi.CreateSym(ctxt) - p := s.Func().Text - if p.Link != nil { - panic("wrapper functions for WASM imports should not have a body") - } - to := obj.Addr{ - Type: obj.TYPE_MEM, - Name: obj.NAME_EXTERN, - Sym: s, - } - - // If the module that the import is for is our magic "gojs" module, then this - // indicates that the called function understands the Go stack-based call convention - // so we just pass the stack pointer to it, knowing it will read the params directly - // off the stack and push the results into memory based on the stack pointer. - if wi.Module == GojsModule { - // The called function has a signature of 'func(sp int)'. It has access to the memory - // value somewhere to be able to address the memory based on the "sp" value. - - p = appendp(p, AGet, regAddr(REG_SP)) - p = appendp(p, ACall, to) - - p.Mark = WasmImport - } else { - if len(wi.Results) > 1 { - // TODO(evanphx) implement support for the multi-value proposal: - // https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md - panic("invalid results type") // impossible until multi-value proposal has landed - } - if len(wi.Results) == 1 { - // If we have a result (rather than returning nothing at all), then - // we'll write the result to the Go stack relative to the current stack pointer. - // We cache the current stack pointer value on the wasm stack here and then use - // it after the Call instruction to store the result. - p = appendp(p, AGet, regAddr(REG_SP)) - } - for _, f := range wi.Params { - // Each load instructions will consume the value of sp on the stack, so - // we need to read sp for each param. WASM appears to not have a stack dup instruction - // (a strange omission for a stack-based VM), if it did, we'd be using the dup here. - p = appendp(p, AGet, regAddr(REG_SP)) - - // Offset is the location of the param on the Go stack (ie relative to sp). - // Because of our call convention, the parameters are located an additional 8 bytes - // from sp because we store the return address as an int64 at the bottom of the stack. - // Ie the stack looks like [return_addr, param3, param2, param1, etc] - - // Ergo, we add 8 to the true byte offset of the param to skip the return address. - loadOffset := f.Offset + 8 - - // We're reading the value from the Go stack onto the WASM stack and leaving it there - // for CALL to pick them up. - switch f.Type { - case obj.WasmI32: - p = appendp(p, AI32Load, constAddr(loadOffset)) - case obj.WasmI64: - p = appendp(p, AI64Load, constAddr(loadOffset)) - case obj.WasmF32: - p = appendp(p, AF32Load, constAddr(loadOffset)) - case obj.WasmF64: - p = appendp(p, AF64Load, constAddr(loadOffset)) - case obj.WasmPtr: - p = appendp(p, AI64Load, constAddr(loadOffset)) - p = appendp(p, AI32WrapI64) - default: - panic("bad param type") - } - } - - // The call instruction is marked as being for a wasm import so that a later phase - // will generate relocation information that allows us to patch this with then - // offset of the imported function in the wasm imports. - p = appendp(p, ACall, to) - p.Mark = WasmImport - - if len(wi.Results) == 1 { - f := wi.Results[0] - - // Much like with the params, we need to adjust the offset we store the result value - // to by 8 bytes to account for the return address on the Go stack. - storeOffset := f.Offset + 8 - - // This code is paired the code above that reads the stack pointer onto the wasm - // stack. We've done this so we have a consistent view of the sp value as it might - // be manipulated by the call and we want to ignore that manipulation here. - switch f.Type { - case obj.WasmI32: - p = appendp(p, AI32Store, constAddr(storeOffset)) - case obj.WasmI64: - p = appendp(p, AI64Store, constAddr(storeOffset)) - case obj.WasmF32: - p = appendp(p, AF32Store, constAddr(storeOffset)) - case obj.WasmF64: - p = appendp(p, AF64Store, constAddr(storeOffset)) - case obj.WasmPtr: - p = appendp(p, AI64ExtendI32U) - p = appendp(p, AI64Store, constAddr(storeOffset)) - default: - panic("bad result type") - } - } - } - - p = appendp(p, obj.ARET) + if s.Func().WasmImport != nil { + genWasmImportWrapper(s, appendp) // It should be 0 already, but we'll set it to 0 anyway just to be sure // that the code below which adds frame expansion code to the function body @@ -894,6 +791,115 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } } +// Generate function body for wasmimport wrapper function. +func genWasmImportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog) { + wi := s.Func().WasmImport + wi.CreateAuxSym() + p := s.Func().Text + if p.Link != nil { + panic("wrapper functions for WASM imports should not have a body") + } + to := obj.Addr{ + Type: obj.TYPE_MEM, + Name: obj.NAME_EXTERN, + Sym: s, + } + + // If the module that the import is for is our magic "gojs" module, then this + // indicates that the called function understands the Go stack-based call convention + // so we just pass the stack pointer to it, knowing it will read the params directly + // off the stack and push the results into memory based on the stack pointer. + if wi.Module == GojsModule { + // The called function has a signature of 'func(sp int)'. It has access to the memory + // value somewhere to be able to address the memory based on the "sp" value. + + p = appendp(p, AGet, regAddr(REG_SP)) + p = appendp(p, ACall, to) + + p.Mark = WasmImport + } else { + if len(wi.Results) > 1 { + // TODO(evanphx) implement support for the multi-value proposal: + // https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md + panic("invalid results type") // impossible until multi-value proposal has landed + } + if len(wi.Results) == 1 { + // If we have a result (rather than returning nothing at all), then + // we'll write the result to the Go stack relative to the current stack pointer. + // We cache the current stack pointer value on the wasm stack here and then use + // it after the Call instruction to store the result. + p = appendp(p, AGet, regAddr(REG_SP)) + } + for _, f := range wi.Params { + // Each load instructions will consume the value of sp on the stack, so + // we need to read sp for each param. WASM appears to not have a stack dup instruction + // (a strange omission for a stack-based VM), if it did, we'd be using the dup here. + p = appendp(p, AGet, regAddr(REG_SP)) + + // Offset is the location of the param on the Go stack (ie relative to sp). + // Because of our call convention, the parameters are located an additional 8 bytes + // from sp because we store the return address as an int64 at the bottom of the stack. + // Ie the stack looks like [return_addr, param3, param2, param1, etc] + + // Ergo, we add 8 to the true byte offset of the param to skip the return address. + loadOffset := f.Offset + 8 + + // We're reading the value from the Go stack onto the WASM stack and leaving it there + // for CALL to pick them up. + switch f.Type { + case obj.WasmI32: + p = appendp(p, AI32Load, constAddr(loadOffset)) + case obj.WasmI64: + p = appendp(p, AI64Load, constAddr(loadOffset)) + case obj.WasmF32: + p = appendp(p, AF32Load, constAddr(loadOffset)) + case obj.WasmF64: + p = appendp(p, AF64Load, constAddr(loadOffset)) + case obj.WasmPtr: + p = appendp(p, AI64Load, constAddr(loadOffset)) + p = appendp(p, AI32WrapI64) + default: + panic("bad param type") + } + } + + // The call instruction is marked as being for a wasm import so that a later phase + // will generate relocation information that allows us to patch this with then + // offset of the imported function in the wasm imports. + p = appendp(p, ACall, to) + p.Mark = WasmImport + + if len(wi.Results) == 1 { + f := wi.Results[0] + + // Much like with the params, we need to adjust the offset we store the result value + // to by 8 bytes to account for the return address on the Go stack. + storeOffset := f.Offset + 8 + + // This code is paired the code above that reads the stack pointer onto the wasm + // stack. We've done this so we have a consistent view of the sp value as it might + // be manipulated by the call and we want to ignore that manipulation here. + switch f.Type { + case obj.WasmI32: + p = appendp(p, AI32Store, constAddr(storeOffset)) + case obj.WasmI64: + p = appendp(p, AI64Store, constAddr(storeOffset)) + case obj.WasmF32: + p = appendp(p, AF32Store, constAddr(storeOffset)) + case obj.WasmF64: + p = appendp(p, AF64Store, constAddr(storeOffset)) + case obj.WasmPtr: + p = appendp(p, AI64ExtendI32U) + p = appendp(p, AI64Store, constAddr(storeOffset)) + default: + panic("bad result type") + } + } + } + + p = appendp(p, obj.ARET) +} + func constAddr(value int64) obj.Addr { return obj.Addr{Type: obj.TYPE_CONST, Offset: value} } |
