From 4e88ee103c5c6462f70895a8aa5780de751f289c Mon Sep 17 00:00:00 2001 From: mathetake Date: Thu, 17 Sep 2020 22:49:37 +0900 Subject: [PATCH 01/15] initial commit for WASI support * merge "time" package with wasi build tag * override syscall package with wasi build tag * create runtime_wasm_{js,wasi}.go files * create syscall_wasi.go file * create time/zoneinfo_wasi.go file as the replacement of zoneinfo_js.go * add targets/wasi.json target --- loader/goroot.go | 22 +++++- loader/loader.go | 2 +- src/runtime/runtime_wasm.go | 54 -------------- src/runtime/runtime_wasm_js.go | 59 +++++++++++++++ src/runtime/runtime_wasm_wasi.go | 41 +++++++++++ src/syscall/syscall_wasi.go | 123 +++++++++++++++++++++++++++++++ src/syscall/tables_baremetal.go | 2 +- src/time/zoneinfo_wasi.go | 11 +++ targets/wasi.json | 22 ++++++ 9 files changed, 278 insertions(+), 58 deletions(-) create mode 100644 src/runtime/runtime_wasm_js.go create mode 100644 src/runtime/runtime_wasm_wasi.go create mode 100644 src/syscall/syscall_wasi.go create mode 100644 src/time/zoneinfo_wasi.go create mode 100644 targets/wasi.json diff --git a/loader/goroot.go b/loader/goroot.go index 9648550719..aed7e4e466 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -15,6 +15,7 @@ import ( "path" "path/filepath" "runtime" + "strings" "sync" "github.com/tinygo-org/tinygo/compileopts" @@ -164,6 +165,17 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides return err } for _, e := range gorootEntries { + if isMergeTargetEntry(importPath, e.Name()) { + // In this case, we have files both from TinyGo and Go. + // As of now, this is only for WASI to have *_wasi.go files as replacements for *_js.go files + newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) + oldname := filepath.Join(goroot, "src", importPath, e.Name()) + err := symlink(oldname, newname) + if err != nil { + return err + } + continue + } if !e.IsDir() { // Don't merge in files from Go. Otherwise we'd end up with a // weird syscall package with files from both roots. @@ -185,11 +197,16 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides return nil } +func isMergeTargetEntry(importPath string, name string) bool { + // TODO: add crypto/rand + return importPath == "time" && !strings.HasSuffix(name, "_js.go") +} + // needsSyscallPackage returns whether the syscall package should be overriden // with the TinyGo version. This is the case on some targets. func needsSyscallPackage(buildTags []string) bool { for _, tag := range buildTags { - if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" { + if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" { return true } } @@ -212,7 +229,8 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool { "reflect/": false, "runtime/": false, "sync/": true, - "testing/": true, + "time/": true, + "testing/": false, } if needsSyscallPackage { paths["syscall/"] = true // include syscall/js diff --git a/loader/loader.go b/loader/loader.go index 847acc2584..6200cc574b 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -166,7 +166,6 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t Err: err, } } - return nil, err } if config.TestConfig.CompileTestBinary { // When creating a test binary, `go list` will list two or three @@ -230,6 +229,7 @@ func (p *Program) getOriginalPath(path string) string { } maybeInTinyGoRoot = true } + if maybeInTinyGoRoot { tinygoPath := filepath.Join(goenv.Get("TINYGOROOT"), "src", relpath) if _, err := os.Stat(tinygoPath); err == nil { diff --git a/src/runtime/runtime_wasm.go b/src/runtime/runtime_wasm.go index aaf95d7846..4bd9c7b3c8 100644 --- a/src/runtime/runtime_wasm.go +++ b/src/runtime/runtime_wasm.go @@ -4,8 +4,6 @@ package runtime import "unsafe" -type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript - // Implements __wasi_ciovec_t and __wasi_iovec_t. type wasiIOVec struct { buf unsafe.Pointer @@ -18,15 +16,6 @@ func fd_write(id uint32, iovs *wasiIOVec, iovs_len uint, nwritten *uint) (errno func postinit() {} -//export _start -func _start() { - // These need to be initialized early so that the heap can be initialized. - heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) - - run() -} - // Using global variables to avoid heap allocation. var ( putcharBuf = byte(0) @@ -44,49 +33,6 @@ func putchar(c byte) { fd_write(stdout, &putcharIOVec, 1, &nwritten) } -var handleEvent func() - -//go:linkname setEventHandler syscall/js.setEventHandler -func setEventHandler(fn func()) { - handleEvent = fn -} - -//export resume -func resume() { - go func() { - handleEvent() - }() - scheduler() -} - -//export go_scheduler -func go_scheduler() { - scheduler() -} - -const asyncScheduler = true - -func ticksToNanoseconds(ticks timeUnit) int64 { - // The JavaScript API works in float64 milliseconds, so convert to - // nanoseconds first before converting to a timeUnit (which is a float64), - // to avoid precision loss. - return int64(ticks * 1e6) -} - -func nanosecondsToTicks(ns int64) timeUnit { - // The JavaScript API works in float64 milliseconds, so convert to timeUnit - // (which is a float64) first before dividing, to avoid precision loss. - return timeUnit(ns) / 1e6 -} - -// This function is called by the scheduler. -// Schedule a call to runtime.scheduler, do not actually sleep. -//export runtime.sleepTicks -func sleepTicks(d timeUnit) - -//export runtime.ticks -func ticks() timeUnit - // Abort executes the wasm 'unreachable' instruction. func abort() { trap() diff --git a/src/runtime/runtime_wasm_js.go b/src/runtime/runtime_wasm_js.go new file mode 100644 index 0000000000..ea1cc6e41d --- /dev/null +++ b/src/runtime/runtime_wasm_js.go @@ -0,0 +1,59 @@ +// +build wasm,!wasi + +package runtime + +import "unsafe" + +type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript + +//export _start +func _start() { + // These need to be initialized early so that the heap can be initialized. + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) + + run() +} + +var handleEvent func() + +//go:linkname setEventHandler syscall/js.setEventHandler +func setEventHandler(fn func()) { + handleEvent = fn +} + +//export resume +func resume() { + go func() { + handleEvent() + }() + scheduler() +} + +//export go_scheduler +func go_scheduler() { + scheduler() +} + +const asyncScheduler = true + +func ticksToNanoseconds(ticks timeUnit) int64 { + // The JavaScript API works in float64 milliseconds, so convert to + // nanoseconds first before converting to a timeUnit (which is a float64), + // to avoid precision loss. + return int64(ticks * 1e6) +} + +func nanosecondsToTicks(ns int64) timeUnit { + // The JavaScript API works in float64 milliseconds, so convert to timeUnit + // (which is a float64) first before dividing, to avoid precision loss. + return timeUnit(ns) / 1e6 +} + +// This function is called by the scheduler. +// Schedule a call to runtime.scheduler, do not actually sleep. +//export runtime.sleepTicks +func sleepTicks(d timeUnit) + +//export runtime.ticks +func ticks() timeUnit diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go new file mode 100644 index 0000000000..a7798654ba --- /dev/null +++ b/src/runtime/runtime_wasm_wasi.go @@ -0,0 +1,41 @@ +// +build wasm,wasi + +package runtime + +import "unsafe" + +type timeUnit float64 + +//export _start +func _start() { + // These need to be initialized early so that the heap can be initialized. + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) + + run() +} + +const asyncScheduler = true + +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns) +} + +// This function is called by the scheduler. +// Schedule a call to runtime.scheduler, do not actually sleep. +//export runtime.sleepTicks +func sleepTicks(d timeUnit) + +//go:wasm-module wasi_unstable +//export clock_time_get +func clock_time_get(id uint32, precision uint64, timePtr *uint64) (errno uint) + +func ticks() timeUnit { + var time uint64 + clock_time_get(0, 100, &time) + return timeUnit(time) +} diff --git a/src/syscall/syscall_wasi.go b/src/syscall/syscall_wasi.go new file mode 100644 index 0000000000..ad6dbb1111 --- /dev/null +++ b/src/syscall/syscall_wasi.go @@ -0,0 +1,123 @@ +// +build wasi + +package syscall + +// Most code here has been copied from the Go sources: +// https://github.com/golang/go/blob/go1.12/src/syscall/syscall_js.go +// It has the following copyright note: +// +// Copyright 2018 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. + +// A Signal is a number describing a process signal. +// It implements the os.Signal interface. +type Signal int + +const ( + _ Signal = iota + SIGCHLD + SIGINT + SIGKILL + SIGTRAP + SIGQUIT + SIGTERM +) + +// File system + +const ( + Stdin = 0 + Stdout = 1 + Stderr = 2 +) + +const ( + O_RDONLY = 0 + O_WRONLY = 1 + O_RDWR = 2 + + O_CREAT = 0100 + O_CREATE = O_CREAT + O_TRUNC = 01000 + O_APPEND = 02000 + O_EXCL = 0200 + O_SYNC = 010000 + + O_CLOEXEC = 0 +) + +func Getenv(key string) (value string, found bool) { + return "", false // stub +} + +func Open(path string, mode int, perm uint32) (fd int, err error) { + return 0, ENOSYS +} + +func Read(fd int, p []byte) (n int, err error) { + return 0, ENOSYS +} + +func Seek(fd int, offset int64, whence int) (off int64, err error) { + return 0, ENOSYS +} + +func Close(fd int) (err error) { + return ENOSYS +} + +// Processes + +type WaitStatus uint32 + +func (w WaitStatus) Exited() bool { return false } +func (w WaitStatus) ExitStatus() int { return 0 } +func (w WaitStatus) Signaled() bool { return false } +func (w WaitStatus) Signal() Signal { return 0 } +func (w WaitStatus) CoreDump() bool { return false } +func (w WaitStatus) Stopped() bool { return false } +func (w WaitStatus) Continued() bool { return false } +func (w WaitStatus) StopSignal() Signal { return 0 } +func (w WaitStatus) TrapCause() int { return 0 } + +// XXX made up +type Rusage struct { + Utime Timeval + Stime Timeval +} + +// XXX made up +type ProcAttr struct { + Dir string + Env []string + Files []uintptr + Sys *SysProcAttr +} + +type SysProcAttr struct { +} + +func Getegid() int { return 1 } +func Geteuid() int { return 1 } +func Getgid() int { return 1 } +func Getgroups() ([]int, error) { return []int{1}, nil } +func Getppid() int { return 2 } +func Getpid() int { return 3 } +func Gettimeofday(tv *Timeval) error { return ENOSYS } +func Getuid() int { return 1 } +func Kill(pid int, signum Signal) error { return ENOSYS } +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + return 0, ENOSYS +} +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { + return 0, 0, ENOSYS +} +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { + return 0, ENOSYS +} + +type Timeval struct { + Sec int64 + Usec int64 +} diff --git a/src/syscall/tables_baremetal.go b/src/syscall/tables_baremetal.go index 47a536bffd..9677d77a09 100644 --- a/src/syscall/tables_baremetal.go +++ b/src/syscall/tables_baremetal.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build baremetal nintendoswitch +// +build baremetal nintendoswitch wasi package syscall diff --git a/src/time/zoneinfo_wasi.go b/src/time/zoneinfo_wasi.go new file mode 100644 index 0000000000..5dc771522d --- /dev/null +++ b/src/time/zoneinfo_wasi.go @@ -0,0 +1,11 @@ +// +build wasi + +// should be replaced with original time/zoneinfo_js.go + +package time + +var zoneSources []string // TODO: + +func initLocal() { + // TODO: +} diff --git a/targets/wasi.json b/targets/wasi.json new file mode 100644 index 0000000000..09ab606d72 --- /dev/null +++ b/targets/wasi.json @@ -0,0 +1,22 @@ +{ + "llvm-target": "wasm32--wasi", + "build-tags": ["js", "wasm", "wasi"], + "goos": "js", + "goarch": "wasm", + "compiler": "clang", + "linker": "wasm-ld", + "cflags": [ + "--target=wasm32--wasi", + "--sysroot={root}/lib/wasi-libc/sysroot", + "-Oz" + ], + "ldflags": [ + "--allow-undefined", + "--no-threads", + "--stack-first", + "--export-dynamic", + "--no-demangle", + "{root}/lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a" + ], + "emulator": ["node", "targets/wasm_exec.js"] +} From c711c445862eda07d001b015927d287605a15391 Mon Sep 17 00:00:00 2001 From: mathetake Date: Thu, 17 Sep 2020 22:54:17 +0900 Subject: [PATCH 02/15] set visbility hidden for runtime extern variables Accodring to the WASI docs (https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi), none of exports of WASI executable(Command) should no be accessed. v0.19.0 of bytecodealliance/wasmetime, which is often refered to as the reference implementation of WASI, does not accept any exports except functions and the only limited variables like "table", "memory". --- transform/gc.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transform/gc.go b/transform/gc.go index 9329644522..d8258a01ba 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -96,6 +96,7 @@ func MakeGCStackSlots(mod llvm.Module) bool { } stackChainStartType := stackChainStart.Type().ElementType() stackChainStart.SetInitializer(llvm.ConstNull(stackChainStartType)) + stackChainStart.SetVisibility(llvm.HiddenVisibility) // Iterate until runtime.trackPointer has no uses left. for use := trackPointer.FirstUse(); !use.IsNil(); use = trackPointer.FirstUse() { @@ -333,12 +334,14 @@ func AddGlobalsBitmap(mod llvm.Module) bool { // Update trackedGlobalsStart, which points to the globals bundle. trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, uintptrType) mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart) + mod.NamedGlobal("runtime.trackedGlobalsStart").SetVisibility(llvm.HiddenVisibility) // Update trackedGlobalsLength, which contains the length (in words) of the // globals bundle. alignment := targetData.PrefTypeAlignment(llvm.PointerType(ctx.Int8Type(), 0)) trackedGlobalsLength := llvm.ConstInt(uintptrType, targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false) mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength) + mod.NamedGlobal("runtime.trackedGlobalsLength").SetVisibility(llvm.HiddenVisibility) // Create a bitmap (a new global) that stores for each word in the globals // bundle whether it contains a pointer. This allows globals to be scanned @@ -357,6 +360,7 @@ func AddGlobalsBitmap(mod llvm.Module) bool { bitmapOld.ReplaceAllUsesWith(llvm.ConstBitCast(bitmapNew, bitmapOld.Type())) bitmapNew.SetInitializer(bitmapArray) bitmapNew.SetName("runtime.trackedGlobalsBitmap") + bitmapNew.SetVisibility(llvm.HiddenVisibility) return true // the IR was changed } From d400858bb9fa643e6f9831aad07f64abb880f40b Mon Sep 17 00:00:00 2001 From: mathetake Date: Thu, 17 Sep 2020 23:17:54 +0900 Subject: [PATCH 03/15] merge syscall_{baremetal,wasi}.go --- loader/goroot.go | 2 +- src/syscall/syscall_baremetal.go | 2 +- src/syscall/syscall_wasi.go | 123 ------------------------------- src/time/zoneinfo_wasi.go | 2 + 4 files changed, 4 insertions(+), 125 deletions(-) delete mode 100644 src/syscall/syscall_wasi.go diff --git a/loader/goroot.go b/loader/goroot.go index aed7e4e466..fb0d2cc6b8 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -230,7 +230,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool { "runtime/": false, "sync/": true, "time/": true, - "testing/": false, + "testing/": true, } if needsSyscallPackage { paths["syscall/"] = true // include syscall/js diff --git a/src/syscall/syscall_baremetal.go b/src/syscall/syscall_baremetal.go index 28fa39ba85..2331101ba3 100644 --- a/src/syscall/syscall_baremetal.go +++ b/src/syscall/syscall_baremetal.go @@ -1,4 +1,4 @@ -// +build baremetal +// +build baremetal wasi package syscall diff --git a/src/syscall/syscall_wasi.go b/src/syscall/syscall_wasi.go deleted file mode 100644 index ad6dbb1111..0000000000 --- a/src/syscall/syscall_wasi.go +++ /dev/null @@ -1,123 +0,0 @@ -// +build wasi - -package syscall - -// Most code here has been copied from the Go sources: -// https://github.com/golang/go/blob/go1.12/src/syscall/syscall_js.go -// It has the following copyright note: -// -// Copyright 2018 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. - -// A Signal is a number describing a process signal. -// It implements the os.Signal interface. -type Signal int - -const ( - _ Signal = iota - SIGCHLD - SIGINT - SIGKILL - SIGTRAP - SIGQUIT - SIGTERM -) - -// File system - -const ( - Stdin = 0 - Stdout = 1 - Stderr = 2 -) - -const ( - O_RDONLY = 0 - O_WRONLY = 1 - O_RDWR = 2 - - O_CREAT = 0100 - O_CREATE = O_CREAT - O_TRUNC = 01000 - O_APPEND = 02000 - O_EXCL = 0200 - O_SYNC = 010000 - - O_CLOEXEC = 0 -) - -func Getenv(key string) (value string, found bool) { - return "", false // stub -} - -func Open(path string, mode int, perm uint32) (fd int, err error) { - return 0, ENOSYS -} - -func Read(fd int, p []byte) (n int, err error) { - return 0, ENOSYS -} - -func Seek(fd int, offset int64, whence int) (off int64, err error) { - return 0, ENOSYS -} - -func Close(fd int) (err error) { - return ENOSYS -} - -// Processes - -type WaitStatus uint32 - -func (w WaitStatus) Exited() bool { return false } -func (w WaitStatus) ExitStatus() int { return 0 } -func (w WaitStatus) Signaled() bool { return false } -func (w WaitStatus) Signal() Signal { return 0 } -func (w WaitStatus) CoreDump() bool { return false } -func (w WaitStatus) Stopped() bool { return false } -func (w WaitStatus) Continued() bool { return false } -func (w WaitStatus) StopSignal() Signal { return 0 } -func (w WaitStatus) TrapCause() int { return 0 } - -// XXX made up -type Rusage struct { - Utime Timeval - Stime Timeval -} - -// XXX made up -type ProcAttr struct { - Dir string - Env []string - Files []uintptr - Sys *SysProcAttr -} - -type SysProcAttr struct { -} - -func Getegid() int { return 1 } -func Geteuid() int { return 1 } -func Getgid() int { return 1 } -func Getgroups() ([]int, error) { return []int{1}, nil } -func Getppid() int { return 2 } -func Getpid() int { return 3 } -func Gettimeofday(tv *Timeval) error { return ENOSYS } -func Getuid() int { return 1 } -func Kill(pid int, signum Signal) error { return ENOSYS } -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { - return 0, ENOSYS -} -func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - return 0, 0, ENOSYS -} -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { - return 0, ENOSYS -} - -type Timeval struct { - Sec int64 - Usec int64 -} diff --git a/src/time/zoneinfo_wasi.go b/src/time/zoneinfo_wasi.go index 5dc771522d..ce3f8786b2 100644 --- a/src/time/zoneinfo_wasi.go +++ b/src/time/zoneinfo_wasi.go @@ -4,6 +4,8 @@ package time +// TODO: https://github.com/WebAssembly/WASI/issues/25 + var zoneSources []string // TODO: func initLocal() { From cacb682d3f758f550b95a240989309e98e3a77f0 Mon Sep 17 00:00:00 2001 From: mathetake Date: Thu, 17 Sep 2020 23:33:32 +0900 Subject: [PATCH 04/15] fix js target build --- loader/goroot.go | 23 ++++++++++++++++++++--- loader/loader.go | 5 ++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/loader/goroot.go b/loader/goroot.go index fb0d2cc6b8..4b43fe7b84 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -89,7 +89,10 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) { return "", err } } - err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride(needsSyscallPackage(config.BuildTags()))) + err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride( + needsSyscallPackage(config.BuildTags()), + needsTimePackage(config.BuildTags()), + )) if err != nil { return "", err } @@ -213,9 +216,20 @@ func needsSyscallPackage(buildTags []string) bool { return false } +// needsTimePackage returns whether the time package should be merged +// with the TinyGo version. This is the case on some targets. +func needsTimePackage(buildTags []string) bool { + for _, tag := range buildTags { + if tag == "wasi" { + return true + } + } + return false +} + // The boolean indicates whether to merge the subdirs. True means merge, false // means use the TinyGo version. -func pathsToOverride(needsSyscallPackage bool) map[string]bool { +func pathsToOverride(needsSyscallPackage, needsTimePackage bool) map[string]bool { paths := map[string]bool{ "/": true, "device/": false, @@ -229,12 +243,15 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool { "reflect/": false, "runtime/": false, "sync/": true, - "time/": true, "testing/": true, } if needsSyscallPackage { paths["syscall/"] = true // include syscall/js } + + if needsTimePackage { + paths["time/"] = true + } return paths } diff --git a/loader/loader.go b/loader/loader.go index 6200cc574b..159fa8a195 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -223,7 +223,10 @@ func (p *Program) getOriginalPath(path string) string { originalPath = realgorootPath } maybeInTinyGoRoot := false - for prefix := range pathsToOverride(needsSyscallPackage(p.config.BuildTags())) { + for prefix := range pathsToOverride( + needsSyscallPackage(p.config.BuildTags()), + needsTimePackage(p.config.BuildTags()), + ) { if !strings.HasPrefix(relpath, prefix) { continue } From c6b77306353af68483b24956a3122e7d283ce84e Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 11:12:47 +0900 Subject: [PATCH 05/15] mv wasi functions to syscall/wasi && implement sleepTicks --- src/runtime/runtime_wasm.go | 22 +++++-------- src/runtime/runtime_wasm_wasi.go | 52 ++++++++++++++++++++---------- src/syscall/wasi/clock.go | 7 ++++ src/syscall/wasi/common.go | 15 +++++++++ src/syscall/wasi/poll_oneoff.go | 55 ++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 src/syscall/wasi/clock.go create mode 100644 src/syscall/wasi/common.go create mode 100644 src/syscall/wasi/poll_oneoff.go diff --git a/src/runtime/runtime_wasm.go b/src/runtime/runtime_wasm.go index 4bd9c7b3c8..8a9fad424c 100644 --- a/src/runtime/runtime_wasm.go +++ b/src/runtime/runtime_wasm.go @@ -2,26 +2,20 @@ package runtime -import "unsafe" +import ( + "unsafe" -// Implements __wasi_ciovec_t and __wasi_iovec_t. -type wasiIOVec struct { - buf unsafe.Pointer - bufLen uint -} - -//go:wasm-module wasi_unstable -//export fd_write -func fd_write(id uint32, iovs *wasiIOVec, iovs_len uint, nwritten *uint) (errno uint) + "github.com/tinygo-org/tinygo/src/syscall/wasi" +) func postinit() {} // Using global variables to avoid heap allocation. var ( putcharBuf = byte(0) - putcharIOVec = wasiIOVec{ - buf: unsafe.Pointer(&putcharBuf), - bufLen: 1, + putcharIOVec = wasi.IOVec{ + Buf: unsafe.Pointer(&putcharBuf), + BufLen: 1, } ) @@ -30,7 +24,7 @@ func putchar(c byte) { const stdout = 1 var nwritten uint putcharBuf = c - fd_write(stdout, &putcharIOVec, 1, &nwritten) + wasi.Fd_write(stdout, &putcharIOVec, 1, &nwritten) } // Abort executes the wasm 'unreachable' instruction. diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index a7798654ba..cfe8af1bb4 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -2,21 +2,22 @@ package runtime -import "unsafe" +import ( + "unsafe" -type timeUnit float64 + "github.com/tinygo-org/tinygo/src/syscall/wasi" +) + +type timeUnit int64 //export _start func _start() { // These need to be initialized early so that the heap can be initialized. heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) - run() } -const asyncScheduler = true - func ticksToNanoseconds(ticks timeUnit) int64 { return int64(ticks) } @@ -25,17 +26,36 @@ func nanosecondsToTicks(ns int64) timeUnit { return timeUnit(ns) } -// This function is called by the scheduler. -// Schedule a call to runtime.scheduler, do not actually sleep. -//export runtime.sleepTicks -func sleepTicks(d timeUnit) - -//go:wasm-module wasi_unstable -//export clock_time_get -func clock_time_get(id uint32, precision uint64, timePtr *uint64) (errno uint) +const ( + asyncScheduler = false + timePrecisionNanoseconds = 1000 // TODO: how can we determine the appropriate `precision`? +) + +var ( + sleepTicksSubscription = wasi.Subscription_t{ + UserData: 0, + U: wasi.Subscription_u_t{ + Tag: wasi.Eventtype_t_clock, + U: wasi.Subscription_clock_t{ + UserData: 0, + ID: 0, + Timeout: 0, + Precision: timePrecisionNanoseconds, + Flags: 0, + }, + }, + } + sleepTicksResult = wasi.Event_t{} + sleepTicksNEvents uint32 +) + +func sleepTicks(d timeUnit) { + sleepTicksSubscription.U.U.Timeout = int64(d) + wasi.Poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents) +} func ticks() timeUnit { - var time uint64 - clock_time_get(0, 100, &time) - return timeUnit(time) + var nano int64 + wasi.Clock_time_get(0, timePrecisionNanoseconds, &nano) + return timeUnit(nano) } diff --git a/src/syscall/wasi/clock.go b/src/syscall/wasi/clock.go new file mode 100644 index 0000000000..ebb504f3b7 --- /dev/null +++ b/src/syscall/wasi/clock.go @@ -0,0 +1,7 @@ +// +build wasm,wasi + +package wasi + +//go:wasm-module wasi_unstable +//export clock_time_get +func Clock_time_get(clockid uint32, precision uint64, time *int64) (errno uint16) diff --git a/src/syscall/wasi/common.go b/src/syscall/wasi/common.go new file mode 100644 index 0000000000..1c94d7dfda --- /dev/null +++ b/src/syscall/wasi/common.go @@ -0,0 +1,15 @@ +// +build wasm + +package wasi + +import "unsafe" + +// Implements __wasi_ciovec_t and __wasi_iovec_t. +type IOVec struct { + Buf unsafe.Pointer + BufLen uint +} + +//go:wasm-module wasi_unstable +//export fd_write +func Fd_write(id uint32, iovs *IOVec, iovs_len uint, nwritten *uint) (errno uint) diff --git a/src/syscall/wasi/poll_oneoff.go b/src/syscall/wasi/poll_oneoff.go new file mode 100644 index 0000000000..c0544b8668 --- /dev/null +++ b/src/syscall/wasi/poll_oneoff.go @@ -0,0 +1,55 @@ +// +build wasm,wasi + +package wasi + +//go:wasm-module wasi_unstable +//export poll_oneoff +func Poll_oneoff(in *Subscription_t, out *Event_t, nsubscriptions uint32, nevents *uint32) (errno uint16) + +type Eventtype_t = uint8 + +const ( + Eventtype_t_clock Eventtype_t = 0 + // TODO: __wasi_eventtype_t_fd_read __wasi_eventtype_t = 1 + // TODO: __wasi_eventtype_t_fd_write __wasi_eventtype_t = 2 +) + +type ( + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L584-L588 + Subscription_t struct { + UserData uint64 + U Subscription_u_t + } + + Subscription_u_t struct { + Tag Eventtype_t + + // TODO: support fd_read/fd_write event + U Subscription_clock_t + } + + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L711-L718 + Subscription_clock_t struct { + UserData uint64 + ID uint32 + Timeout int64 + Precision int64 + Flags uint16 + } +) + +type ( + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L191-L198 + Event_t struct { + UserData uint64 + Errno uint16 + EventType Eventtype_t + + // only used for fd_read or fd_write events + // TODO: support fd_read/fd_write event + _ struct { + NBytes uint64 + Flags uint16 + } + } +) From a4567e178a77dee6f28c3072f670c91354db4823 Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 12:14:13 +0900 Subject: [PATCH 06/15] WASI: set visibility hidden for globals variables --- transform/gc.go | 23 +++++++++++++++++------ transform/gc_test.go | 4 ++-- transform/optimizer.go | 20 ++++++++++++++++++-- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/transform/gc.go b/transform/gc.go index d8258a01ba..74109021c2 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -8,9 +8,12 @@ import ( // MakeGCStackSlots converts all calls to runtime.trackPointer to explicit // stores to stack slots that are scannable by the GC. -func MakeGCStackSlots(mod llvm.Module) bool { +func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { // Check whether there are allocations at all. alloc := mod.NamedFunction("runtime.alloc") + if shouldHiveGlobals { + alloc.SetVisibility(llvm.HiddenVisibility) + } if alloc.IsNil() { // Nothing to. Make sure all remaining bits and pieces for stack // chains are neutralized. @@ -85,6 +88,9 @@ func MakeGCStackSlots(mod llvm.Module) bool { // Collect some variables used below in the loop. stackChainStart := mod.NamedGlobal("runtime.stackChainStart") + if shouldHiveGlobals { + stackChainStart.SetVisibility(llvm.HiddenVisibility) + } if stackChainStart.IsNil() { // This may be reached in a weird scenario where we call runtime.alloc but the garbage collector is unreachable. // This can be accomplished by allocating 0 bytes. @@ -96,7 +102,6 @@ func MakeGCStackSlots(mod llvm.Module) bool { } stackChainStartType := stackChainStart.Type().ElementType() stackChainStart.SetInitializer(llvm.ConstNull(stackChainStartType)) - stackChainStart.SetVisibility(llvm.HiddenVisibility) // Iterate until runtime.trackPointer has no uses left. for use := trackPointer.FirstUse(); !use.IsNil(); use = trackPointer.FirstUse() { @@ -286,7 +291,7 @@ func MakeGCStackSlots(mod llvm.Module) bool { // bitmap (bit vector) to locate all the pointers in this large global. This // bitmap allows the GC to know in advance where exactly all the pointers live // in the large globals bundle, to avoid false positives. -func AddGlobalsBitmap(mod llvm.Module) bool { +func AddGlobalsBitmap(mod llvm.Module, shouldHiveGlobals bool) bool { if mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() { return false // nothing to do: no GC in use } @@ -334,14 +339,18 @@ func AddGlobalsBitmap(mod llvm.Module) bool { // Update trackedGlobalsStart, which points to the globals bundle. trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, uintptrType) mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart) - mod.NamedGlobal("runtime.trackedGlobalsStart").SetVisibility(llvm.HiddenVisibility) + if shouldHiveGlobals { + mod.NamedGlobal("runtime.trackedGlobalsStart").SetVisibility(llvm.HiddenVisibility) + } // Update trackedGlobalsLength, which contains the length (in words) of the // globals bundle. alignment := targetData.PrefTypeAlignment(llvm.PointerType(ctx.Int8Type(), 0)) trackedGlobalsLength := llvm.ConstInt(uintptrType, targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false) mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength) - mod.NamedGlobal("runtime.trackedGlobalsLength").SetVisibility(llvm.HiddenVisibility) + if shouldHiveGlobals { + mod.NamedGlobal("runtime.trackedGlobalsLength").SetVisibility(llvm.HiddenVisibility) + } // Create a bitmap (a new global) that stores for each word in the globals // bundle whether it contains a pointer. This allows globals to be scanned @@ -360,7 +369,9 @@ func AddGlobalsBitmap(mod llvm.Module) bool { bitmapOld.ReplaceAllUsesWith(llvm.ConstBitCast(bitmapNew, bitmapOld.Type())) bitmapNew.SetInitializer(bitmapArray) bitmapNew.SetName("runtime.trackedGlobalsBitmap") - bitmapNew.SetVisibility(llvm.HiddenVisibility) + if shouldHiveGlobals { + bitmapNew.SetVisibility(llvm.HiddenVisibility) + } return true // the IR was changed } diff --git a/transform/gc_test.go b/transform/gc_test.go index 64962f0a0b..d185aed338 100644 --- a/transform/gc_test.go +++ b/transform/gc_test.go @@ -9,13 +9,13 @@ import ( func TestAddGlobalsBitmap(t *testing.T) { t.Parallel() testTransform(t, "testdata/gc-globals", func(mod llvm.Module) { - AddGlobalsBitmap(mod) + AddGlobalsBitmap(mod, false) }) } func TestMakeGCStackSlots(t *testing.T) { t.Parallel() testTransform(t, "testdata/gc-stackslots", func(mod llvm.Module) { - MakeGCStackSlots(mod) + MakeGCStackSlots(mod, false) }) } diff --git a/transform/optimizer.go b/transform/optimizer.go index 917083ce80..8cdfd94a5c 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -177,8 +177,9 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i builder.Populate(modPasses) modPasses.Run(mod) - hasGCPass := AddGlobalsBitmap(mod) - hasGCPass = MakeGCStackSlots(mod) || hasGCPass + shouldHideGlobals := shouldHideGlobalVariables(config.BuildTags()) + hasGCPass := AddGlobalsBitmap(mod, shouldHideGlobals) + hasGCPass = MakeGCStackSlots(mod, shouldHideGlobals) || hasGCPass if hasGCPass { if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return []error{errors.New("GC pass caused a verification failure")} @@ -228,3 +229,18 @@ func getFunctionsUsedInTransforms(config *compileopts.Config) []string { } return fnused } + +func shouldHideGlobalVariables(buildTags []string) bool { + for _, t := range buildTags { + // According to WASI spec, none of exports in WASI executable(Command) should no be accessed. + // Reference: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi + // + // v0.19.0 of wasmetime, which is often refered to as the reference implementation of WASI, + // does not accept any exports except functions and the only limited variables like "table" or "memory". + // See: https://github.com/bytecodealliance/wasmtime/blob/60681d7019b38a5648b5615001ffa55442a64b83/crates/wasmtime/src/linker.rs#L401-L460 + if t == "wasi" { + return true + } + } + return false +} From 7d985c5894f89ba8c6009483ac9bd1a3dfdb12ec Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 13:07:31 +0900 Subject: [PATCH 07/15] mv back syscall/wasi/* to runtime package --- src/runtime/runtime_wasm.go | 20 ++++++--- src/runtime/runtime_wasm_wasi.go | 74 ++++++++++++++++++++++++++++---- src/syscall/wasi/clock.go | 7 --- src/syscall/wasi/common.go | 15 ------- src/syscall/wasi/poll_oneoff.go | 55 ------------------------ 5 files changed, 79 insertions(+), 92 deletions(-) delete mode 100644 src/syscall/wasi/clock.go delete mode 100644 src/syscall/wasi/common.go delete mode 100644 src/syscall/wasi/poll_oneoff.go diff --git a/src/runtime/runtime_wasm.go b/src/runtime/runtime_wasm.go index 8a9fad424c..e37d432e04 100644 --- a/src/runtime/runtime_wasm.go +++ b/src/runtime/runtime_wasm.go @@ -4,18 +4,26 @@ package runtime import ( "unsafe" - - "github.com/tinygo-org/tinygo/src/syscall/wasi" ) +// Implements __wasi_iovec_t. +type __wasi_iovec_t struct { + buf unsafe.Pointer + bufLen uint +} + +//go:wasm-module wasi_unstable +//export fd_write +func fd_write(id uint32, iovs *__wasi_iovec_t, iovs_len uint, nwritten *uint) (errno uint) + func postinit() {} // Using global variables to avoid heap allocation. var ( putcharBuf = byte(0) - putcharIOVec = wasi.IOVec{ - Buf: unsafe.Pointer(&putcharBuf), - BufLen: 1, + putcharIOVec = __wasi_iovec_t{ + buf: unsafe.Pointer(&putcharBuf), + bufLen: 1, } ) @@ -24,7 +32,7 @@ func putchar(c byte) { const stdout = 1 var nwritten uint putcharBuf = c - wasi.Fd_write(stdout, &putcharIOVec, 1, &nwritten) + fd_write(stdout, &putcharIOVec, 1, &nwritten) } // Abort executes the wasm 'unreachable' instruction. diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index cfe8af1bb4..abc4063dcf 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -4,8 +4,6 @@ package runtime import ( "unsafe" - - "github.com/tinygo-org/tinygo/src/syscall/wasi" ) type timeUnit int64 @@ -32,11 +30,11 @@ const ( ) var ( - sleepTicksSubscription = wasi.Subscription_t{ + sleepTicksSubscription = __wasi_subscription_t{ UserData: 0, - U: wasi.Subscription_u_t{ - Tag: wasi.Eventtype_t_clock, - U: wasi.Subscription_clock_t{ + U: __wasi_subscription_u_t{ + Tag: __wasi_eventtype_t_clock, + U: __wasi_subscription_clock_t{ UserData: 0, ID: 0, Timeout: 0, @@ -45,17 +43,75 @@ var ( }, }, } - sleepTicksResult = wasi.Event_t{} + sleepTicksResult = __wasi_event_t{} sleepTicksNEvents uint32 ) func sleepTicks(d timeUnit) { sleepTicksSubscription.U.U.Timeout = int64(d) - wasi.Poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents) + poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents) } func ticks() timeUnit { var nano int64 - wasi.Clock_time_get(0, timePrecisionNanoseconds, &nano) + clock_time_get(0, timePrecisionNanoseconds, &nano) return timeUnit(nano) } + +// Implementations of wasi_unstable APIs + +//go:wasm-module wasi_unstable +//export clock_time_get +func clock_time_get(clockid uint32, precision uint64, time *int64) (errno uint16) + +//go:wasm-module wasi_unstable +//export poll_oneoff +func poll_oneoff(in *__wasi_subscription_t, out *__wasi_event_t, nsubscriptions uint32, nevents *uint32) (errno uint16) + +type __wasi_eventtype_t = uint8 + +const ( + __wasi_eventtype_t_clock __wasi_eventtype_t = 0 + // TODO: __wasi_eventtype_t_fd_read __wasi_eventtype_t = 1 + // TODO: __wasi_eventtype_t_fd_write __wasi_eventtype_t = 2 +) + +type ( + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L584-L588 + __wasi_subscription_t struct { + UserData uint64 + U __wasi_subscription_u_t + } + + __wasi_subscription_u_t struct { + Tag __wasi_eventtype_t + + // TODO: support fd_read/fd_write event + U __wasi_subscription_clock_t + } + + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L711-L718 + __wasi_subscription_clock_t struct { + UserData uint64 + ID uint32 + Timeout int64 + Precision int64 + Flags uint16 + } +) + +type ( + // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L191-L198 + __wasi_event_t struct { + userData uint64 + errno uint16 + eventType __wasi_eventtype_t + + // only used for fd_read or fd_write events + // TODO: support fd_read/fd_write event + _ struct { + nBytes uint64 + flags uint16 + } + } +) diff --git a/src/syscall/wasi/clock.go b/src/syscall/wasi/clock.go deleted file mode 100644 index ebb504f3b7..0000000000 --- a/src/syscall/wasi/clock.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build wasm,wasi - -package wasi - -//go:wasm-module wasi_unstable -//export clock_time_get -func Clock_time_get(clockid uint32, precision uint64, time *int64) (errno uint16) diff --git a/src/syscall/wasi/common.go b/src/syscall/wasi/common.go deleted file mode 100644 index 1c94d7dfda..0000000000 --- a/src/syscall/wasi/common.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build wasm - -package wasi - -import "unsafe" - -// Implements __wasi_ciovec_t and __wasi_iovec_t. -type IOVec struct { - Buf unsafe.Pointer - BufLen uint -} - -//go:wasm-module wasi_unstable -//export fd_write -func Fd_write(id uint32, iovs *IOVec, iovs_len uint, nwritten *uint) (errno uint) diff --git a/src/syscall/wasi/poll_oneoff.go b/src/syscall/wasi/poll_oneoff.go deleted file mode 100644 index c0544b8668..0000000000 --- a/src/syscall/wasi/poll_oneoff.go +++ /dev/null @@ -1,55 +0,0 @@ -// +build wasm,wasi - -package wasi - -//go:wasm-module wasi_unstable -//export poll_oneoff -func Poll_oneoff(in *Subscription_t, out *Event_t, nsubscriptions uint32, nevents *uint32) (errno uint16) - -type Eventtype_t = uint8 - -const ( - Eventtype_t_clock Eventtype_t = 0 - // TODO: __wasi_eventtype_t_fd_read __wasi_eventtype_t = 1 - // TODO: __wasi_eventtype_t_fd_write __wasi_eventtype_t = 2 -) - -type ( - // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L584-L588 - Subscription_t struct { - UserData uint64 - U Subscription_u_t - } - - Subscription_u_t struct { - Tag Eventtype_t - - // TODO: support fd_read/fd_write event - U Subscription_clock_t - } - - // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L711-L718 - Subscription_clock_t struct { - UserData uint64 - ID uint32 - Timeout int64 - Precision int64 - Flags uint16 - } -) - -type ( - // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L191-L198 - Event_t struct { - UserData uint64 - Errno uint16 - EventType Eventtype_t - - // only used for fd_read or fd_write events - // TODO: support fd_read/fd_write event - _ struct { - NBytes uint64 - Flags uint16 - } - } -) From cb4d61f4f1d7e1d5f6c990cbe5bcb558313136e4 Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 13:53:27 +0900 Subject: [PATCH 08/15] WASI: add test --- .circleci/config.yml | 6 ++++++ main_test.go | 10 +++++++++- targets/wasi.json | 2 +- transform/gc.go | 14 ++++++++------ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eedf08d27d..29e3de8aa4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,6 +44,11 @@ commands: command: | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt install ./google-chrome-stable_current_amd64.deb + install-wasmtime: + steps: + - run: + name: "Install wasmtime" + command: curl https://wasmtime.dev/install.sh -sSf | bash install-xtensa-toolchain: parameters: variant: @@ -91,6 +96,7 @@ commands: llvm: "<>" - install-node - install-chrome + - install-wasmtime - restore_cache: keys: - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} diff --git a/main_test.go b/main_test.go index f66117b90a..3434cc27d3 100644 --- a/main_test.go +++ b/main_test.go @@ -97,6 +97,10 @@ func TestCompiler(t *testing.T) { t.Run("WebAssembly", func(t *testing.T) { runPlatTests("wasm", matches, t) }) + + t.Run("WASI", func(t *testing.T) { + runPlatTests("wasi", matches, t) + }) } } } @@ -109,7 +113,6 @@ func runPlatTests(target string, matches []string, t *testing.T) { t.Run(filepath.Base(path), func(t *testing.T) { t.Parallel() - runTest(path, target, t) }) } @@ -161,6 +164,11 @@ func runTest(path, target string, t *testing.T) { PrintSizes: "", WasmAbi: "js", } + + if target == "wasi" { + config.WasmAbi = "generic" + } + binary := filepath.Join(tmpdir, "test") err = runBuild("./"+path, binary, config) if err != nil { diff --git a/targets/wasi.json b/targets/wasi.json index 09ab606d72..edbed161fa 100644 --- a/targets/wasi.json +++ b/targets/wasi.json @@ -18,5 +18,5 @@ "--no-demangle", "{root}/lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a" ], - "emulator": ["node", "targets/wasm_exec.js"] + "emulator": ["wasmtime"] } diff --git a/transform/gc.go b/transform/gc.go index 74109021c2..38edc106f2 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -11,9 +11,6 @@ import ( func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { // Check whether there are allocations at all. alloc := mod.NamedFunction("runtime.alloc") - if shouldHiveGlobals { - alloc.SetVisibility(llvm.HiddenVisibility) - } if alloc.IsNil() { // Nothing to. Make sure all remaining bits and pieces for stack // chains are neutralized. @@ -22,12 +19,17 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { } stackChainStart := mod.NamedGlobal("runtime.stackChainStart") if !stackChainStart.IsNil() { + stackChainStart.SetVisibility(llvm.HiddenVisibility) stackChainStart.SetInitializer(llvm.ConstNull(stackChainStart.Type().ElementType())) stackChainStart.SetGlobalConstant(true) } return false } + if shouldHiveGlobals { + alloc.SetVisibility(llvm.HiddenVisibility) + } + trackPointer := mod.NamedFunction("runtime.trackPointer") if trackPointer.IsNil() || trackPointer.FirstUse().IsNil() { return false // nothing to do @@ -88,9 +90,6 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { // Collect some variables used below in the loop. stackChainStart := mod.NamedGlobal("runtime.stackChainStart") - if shouldHiveGlobals { - stackChainStart.SetVisibility(llvm.HiddenVisibility) - } if stackChainStart.IsNil() { // This may be reached in a weird scenario where we call runtime.alloc but the garbage collector is unreachable. // This can be accomplished by allocating 0 bytes. @@ -100,6 +99,9 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { } return false } + if shouldHiveGlobals { + stackChainStart.SetVisibility(llvm.HiddenVisibility) + } stackChainStartType := stackChainStart.Type().ElementType() stackChainStart.SetInitializer(llvm.ConstNull(stackChainStartType)) From f40eebb653f8de837d0856410e9e0f3b6959efc9 Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 14:01:27 +0900 Subject: [PATCH 09/15] unexport wasi types --- src/runtime/runtime_wasm_wasi.go | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index abc4063dcf..99a34ebb4f 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -31,15 +31,15 @@ const ( var ( sleepTicksSubscription = __wasi_subscription_t{ - UserData: 0, - U: __wasi_subscription_u_t{ - Tag: __wasi_eventtype_t_clock, - U: __wasi_subscription_clock_t{ - UserData: 0, - ID: 0, - Timeout: 0, - Precision: timePrecisionNanoseconds, - Flags: 0, + userData: 0, + u: __wasi_subscription_u_t{ + tag: __wasi_eventtype_t_clock, + u: __wasi_subscription_clock_t{ + userData: 0, + id: 0, + timeout: 0, + precision: timePrecisionNanoseconds, + flags: 0, }, }, } @@ -48,7 +48,7 @@ var ( ) func sleepTicks(d timeUnit) { - sleepTicksSubscription.U.U.Timeout = int64(d) + sleepTicksSubscription.u.u.timeout = int64(d) poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents) } @@ -79,24 +79,24 @@ const ( type ( // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L584-L588 __wasi_subscription_t struct { - UserData uint64 - U __wasi_subscription_u_t + userData uint64 + u __wasi_subscription_u_t } __wasi_subscription_u_t struct { - Tag __wasi_eventtype_t + tag __wasi_eventtype_t // TODO: support fd_read/fd_write event - U __wasi_subscription_clock_t + u __wasi_subscription_clock_t } // https://github.com/wasmerio/wasmer/blob/1.0.0-alpha3/lib/wasi/src/syscalls/types.rs#L711-L718 __wasi_subscription_clock_t struct { - UserData uint64 - ID uint32 - Timeout int64 - Precision int64 - Flags uint16 + userData uint64 + id uint32 + timeout int64 + precision int64 + flags uint16 } ) From 507010eed45a3ff193dd9e66af6ea2ba0ca048a7 Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 15:05:45 +0900 Subject: [PATCH 10/15] WASI test: fix wasmtime path --- .circleci/config.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 29e3de8aa4..5c999d8632 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,7 +48,9 @@ commands: steps: - run: name: "Install wasmtime" - command: curl https://wasmtime.dev/install.sh -sSf | bash + command: | + curl https://wasmtime.dev/install.sh -sSf | bash + sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime install-xtensa-toolchain: parameters: variant: @@ -99,7 +101,7 @@ commands: - install-wasmtime - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - run: go install -tags=llvm<> . @@ -140,11 +142,12 @@ commands: gcc-avr \ avr-libc - install-node + - install-wasmtime - install-xtensa-toolchain: variant: "linux-amd64" - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - restore_cache: @@ -200,11 +203,12 @@ commands: gcc-avr \ avr-libc - install-node + - install-wasmtime - install-xtensa-toolchain: variant: "linux-amd64" - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - restore_cache: @@ -277,7 +281,7 @@ commands: variant: "macos" - restore_cache: keys: - - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} + - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- - go-cache-macos-v2-{{ checksum "go.mod" }} - restore_cache: keys: From 6e68e97469691bd2d8c1227d144b7cab01a7fc74 Mon Sep 17 00:00:00 2001 From: mathetake Date: Sat, 19 Sep 2020 17:44:42 +0900 Subject: [PATCH 11/15] stop changing visibility of runtime.alloc --- .circleci/config.yml | 8 ++++---- transform/gc.go | 8 +++----- transform/optimizer.go | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c999d8632..15ca09e010 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -101,7 +101,7 @@ commands: - install-wasmtime - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - run: go install -tags=llvm<> . @@ -147,7 +147,7 @@ commands: variant: "linux-amd64" - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - restore_cache: @@ -208,7 +208,7 @@ commands: variant: "linux-amd64" - restore_cache: keys: - - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- + - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - restore_cache: @@ -281,7 +281,7 @@ commands: variant: "macos" - restore_cache: keys: - - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}- + - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-macos-v2-{{ checksum "go.mod" }} - restore_cache: keys: diff --git a/transform/gc.go b/transform/gc.go index 38edc106f2..86060d3b1d 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -19,17 +19,15 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { } stackChainStart := mod.NamedGlobal("runtime.stackChainStart") if !stackChainStart.IsNil() { - stackChainStart.SetVisibility(llvm.HiddenVisibility) + if shouldHiveGlobals { + stackChainStart.SetVisibility(llvm.HiddenVisibility) + } stackChainStart.SetInitializer(llvm.ConstNull(stackChainStart.Type().ElementType())) stackChainStart.SetGlobalConstant(true) } return false } - if shouldHiveGlobals { - alloc.SetVisibility(llvm.HiddenVisibility) - } - trackPointer := mod.NamedFunction("runtime.trackPointer") if trackPointer.IsNil() || trackPointer.FirstUse().IsNil() { return false // nothing to do diff --git a/transform/optimizer.go b/transform/optimizer.go index 8cdfd94a5c..c7058208f7 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -232,10 +232,10 @@ func getFunctionsUsedInTransforms(config *compileopts.Config) []string { func shouldHideGlobalVariables(buildTags []string) bool { for _, t := range buildTags { - // According to WASI spec, none of exports in WASI executable(Command) should no be accessed. + // According to WASI spec, global variables in WASI executable(Command) should not be exported. // Reference: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi // - // v0.19.0 of wasmetime, which is often refered to as the reference implementation of WASI, + // v0.19.0 of wasmtime, which is often referred to as the reference implementation of WASI, // does not accept any exports except functions and the only limited variables like "table" or "memory". // See: https://github.com/bytecodealliance/wasmtime/blob/60681d7019b38a5648b5615001ffa55442a64b83/crates/wasmtime/src/linker.rs#L401-L460 if t == "wasi" { From 6ef9dc02f5cd3a7af28897d23a2711eba9890e29 Mon Sep 17 00:00:00 2001 From: mathetake Date: Wed, 23 Sep 2020 09:11:11 +0900 Subject: [PATCH 12/15] use GOOS=linux, GOARCH=arm for wasi target Signed-off-by: mathetake --- loader/goroot.go | 39 ++------------------------------------- loader/loader.go | 5 +---- main_test.go | 8 ++++---- src/time/zoneinfo_wasi.go | 13 ------------- targets/wasi.json | 6 +++--- 5 files changed, 10 insertions(+), 61 deletions(-) delete mode 100644 src/time/zoneinfo_wasi.go diff --git a/loader/goroot.go b/loader/goroot.go index 4b43fe7b84..5fa4bea071 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -15,7 +15,6 @@ import ( "path" "path/filepath" "runtime" - "strings" "sync" "github.com/tinygo-org/tinygo/compileopts" @@ -89,10 +88,7 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) { return "", err } } - err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride( - needsSyscallPackage(config.BuildTags()), - needsTimePackage(config.BuildTags()), - )) + err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride(needsSyscallPackage(config.BuildTags()))) if err != nil { return "", err } @@ -168,17 +164,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides return err } for _, e := range gorootEntries { - if isMergeTargetEntry(importPath, e.Name()) { - // In this case, we have files both from TinyGo and Go. - // As of now, this is only for WASI to have *_wasi.go files as replacements for *_js.go files - newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) - oldname := filepath.Join(goroot, "src", importPath, e.Name()) - err := symlink(oldname, newname) - if err != nil { - return err - } - continue - } if !e.IsDir() { // Don't merge in files from Go. Otherwise we'd end up with a // weird syscall package with files from both roots. @@ -200,11 +185,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides return nil } -func isMergeTargetEntry(importPath string, name string) bool { - // TODO: add crypto/rand - return importPath == "time" && !strings.HasSuffix(name, "_js.go") -} - // needsSyscallPackage returns whether the syscall package should be overriden // with the TinyGo version. This is the case on some targets. func needsSyscallPackage(buildTags []string) bool { @@ -216,20 +196,9 @@ func needsSyscallPackage(buildTags []string) bool { return false } -// needsTimePackage returns whether the time package should be merged -// with the TinyGo version. This is the case on some targets. -func needsTimePackage(buildTags []string) bool { - for _, tag := range buildTags { - if tag == "wasi" { - return true - } - } - return false -} - // The boolean indicates whether to merge the subdirs. True means merge, false // means use the TinyGo version. -func pathsToOverride(needsSyscallPackage, needsTimePackage bool) map[string]bool { +func pathsToOverride(needsSyscallPackage bool) map[string]bool { paths := map[string]bool{ "/": true, "device/": false, @@ -248,10 +217,6 @@ func pathsToOverride(needsSyscallPackage, needsTimePackage bool) map[string]bool if needsSyscallPackage { paths["syscall/"] = true // include syscall/js } - - if needsTimePackage { - paths["time/"] = true - } return paths } diff --git a/loader/loader.go b/loader/loader.go index 159fa8a195..6200cc574b 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -223,10 +223,7 @@ func (p *Program) getOriginalPath(path string) string { originalPath = realgorootPath } maybeInTinyGoRoot := false - for prefix := range pathsToOverride( - needsSyscallPackage(p.config.BuildTags()), - needsTimePackage(p.config.BuildTags()), - ) { + for prefix := range pathsToOverride(needsSyscallPackage(p.config.BuildTags())) { if !strings.HasPrefix(relpath, prefix) { continue } diff --git a/main_test.go b/main_test.go index 3434cc27d3..e12b165ec0 100644 --- a/main_test.go +++ b/main_test.go @@ -97,12 +97,12 @@ func TestCompiler(t *testing.T) { t.Run("WebAssembly", func(t *testing.T) { runPlatTests("wasm", matches, t) }) - - t.Run("WASI", func(t *testing.T) { - runPlatTests("wasi", matches, t) - }) } } + + t.Run("WASI", func(t *testing.T) { + runPlatTests("wasi", matches, t) + }) } func runPlatTests(target string, matches []string, t *testing.T) { diff --git a/src/time/zoneinfo_wasi.go b/src/time/zoneinfo_wasi.go deleted file mode 100644 index ce3f8786b2..0000000000 --- a/src/time/zoneinfo_wasi.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build wasi - -// should be replaced with original time/zoneinfo_js.go - -package time - -// TODO: https://github.com/WebAssembly/WASI/issues/25 - -var zoneSources []string // TODO: - -func initLocal() { - // TODO: -} diff --git a/targets/wasi.json b/targets/wasi.json index edbed161fa..97f7848f9b 100644 --- a/targets/wasi.json +++ b/targets/wasi.json @@ -1,8 +1,8 @@ { "llvm-target": "wasm32--wasi", - "build-tags": ["js", "wasm", "wasi"], - "goos": "js", - "goarch": "wasm", + "build-tags": ["wasm", "wasi"], + "goos": "linux", + "goarch": "arm", "compiler": "clang", "linker": "wasm-ld", "cflags": [ From d852bba278bd3993a6faf2da6203c938062be78b Mon Sep 17 00:00:00 2001 From: mathetake Date: Wed, 23 Sep 2020 09:23:58 +0900 Subject: [PATCH 13/15] WASI: fix build tags for os/runtime packages Signed-off-by: mathetake --- src/os/file_unix.go | 2 +- src/runtime/arch_arm.go | 2 +- src/runtime/runtime_unix.go | 2 +- src/runtime/runtime_unix_heap.go | 2 +- src/runtime/runtime_unix_noheap.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/os/file_unix.go b/src/os/file_unix.go index e77d6773fb..84d738b0ca 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -1,4 +1,4 @@ -// +build darwin linux,!baremetal freebsd,!baremetal +// +build darwin linux,!baremetal,!wasi freebsd,!baremetal package os diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go index 0a98f6d532..e2dc4b5bfe 100644 --- a/src/runtime/arch_arm.go +++ b/src/runtime/arch_arm.go @@ -1,4 +1,4 @@ -// +build arm,!baremetal arm,arm7tdmi +// +build arm,!baremetal,!wasm arm,arm7tdmi package runtime diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index a3c2b4471b..ef4b825856 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -1,4 +1,4 @@ -// +build darwin linux,!baremetal freebsd,!baremetal +// +build darwin linux,!baremetal,!wasi freebsd,!baremetal // +build !nintendoswitch package runtime diff --git a/src/runtime/runtime_unix_heap.go b/src/runtime/runtime_unix_heap.go index 14168f4387..1aefd2f615 100644 --- a/src/runtime/runtime_unix_heap.go +++ b/src/runtime/runtime_unix_heap.go @@ -1,4 +1,4 @@ -// +build darwin linux,!baremetal freebsd,!baremetal +// +build darwin linux,!baremetal,!wasi freebsd,!baremetal // +build !nintendoswitch // +build gc.conservative gc.leaking diff --git a/src/runtime/runtime_unix_noheap.go b/src/runtime/runtime_unix_noheap.go index bf7f675987..f9826ac5d7 100644 --- a/src/runtime/runtime_unix_noheap.go +++ b/src/runtime/runtime_unix_noheap.go @@ -1,4 +1,4 @@ -// +build darwin linux,!baremetal freebsd,!baremetal +// +build darwin linux,!baremetal,!wasi freebsd,!baremetal // +build !nintendoswitch From 5140e530bcea8edd1fa80750690909cf3a1f2fe1 Mon Sep 17 00:00:00 2001 From: mathetake Date: Wed, 23 Sep 2020 09:34:33 +0900 Subject: [PATCH 14/15] run WASI test only on Linux Signed-off-by: mathetake --- main_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main_test.go b/main_test.go index e12b165ec0..ac01baafaa 100644 --- a/main_test.go +++ b/main_test.go @@ -98,11 +98,11 @@ func TestCompiler(t *testing.T) { runPlatTests("wasm", matches, t) }) } - } - t.Run("WASI", func(t *testing.T) { - runPlatTests("wasi", matches, t) - }) + t.Run("WASI", func(t *testing.T) { + runPlatTests("wasi", matches, t) + }) + } } func runPlatTests(target string, matches []string, t *testing.T) { From 62a9104ec6eec18e3014e76eedcc6d3387aa5494 Mon Sep 17 00:00:00 2001 From: mathetake Date: Wed, 23 Sep 2020 10:02:52 +0900 Subject: [PATCH 15/15] set InternalLinkage instead of changing visibility Signed-off-by: mathetake --- loader/loader.go | 2 +- transform/gc.go | 24 +++++++----------------- transform/gc_test.go | 4 ++-- transform/optimizer.go | 20 ++------------------ transform/testdata/gc-globals.out.ll | 6 +++--- transform/testdata/gc-stackslots.out.ll | 2 +- 6 files changed, 16 insertions(+), 42 deletions(-) diff --git a/loader/loader.go b/loader/loader.go index 6200cc574b..847acc2584 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -166,6 +166,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t Err: err, } } + return nil, err } if config.TestConfig.CompileTestBinary { // When creating a test binary, `go list` will list two or three @@ -229,7 +230,6 @@ func (p *Program) getOriginalPath(path string) string { } maybeInTinyGoRoot = true } - if maybeInTinyGoRoot { tinygoPath := filepath.Join(goenv.Get("TINYGOROOT"), "src", relpath) if _, err := os.Stat(tinygoPath); err == nil { diff --git a/transform/gc.go b/transform/gc.go index 86060d3b1d..0688604986 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -8,7 +8,7 @@ import ( // MakeGCStackSlots converts all calls to runtime.trackPointer to explicit // stores to stack slots that are scannable by the GC. -func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { +func MakeGCStackSlots(mod llvm.Module) bool { // Check whether there are allocations at all. alloc := mod.NamedFunction("runtime.alloc") if alloc.IsNil() { @@ -19,9 +19,7 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { } stackChainStart := mod.NamedGlobal("runtime.stackChainStart") if !stackChainStart.IsNil() { - if shouldHiveGlobals { - stackChainStart.SetVisibility(llvm.HiddenVisibility) - } + stackChainStart.SetLinkage(llvm.InternalLinkage) stackChainStart.SetInitializer(llvm.ConstNull(stackChainStart.Type().ElementType())) stackChainStart.SetGlobalConstant(true) } @@ -97,9 +95,7 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { } return false } - if shouldHiveGlobals { - stackChainStart.SetVisibility(llvm.HiddenVisibility) - } + stackChainStart.SetLinkage(llvm.InternalLinkage) stackChainStartType := stackChainStart.Type().ElementType() stackChainStart.SetInitializer(llvm.ConstNull(stackChainStartType)) @@ -291,7 +287,7 @@ func MakeGCStackSlots(mod llvm.Module, shouldHiveGlobals bool) bool { // bitmap (bit vector) to locate all the pointers in this large global. This // bitmap allows the GC to know in advance where exactly all the pointers live // in the large globals bundle, to avoid false positives. -func AddGlobalsBitmap(mod llvm.Module, shouldHiveGlobals bool) bool { +func AddGlobalsBitmap(mod llvm.Module) bool { if mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() { return false // nothing to do: no GC in use } @@ -339,18 +335,14 @@ func AddGlobalsBitmap(mod llvm.Module, shouldHiveGlobals bool) bool { // Update trackedGlobalsStart, which points to the globals bundle. trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, uintptrType) mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart) - if shouldHiveGlobals { - mod.NamedGlobal("runtime.trackedGlobalsStart").SetVisibility(llvm.HiddenVisibility) - } + mod.NamedGlobal("runtime.trackedGlobalsStart").SetLinkage(llvm.InternalLinkage) // Update trackedGlobalsLength, which contains the length (in words) of the // globals bundle. alignment := targetData.PrefTypeAlignment(llvm.PointerType(ctx.Int8Type(), 0)) trackedGlobalsLength := llvm.ConstInt(uintptrType, targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false) + mod.NamedGlobal("runtime.trackedGlobalsLength").SetLinkage(llvm.InternalLinkage) mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength) - if shouldHiveGlobals { - mod.NamedGlobal("runtime.trackedGlobalsLength").SetVisibility(llvm.HiddenVisibility) - } // Create a bitmap (a new global) that stores for each word in the globals // bundle whether it contains a pointer. This allows globals to be scanned @@ -369,9 +361,7 @@ func AddGlobalsBitmap(mod llvm.Module, shouldHiveGlobals bool) bool { bitmapOld.ReplaceAllUsesWith(llvm.ConstBitCast(bitmapNew, bitmapOld.Type())) bitmapNew.SetInitializer(bitmapArray) bitmapNew.SetName("runtime.trackedGlobalsBitmap") - if shouldHiveGlobals { - bitmapNew.SetVisibility(llvm.HiddenVisibility) - } + bitmapNew.SetLinkage(llvm.InternalLinkage) return true // the IR was changed } diff --git a/transform/gc_test.go b/transform/gc_test.go index d185aed338..64962f0a0b 100644 --- a/transform/gc_test.go +++ b/transform/gc_test.go @@ -9,13 +9,13 @@ import ( func TestAddGlobalsBitmap(t *testing.T) { t.Parallel() testTransform(t, "testdata/gc-globals", func(mod llvm.Module) { - AddGlobalsBitmap(mod, false) + AddGlobalsBitmap(mod) }) } func TestMakeGCStackSlots(t *testing.T) { t.Parallel() testTransform(t, "testdata/gc-stackslots", func(mod llvm.Module) { - MakeGCStackSlots(mod, false) + MakeGCStackSlots(mod) }) } diff --git a/transform/optimizer.go b/transform/optimizer.go index c7058208f7..917083ce80 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -177,9 +177,8 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i builder.Populate(modPasses) modPasses.Run(mod) - shouldHideGlobals := shouldHideGlobalVariables(config.BuildTags()) - hasGCPass := AddGlobalsBitmap(mod, shouldHideGlobals) - hasGCPass = MakeGCStackSlots(mod, shouldHideGlobals) || hasGCPass + hasGCPass := AddGlobalsBitmap(mod) + hasGCPass = MakeGCStackSlots(mod) || hasGCPass if hasGCPass { if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return []error{errors.New("GC pass caused a verification failure")} @@ -229,18 +228,3 @@ func getFunctionsUsedInTransforms(config *compileopts.Config) []string { } return fnused } - -func shouldHideGlobalVariables(buildTags []string) bool { - for _, t := range buildTags { - // According to WASI spec, global variables in WASI executable(Command) should not be exported. - // Reference: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi - // - // v0.19.0 of wasmtime, which is often referred to as the reference implementation of WASI, - // does not accept any exports except functions and the only limited variables like "table" or "memory". - // See: https://github.com/bytecodealliance/wasmtime/blob/60681d7019b38a5648b5615001ffa55442a64b83/crates/wasmtime/src/linker.rs#L401-L460 - if t == "wasi" { - return true - } - } - return false -} diff --git a/transform/testdata/gc-globals.out.ll b/transform/testdata/gc-globals.out.ll index 45b80b7d84..905e8eafa1 100644 --- a/transform/testdata/gc-globals.out.ll +++ b/transform/testdata/gc-globals.out.ll @@ -7,11 +7,11 @@ target triple = "wasm32-unknown-unknown-wasm" @globalInt = global i32 5 @constString = constant %runtime._string zeroinitializer @constInterface = constant %runtime._interface zeroinitializer -@runtime.trackedGlobalsLength = global i32 4 +@runtime.trackedGlobalsLength = internal global i32 4 @runtime.trackedGlobalsBitmap = external global [0 x i8] -@runtime.trackedGlobalsStart = global i32 ptrtoint ({ %runtime._string, %runtime._interface }* @tinygo.trackedGlobals to i32) +@runtime.trackedGlobalsStart = internal global i32 ptrtoint ({ %runtime._string, %runtime._interface }* @tinygo.trackedGlobals to i32) @tinygo.trackedGlobals = internal unnamed_addr global { %runtime._string, %runtime._interface } zeroinitializer -@runtime.trackedGlobalsBitmap.1 = global [1 x i8] c"\09" +@runtime.trackedGlobalsBitmap.1 = internal global [1 x i8] c"\09" define void @main() { %1 = load i32, i32* @globalInt diff --git a/transform/testdata/gc-stackslots.out.ll b/transform/testdata/gc-stackslots.out.ll index efcc3ed25e..9acb0abe7d 100644 --- a/transform/testdata/gc-stackslots.out.ll +++ b/transform/testdata/gc-stackslots.out.ll @@ -3,7 +3,7 @@ target triple = "wasm32-unknown-unknown-wasm" %runtime.stackChainObject = type { %runtime.stackChainObject*, i32 } -@runtime.stackChainStart = global %runtime.stackChainObject* null +@runtime.stackChainStart = internal global %runtime.stackChainObject* null @someGlobal = global i8 3 declare void @runtime.trackPointer(i8* nocapture readonly)