Skip to content

Instantly share code, notes, and snippets.

@josephg
Last active March 7, 2024 06:58
Show Gist options
  • Save josephg/873a21d4558fd69aeccea19c3df96672 to your computer and use it in GitHub Desktop.
Save josephg/873a21d4558fd69aeccea19c3df96672 to your computer and use it in GitHub Desktop.
Getting Zig compiling to WASM

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.

1. Get LLVM 7 compiled with webassembly support.

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.

2. Build zig from source.

$ 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

3. Write some zig code to test.

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.

4. Run it through JS

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

💃🕺🏾

@pims
Copy link

pims commented Aug 24, 2021

$ zig version
0.8.0

zig build-lib src/main.zig -target wasm32-freestanding -dynamic -OReleaseSmall

# produces main.wasm in the current directory

@jackdbd
Copy link

jackdbd commented Apr 15, 2022

I had the same issue with zig 0.9.1 on Ubuntu, then I found out that you can force symbols to be exported if you use --export.

Example:

zig build-lib your_lib.zig -target wasm32-freestanding-musl -dynamic -O ReleaseSmall --export=some_zig_function --export=another_zig_function

@IcyTv
Copy link

IcyTv commented Sep 15, 2022

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment