Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support WASI target #1373

Merged
merged 15 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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