Skip to content

Commit

Permalink
support WASI target (#1373)
Browse files Browse the repository at this point in the history
* 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

* 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".

* merge syscall_{baremetal,wasi}.go

* fix js target build

* mv wasi functions to syscall/wasi && implement sleepTicks

* WASI: set visibility hidden for globals variables

* mv back syscall/wasi/* to runtime package

* WASI: add test

* unexport wasi types

* WASI test: fix wasmtime path

* stop changing visibility of runtime.alloc

* use GOOS=linux, GOARCH=arm for wasi target

Signed-off-by: mathetake <takeshi@tetrate.io>

* WASI: fix build tags for os/runtime packages

Signed-off-by: mathetake <takeshi@tetrate.io>

* run WASI test only on Linux

Signed-off-by: mathetake <takeshi@tetrate.io>

* set InternalLinkage instead of changing visibility

Signed-off-by: mathetake <takeshi@tetrate.io>
  • Loading branch information
Takeshi Yoneda authored Sep 29, 2020
1 parent d39c7ab commit f50ad35
Show file tree
Hide file tree
Showing 17 changed files with 241 additions and 72 deletions.
10 changes: 10 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ 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
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
install-xtensa-toolchain:
parameters:
variant:
Expand Down Expand Up @@ -91,6 +98,7 @@ commands:
llvm: "<<parameters.llvm>>"
- install-node
- install-chrome
- install-wasmtime
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
Expand Down Expand Up @@ -134,6 +142,7 @@ commands:
gcc-avr \
avr-libc
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
Expand Down Expand Up @@ -194,6 +203,7 @@ commands:
gcc-avr \
avr-libc
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
Expand Down
2 changes: 1 addition & 1 deletion loader/goroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// 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
}
}
Expand Down
10 changes: 9 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ func TestCompiler(t *testing.T) {
runPlatTests("wasm", matches, t)
})
}

t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", matches, t)
})
}
}

Expand All @@ -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)
})
}
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/os/file_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal

package os

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/arch_arm.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build arm,!baremetal arm,arm7tdmi
// +build arm,!baremetal,!wasm arm,arm7tdmi

package runtime

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
// +build !nintendoswitch

package runtime
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime_unix_heap.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
// +build !nintendoswitch

// +build gc.conservative gc.leaking
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime_unix_noheap.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build darwin linux,!baremetal freebsd,!baremetal
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal

// +build !nintendoswitch

Expand Down
66 changes: 7 additions & 59 deletions src/runtime/runtime_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,26 @@

package runtime

import "unsafe"

type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
import (
"unsafe"
)

// Implements __wasi_ciovec_t and __wasi_iovec_t.
type wasiIOVec struct {
// 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 *wasiIOVec, iovs_len uint, nwritten *uint) (errno uint)
func fd_write(id uint32, iovs *__wasi_iovec_t, iovs_len uint, nwritten *uint) (errno uint)

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)
putcharIOVec = wasiIOVec{
putcharIOVec = __wasi_iovec_t{
buf: unsafe.Pointer(&putcharBuf),
bufLen: 1,
}
Expand All @@ -44,49 +35,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()
Expand Down
59 changes: 59 additions & 0 deletions src/runtime/runtime_wasm_js.go
Original file line number Diff line number Diff line change
@@ -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
117 changes: 117 additions & 0 deletions src/runtime/runtime_wasm_wasi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// +build wasm,wasi

package runtime

import (
"unsafe"
)

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()
}

func ticksToNanoseconds(ticks timeUnit) int64 {
return int64(ticks)
}

func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(ns)
}

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)
poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
}

func ticks() timeUnit {
var nano int64
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
}
}
)
2 changes: 1 addition & 1 deletion src/syscall/syscall_baremetal.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build baremetal
// +build baremetal wasi

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/tables_baremetal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading

0 comments on commit f50ad35

Please sign in to comment.