In case anyone else wants to play with Zig on webassembly, here's what you need to do to make it work on a mac today.
You'll need LLVM to output to the WASM target. This has just been added by default in trunk, so if LLVM >7 is available, you might be able to just brew install llvm
.
If you have wasm support already you should see:
$ llc --version
LLVM (http://llvm.org/):
LLVM version 7.0.0
Optimized build.
Default target: x86_64-apple-darwin17.7.0
Host CPU: skylake
Registered Targets:
...
wasm32 - WebAssembly 32-bit
wasm64 - WebAssembly 64-bit
If wasm32 / wasm64 isn't in the list, you need to enable wasm support explicitly in llvm by recompiling LLVM with the -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly
flag.
With homebrew the easiest way to do this is to edit /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/llvm.rb and scroll down to this section:
args = %W[
-DLIBOMP_ARCH=x86_64
-DLINK_POLLY_INTO_TOOLS=ON
-DLLVM_BUILD_EXTERNAL_COMPILER_RT=ON
-DLLVM_BUILD_LLVM_DYLIB=ON
-DLLVM_ENABLE_EH=ON
-DLLVM_ENABLE_FFI=ON
-DLLVM_ENABLE_LIBCXX=ON
-DLLVM_ENABLE_RTTI=ON
-DLLVM_INCLUDE_DOCS=OFF
-DLLVM_INSTALL_UTILS=ON
-DLLVM_OPTIMIZED_TABLEGEN=ON
-DLLVM_TARGETS_TO_BUILD=all
-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly # <--- Add this line!
-DWITH_POLLY=ON
-DFFI_INCLUDE_DIR=#{Formula["libffi"].opt_lib}/libffi-#{Formula["libffi"].version}/include
-DFFI_LIBRARY_DIR=#{Formula["libffi"].opt_lib}
]
And add the -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly
line in there somewhere like I've done above.
Then brew install -s llvm
. This will rebuild llvm from source, and will probably take ~3-5 hours.
$ git clone https://github.com/ziglang/zig.git
$ cd zig
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@7/
$ make install
extern fn inc(a: i32) i32;
export fn addInc(a: i32, b: i32) i32 {
return inc(a) + b;
}
Then build it and stuff:
For some reason zig's built-in wasm linker doesn't export symbols properly. You can work around that using wasm-ld
which is included as part of llvm.
$ zig build-obj --release-small --target-arch wasm32 wasm.zig
$ wasm-ld wasm.o -o xyz -O2 --no-entry --allow-undefined
If you want to see what the resulting wasm looks like, you can view it in text format using WABT (brew install wabt
) then:
$ wasm2wat xyz
You should see some sweet (()()()()) code.
Here's a dumb nodejs script to invoke your wasm code:
const fs = require('fs')
const m = new WebAssembly.Module(fs.readFileSync('xyz'))
const env = {
inc(x) { return x+1 }
}
const i = new WebAssembly.Instance(m, {env})
console.log(i.exports.addInc(1,2))
$ node test.js
4
💃🕺🏾
For anyone wondering, with ^0.9 this is not necessary anymore. You can just use a shared library with target wasm32 (or as the triplet: wasm32-freestanding-none)
(Resolved in ziglang/zig#1570)