"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"darwin/amd64", "darwin/arm64",
- "windows/amd64", "windows/386", "windows/arm64":
+ "windows/amd64", "windows/386", "windows/arm64",
+ "wasip1/wasm":
return true
}
return false
// Some build modes always require external linking.
switch cfg.BuildBuildmode {
- case "c-shared", "plugin":
+ case "c-shared":
+ if cfg.BuildContext.GOARCH == "wasm" {
+ break
+ }
+ fallthrough
+ case "plugin":
return "-buildmode=" + cfg.BuildBuildmode
}
var notUsePC_B = map[string]bool{
"_rt0_wasm_js": true,
"_rt0_wasm_wasip1": true,
+ "_rt0_wasm_wasip1_lib": true,
"wasm_export_run": true,
"wasm_export_resume": true,
"wasm_export_getsp": true,
// Function starts with declaration of locals: numbers and types.
// Some functions use a special calling convention.
switch s.Name {
- case "_rt0_wasm_js", "_rt0_wasm_wasip1", "wasm_export_run", "wasm_export_resume", "wasm_export_getsp",
+ case "_rt0_wasm_js", "_rt0_wasm_wasip1", "_rt0_wasm_wasip1_lib",
+ "wasm_export_run", "wasm_export_resume", "wasm_export_getsp",
"wasm_pc_f_loop", "runtime.wasmDiv", "runtime.wasmTruncS", "runtime.wasmTruncU", "memeqbody":
varDecls = []*varDecl{}
useAssemblyRegMap()
case BuildModeCArchive:
return true, "buildmode=c-archive"
case BuildModeCShared:
+ if buildcfg.GOARCH == "wasm" {
+ break
+ }
return true, "buildmode=c-shared"
case BuildModePIE:
if !platform.InternalLinkPIESupported(buildcfg.GOOS, buildcfg.GOARCH) {
func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
ldr := ctxt.loader
- if !ctxt.IsAIX() {
+ if !ctxt.IsAIX() && !ctxt.IsWasm() {
switch ctxt.BuildMode {
case BuildModeCArchive, BuildModeCShared:
s := ldr.Lookup(*flagEntrySymbol, sym.SymVerABI0)
var wasmFuncTypes = map[string]*wasmFuncType{
"_rt0_wasm_js": {Params: []byte{}}, //
"_rt0_wasm_wasip1": {Params: []byte{}}, //
+ "_rt0_wasm_wasip1_lib": {Params: []byte{}}, //
"wasm_export__start": {}, //
"wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv
"wasm_export_resume": {Params: []byte{}}, //
switch buildcfg.GOOS {
case "wasip1":
writeUleb128(ctxt.Out, uint64(2+len(ldr.WasmExports))) // number of exports
- s := ldr.Lookup("_rt0_wasm_wasip1", 0)
+ var entry, entryExpName string
+ switch ctxt.BuildMode {
+ case ld.BuildModeExe:
+ entry = "_rt0_wasm_wasip1"
+ entryExpName = "_start"
+ case ld.BuildModeCShared:
+ entry = "_rt0_wasm_wasip1_lib"
+ entryExpName = "_initialize"
+ }
+ s := ldr.Lookup(entry, 0)
+ if s == 0 {
+ ld.Errorf(nil, "export symbol %s not defined", entry)
+ }
idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
- writeName(ctxt.Out, "_start") // the wasi entrypoint
+ writeName(ctxt.Out, entryExpName) // the wasi entrypoint
ctxt.Out.WriteByte(0x00) // func export
writeUleb128(ctxt.Out, uint64(idx)) // funcidx
for _, s := range ldr.WasmExports {
writeUleb128(ctxt.Out, 4) // number of exports
for _, name := range []string{"run", "resume", "getsp"} {
s := ldr.Lookup("wasm_export_"+name, 0)
+ if s == 0 {
+ ld.Errorf(nil, "export symbol %s not defined", "wasm_export_"+name)
+ }
idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js
ctxt.Out.WriteByte(0x00) // func export
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"darwin/amd64", "darwin/arm64",
- "windows/amd64", "windows/386", "windows/arm64":
+ "windows/amd64", "windows/386", "windows/arm64",
+ "wasip1/wasm":
return true
}
return false
TEXT wasm_export_lib(SB),NOSPLIT,$0
UNDEF
+
+TEXT runtime·pause(SB), NOSPLIT, $0-8
+ MOVD newsp+0(FP), SP
+ I32Const $1
+ Set PAUSE
+ RETUNWIND
idleTimeout = nil
}
-// pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered.
-func pause(newsp uintptr)
-
// scheduleTimeoutEvent tells the WebAssembly environment to trigger an event after ms milliseconds.
// It returns a timer id that can be used with clearTimeoutEvent.
//
if isarchive || islibrary {
// A program compiled with -buildmode=c-archive or c-shared
// has a main, but it is not executed.
+ if GOARCH == "wasm" {
+ // On Wasm, pause makes it return to the host.
+ // Unlike cgo callbacks where Ms are created on demand,
+ // on Wasm we have only one M. So we keep this M (and this
+ // G) for callbacks.
+ // Using the caller's SP unwinds this frame and backs to
+ // goexit. The -16 is: 8 for goexit's (fake) return PC,
+ // and pause's epilogue pops 8.
+ pause(getcallersp() - 16) // should not return
+ panic("unreachable")
+ }
return
}
fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
// there are no running goroutines. The calling program is
// assumed to be running.
- if islibrary || isarchive {
+ // One exception is Wasm, which is single-threaded. If we are
+ // in Go and all goroutines are blocked, it deadlocks.
+ if (islibrary || isarchive) && GOARCH != "wasm" {
return
}
Get SP
Return
-TEXT runtime·pause(SB), NOSPLIT, $0-8
- MOVD newsp+0(FP), SP
- I32Const $1
- Set PAUSE
- RETUNWIND
-
TEXT runtime·exit(SB), NOSPLIT, $0-4
I32Const $0
Call runtime·wasmExit(SB)
Call wasm_pc_f_loop(SB)
Return
+
+TEXT _rt0_wasm_wasip1_lib(SB),NOSPLIT,$0
+ Call _rt0_wasm_wasip1(SB)
+ Return
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !wasm
+
+package runtime
+
+// pause is only used on wasm.
+func pause(newsp uintptr) { panic("unreachable") }
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// pause sets SP to newsp and pauses the execution of Go's WebAssembly
+// code until an event is triggered, or call back into Go.
+//
+// Note: the epilogue of pause pops 8 bytes from the stack, so when
+// returning to the host, the SP is newsp+8.
+// If we want to set the SP such that when it calls back into Go, the
+// Go function appears to be called from pause's caller's caller, then
+// call pause with newsp = getcallersp()-16 (another 8 is the return
+// PC pushed to the stack).
+func pause(newsp uintptr)