+ // 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 ommission 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 a 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)
+
+ // 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
+ // isn't run. We don't want the frame expansion code because our function
+ // body is just the code to translate and call the imported function.
+ framesize = 0
+ } else if s.Func().Text.From.Sym.Wrapper() {