diff --git a/builder/build.go b/builder/build.go index 3ae23b021..2d7156b9b 100644 --- a/builder/build.go +++ b/builder/build.go @@ -672,6 +672,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe ldflags = append(ldflags, "--no-entry") } + if config.Options.BuildMode == "wasi-legacy" { + if !strings.HasPrefix(config.Triple(), "wasm32-") { + return result, fmt.Errorf("buildmode wasi-legacy is only supported on wasm") + } + + if config.Options.Scheduler != "none" { + return result, fmt.Errorf("buildmode wasi-legacy only supports scheduler=none") + } + } + // Add compiler-rt dependency if needed. Usually this is a simple load from // a cache. if config.Target.RTLib == "compiler-rt" { diff --git a/compileopts/options.go b/compileopts/options.go index 30e0e4dbe..e698cb3cb 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -8,7 +8,7 @@ import ( ) var ( - validBuildModeOptions = []string{"default", "c-shared"} + validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"} validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"} validSchedulerOptions = []string{"none", "tasks", "asyncify"} validSerialOptions = []string{"none", "uart", "usb", "rtt"} diff --git a/compiler/symbol.go b/compiler/symbol.go index ff7ef0550..5ecf68e54 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -283,6 +283,11 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { info.wasmName = "_start" info.exported = true } + if info.linkName == "runtime.wasmEntryLegacy" && c.BuildMode == "wasi-legacy" { + info.linkName = "_start" + info.wasmName = "_start" + info.exported = true + } // Check for //go: pragmas, which may change the link name (among others). c.parsePragmas(&info, f) diff --git a/main.go b/main.go index dee016558..1f2fc95b1 100644 --- a/main.go +++ b/main.go @@ -1501,7 +1501,7 @@ func main() { var tags buildutil.TagsFlag flag.Var(&tags, "tags", "a space-separated list of extra build tags") target := flag.String("target", "", "chip/board name or JSON target specification file") - buildMode := flag.String("buildmode", "", "build mode to use (default, c-shared)") + buildMode := flag.String("buildmode", "", "build mode to use (default, c-shared, wasi-legacy)") var stackSize uint64 flag.Func("stack-size", "goroutine stack size (if unknown at compile time)", func(s string) error { size, err := bytesize.Parse(s) diff --git a/main_test.go b/main_test.go index 22ac549de..f193f4679 100644 --- a/main_test.go +++ b/main_test.go @@ -594,6 +594,15 @@ func TestWasmExport(t *testing.T) { noOutput: true, // wasm-unknown cannot produce output command: true, }, + // Test buildmode=wasi-legacy with WASI. + { + name: "WASIp1-legacy", + target: "wasip1", + buildMode: "wasi-legacy", + scheduler: "none", + file: "wasmexport-noscheduler.go", + command: true, + }, } for _, tc := range tests { diff --git a/src/runtime/runtime_wasmentry.go b/src/runtime/runtime_wasmentry.go index 807590eb7..b33a19d40 100644 --- a/src/runtime/runtime_wasmentry.go +++ b/src/runtime/runtime_wasmentry.go @@ -52,6 +52,15 @@ func wasmEntryReactor() { } } +// This is the _start entry point, when using -buildmode=wasi-legacy. +func wasmEntryLegacy() { + // These need to be initialized early so that the heap can be initialized. + initializeCalled = true + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) + run() +} + // Whether the runtime was initialized by a call to _initialize or _start. var initializeCalled bool