Hi everyone! My internship is over, so I just wanted to leave an update on the state of WebAssembly support in Rust and what Iâve been up to for the past few weeks.
Last time I left an update, I was ready to have the wasm32-experimental-emscripten emit native WebAssembly object files, but there were a lot of things that needed to happen first. LLDâs WebAssembly support needed to stabilize and Emscripten and Rust needed to upgrade to a recent version of LLVM. Although I had a prototype working locally, I couldnât ask for upgrades to be merged in until LLD had stabilized. I was stuck, so I pivoted to working on something else. Specifically, Iâve been working on the wasm2asm tool in Binaryen. This tool translates WebAssembly to asm.js and will allow Emscripten to target asm.js using the LLVM WebAssembly backend instead of Fastcomp. This would break Rustâs dependency on Fastcomp, making it much easier to upgrade Rustâs LLVM in the future while still allowing rustc to target asm.js. The work is unfortunately not finished, but itâs much farther along than it was, and Iâd like to keep working on it if I have time.
Meanwhile, progress has been made on other fronts. Rust updated to LLVM 5 while I was working on wasm2asm and WebAssembly support in LLD is being actively discussed and reviewed. Some of the follks working on GHC, the Haskell compiler, have also been able to extract some of the JS runtime out of Emscripten. Overall things are moving slowly but surely where we want them to go.
With that, I thought I would leave instructions for using the wasm32-experimental-emscripten target in case anyone wants to play around with it or hack on it. The instructions are a bit complicated and you will need to build Rust from source because it depends on functionality that is not built into LLVM by default and because Emscripten is highly sensitive to changes in your environment. These instructions are only known to work on Linux.
Build Rust
-
Clone the Rust repo and check out master
-
Write the following config.toml in rust top level directory. The important parts are the wasm32-experimental-emscripten build target and the experimental-targets = âWebAssemblyâ LLVM option. You should probably set codegen-units equal to however many cores you have on your machine.
[build]
target = ["x86_64-unknown-linux-gnu", "wasm32-experimental-emscripten"]
[rust]
codegen-units = 48
debug-assertions = true
[llvm]
assertions = true
experimental-targets = "WebAssembly"
optimize = false
ccache = true
- Compile rust (as quickly as possible). Or do a full build if you donât mind waiting.
cd rust
./x.py build --stage 1 src/libtest
Set up environment
- Download last known good build of wasm binaries by using or following this script:
BUILDNO=$(curl -fL https://storage.googleapis.com/wasm-llvm/builds/linux/lkgr.json | jq '.build | tonumber')
curl -sL https://storage.googleapis.com/wasm-llvm/builds/linux/$BUILDNO/wasm-binaries.tbz2 | tar xvkj
This will ensure you have Emscripten and all the LLVM tools it needs to target wasm using the native LLVM backend by downloading them directly from the WebAssembly testing infrastructure.
-
Install a wasm-ready nodejs like node 8
-
Update PATH
export PATH=/path/to/node-v8.1.2-linux-x64/bin:/path/to/wasm-install/emscripten:/path/to/wasm-install/bin:$PATH
It is important that you have wasm-install/emscripten
before wasm-install/bin
in your path, otherwise Emscripten will not pick up the correct configuration file.
- Write the emscripten configuration file ~/.emscripten. You must use full paths. Note: only the wasm32-experimental-emscripten target will work properly with this emscripten config, not wasm32-unknown-emscripten or asmjs-unknown-emscripten. This file points Emscripten to all of the other tools it depends on.
EMSCRIPTEN_ROOT = '/path/to/wasm-install/emscripten'
NODE_JS='/path/to/node-v8.1.2-linux-x64/bin/node'
LLVM_ROOT='/path/to/wasm-install/bin'
BINARYEN_ROOT='/path/to/wasm-install'
COMPILER_ENGINE= NODE_JS
JS_ENGINES = [NODE_JS]
- Populate the emscripten caches
echo 'main(){}' > a.c
emcc a.c -s WASM=1
Compile and Run
- Compile a Rust program, hello.rs, to wasm
rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc --target wasm32-experimental-emscripten hello.rs
- Finally, run it with node
node hello.js
It will fail due to LLVM bug #33824, which affects LLVM bitcode produced by rustc but not clang and which I never had time to fix. This bug will will become a non-issue once native WebAssembly exception handling is implemented.