mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
all: implement wasmimport directive
Go programs can now use the //go:wasmimport module_name function_name directive to import functions from the WebAssembly runtime. For now, the directive is restricted to the runtime and syscall/js packages. * Derived from CL 350737 * Original work modified to work with changes to the IR conversion code. * Modification of CL 350737 changes to fully exist in Unified IR path (emp) * Original work modified to work with changes to the ABI configuration code. * Fixes #38248 Co-authored-by: Vedant Roy <vroy101@gmail.com> Co-authored-by: Richard Musiol <mail@richard-musiol.de> Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Change-Id: I740719735d91c306ac718a435a78e1ee9686bc16 Reviewed-on: https://go-review.googlesource.com/c/go/+/463018 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
This commit is contained in:
parent
af9f21289f
commit
02411bcd7c
29 changed files with 585 additions and 145 deletions
|
|
@ -6,10 +6,14 @@ package wasm
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/wasm"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/link/internal/ld"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"internal/buildcfg"
|
||||
"io"
|
||||
"regexp"
|
||||
|
|
@ -44,9 +48,10 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) {
|
|||
}
|
||||
|
||||
type wasmFunc struct {
|
||||
Name string
|
||||
Type uint32
|
||||
Code []byte
|
||||
Module string
|
||||
Name string
|
||||
Type uint32
|
||||
Code []byte
|
||||
}
|
||||
|
||||
type wasmFuncType struct {
|
||||
|
|
@ -54,6 +59,59 @@ type wasmFuncType struct {
|
|||
Results []byte
|
||||
}
|
||||
|
||||
func readWasmImport(ldr *loader.Loader, s loader.Sym) obj.WasmImport {
|
||||
reportError := func(err error) { panic(fmt.Sprintf("failed to read WASM import in sym %v: %v", s, err)) }
|
||||
|
||||
data := ldr.Data(s)
|
||||
|
||||
readUint32 := func() (v uint32) {
|
||||
v = binary.LittleEndian.Uint32(data)
|
||||
data = data[4:]
|
||||
return
|
||||
}
|
||||
|
||||
readUint64 := func() (v uint64) {
|
||||
v = binary.LittleEndian.Uint64(data)
|
||||
data = data[8:]
|
||||
return
|
||||
}
|
||||
|
||||
readByte := func() byte {
|
||||
if len(data) == 0 {
|
||||
reportError(io.EOF)
|
||||
}
|
||||
|
||||
b := data[0]
|
||||
data = data[1:]
|
||||
return b
|
||||
}
|
||||
|
||||
readString := func() string {
|
||||
n := readUint32()
|
||||
|
||||
s := string(data[:n])
|
||||
|
||||
data = data[n:]
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
var wi obj.WasmImport
|
||||
wi.Module = readString()
|
||||
wi.Name = readString()
|
||||
wi.Params = make([]obj.WasmField, readUint32())
|
||||
for i := range wi.Params {
|
||||
wi.Params[i].Type = obj.WasmFieldType(readByte())
|
||||
wi.Params[i].Offset = int64(readUint64())
|
||||
}
|
||||
wi.Results = make([]obj.WasmField, readUint32())
|
||||
for i := range wi.Results {
|
||||
wi.Results[i].Type = obj.WasmFieldType(readByte())
|
||||
wi.Results[i].Offset = int64(readUint64())
|
||||
}
|
||||
return wi
|
||||
}
|
||||
|
||||
var wasmFuncTypes = map[string]*wasmFuncType{
|
||||
"_rt0_wasm_js": {Params: []byte{}}, //
|
||||
"wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv
|
||||
|
|
@ -136,23 +194,30 @@ func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
|
|||
}
|
||||
|
||||
// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
|
||||
hostImports := []*wasmFunc{
|
||||
{
|
||||
Name: "debug",
|
||||
Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
|
||||
},
|
||||
}
|
||||
// we store the import index of each imported function, so the R_WASMIMPORT relocation
|
||||
// can write the correct index after a "call" instruction
|
||||
// these are added as import statements to the top of the WebAssembly binary
|
||||
var hostImports []*wasmFunc
|
||||
hostImportMap := make(map[loader.Sym]int64)
|
||||
for _, fn := range ctxt.Textp {
|
||||
relocs := ldr.Relocs(fn)
|
||||
for ri := 0; ri < relocs.Count(); ri++ {
|
||||
r := relocs.At(ri)
|
||||
if r.Type() == objabi.R_WASMIMPORT {
|
||||
hostImportMap[r.Sym()] = int64(len(hostImports))
|
||||
hostImports = append(hostImports, &wasmFunc{
|
||||
Name: ldr.SymName(r.Sym()),
|
||||
Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
|
||||
})
|
||||
if lsym, ok := ldr.WasmImportSym(fn); ok {
|
||||
wi := readWasmImport(ldr, lsym)
|
||||
hostImportMap[fn] = int64(len(hostImports))
|
||||
hostImports = append(hostImports, &wasmFunc{
|
||||
Module: wi.Module,
|
||||
Name: wi.Name,
|
||||
Type: lookupType(&wasmFuncType{
|
||||
Params: fieldsToTypes(wi.Params),
|
||||
Results: fieldsToTypes(wi.Results),
|
||||
}, &types),
|
||||
})
|
||||
} else {
|
||||
panic(fmt.Sprintf("missing wasm symbol for %s", ldr.SymName(r.Sym())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -288,7 +353,11 @@ func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
|
|||
|
||||
writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
|
||||
for _, fn := range hostImports {
|
||||
writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js
|
||||
if fn.Module != "" {
|
||||
writeName(ctxt.Out, fn.Module)
|
||||
} else {
|
||||
writeName(ctxt.Out, wasm.GojsModule) // provided by the import object in wasm_exec.js
|
||||
}
|
||||
writeName(ctxt.Out, fn.Name)
|
||||
ctxt.Out.WriteByte(0x00) // func import
|
||||
writeUleb128(ctxt.Out, uint64(fn.Type))
|
||||
|
|
@ -610,3 +679,22 @@ func writeSleb128(w io.ByteWriter, v int64) {
|
|||
w.WriteByte(c)
|
||||
}
|
||||
}
|
||||
|
||||
func fieldsToTypes(fields []obj.WasmField) []byte {
|
||||
b := make([]byte, len(fields))
|
||||
for i, f := range fields {
|
||||
switch f.Type {
|
||||
case obj.WasmI32, obj.WasmPtr:
|
||||
b[i] = I32
|
||||
case obj.WasmI64:
|
||||
b[i] = I64
|
||||
case obj.WasmF32:
|
||||
b[i] = F32
|
||||
case obj.WasmF64:
|
||||
b[i] = F64
|
||||
default:
|
||||
panic(fmt.Sprintf("fieldsToTypes: unknown field type: %d", f.Type))
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue