The input is a wasm binary module.
The output is machine code.
It is only a compiler. A runtime environment for the compiled program, including all import functions, needs to be implemented separately. (But see wasys for an example runtime.)
Single-pass, fast ahead-of-time compilation. Early functions can be executed while the latter functions are still being compiled, even while the source is still being downloaded.
The generated code requires minimal runtime support; it's designed to be executed in an isolated environment. Calling standard library ABIs is not directly supported, but see wasys for an example which exposes syscalls as WebAssembly import functions.
Supports snapshot-and-restore across compiler versions and CPU architectures.
Supports breakpoint debugging via recompilation.
Cross-compilation is supported via Go build tags. If
wagamd64is specified, the x86-64 code generator is used regardless of host architecture, and CPU feature detection is disabled with pessimistic assumptions. Likewise for
wagarm64(but feature detection is not used for ARM64).
Supports WebAssembly version 1 (wasm32). No wasm extensions are supported.
Supports x86-64 and ARM64 code generation.
Generated x86-64 code requires SSE4.1 ROUNDSS/ROUNDSD instructions.
Spectre variant 1: Out-of-bounds linear memory access detection requires that addressable but unallocated memory is inaccessible. It naturally prevents conditional branch exploitation.
Spectre variant 2: On x86, Retpoline is used to protect the runtime environment (although user programs shouldn't be able to inject arbitrary addresses into the branch target buffer).
Requires Linux, Go, make, cmake, clang++ and libcapstone. About 75% of the WebAssembly spec testsuite is run, by first converting the tests to binary format:
git clone --recurse-submodules https://github.com/tsavola/wag.git
$ go get github.com/tsavola/wag/cmd/wasys $ wasys -v $GOPATH/src/github.com/tsavola/wag/testdata/hello.wasm import write(i32, i32, i32) i32 import openat(i32, i32, i32, i32) i32 import read(i32, i32, i32) i32 import close(i32) i32 import pipe2(i32, i32) i32 import _exit(i32) hello, world
=== RUN TestSnapshot --- PASS: TestSnapshot (0.00s) snapshot_test.go:80: print output: 10 --- snapshotting --- current memory limit: 0x6a96051ca000 current stack ptr: 0x6a960533ffc0 globals+memory addr: 0x6a96051ba000 stack addr: 0x6a960533f000 globals+memory size: 65536 memory size: 65536 stack offset: 4032 stacktrace: #1 func-3 #2 func-2 --- shot snapped --- 20 snapshot_test.go:88: resuming snapshot_test.go:100: print output: 20 30 330 40 440