A WebAssembly Compiler tale
How we abstracted our API to be independent of the IR, allowing Wasmer to support multiple compiler backends.
Originally, the Wasmer runtime was designed around Cranelift, a compiler framework written in Rust.
Over time, we realized that different user-cases needed different compiler characteristics, so we’ve expanded our backend repertoire.
We now support selecting multiple compiler backends while exposing the same, familiar, simple API to the user.
Why would you want multiple compiler backends? Each backend offers a different tradeoff between compilation speed and runtime performance.
Today, we are super happy to announce that Wasmer 0.3.0 just shipped two more compiler backends 🎉:
- Single-pass: super fast compilation, non-optimized execution
wasmer run --backend=singlepass myfile.wasm
- Cranelift: normal compilation, normal execution (default)
wasmer run --backend=cranelift myfile.wasm
- LLVM: slow compilation, fast execution
wasmer run --backend=llvm myfile.wasm
Let’s review the tradeoffs of each of this compiler backend:
PS: for now, only the Cranelift backend ships with caching support, LLVM will support caching soon
PPS: At the moment, all of our backends only support x86_64, but we will support others, namely AArch64 (ARM) and possibly RISCV soon.
The single-pass compilation backend emits machine code as the WebAssembly bytecode is being parsed.
Because there is no post-processing or analysis involved in generating the machine code, the compilation times are very fast. 🔥
Single-pass compilation can almost as fast as just validating a WebAssembly module
However, very few optimizations are performed, runtime performance is generally slower than either of the two other backends (Cranelift or LLVM).
To generate the machine code, we are using a macroassembler, currently the dynasm library, allowing us to emit machine code efficiently at runtime.
When to use Single-pass
- Devices that need to run WebAssembly on the fly but can’t afford to run a “heavy” compiler: IoT devices
- When we need to be cautious about JIT bombs (wasm modules that take a lot longer to compile than they would to execute): blockchain smart-contracts
- Programs that need to start-up quickly, but don’t necessarily need extreme runtime performance: CLI tools, or development environments
The Cranelift backend uses Cranelift to generate machine code.
Cranelift is a pure-Rust compiler infrastructure, similar to LLVM.
It’s compromise between compilation speed and runtime performance, situated roughly in the middle between our two other backends.
When to use Cranelift
- You need faster compilation times than LLVM
- You need better runtime performance than the single-pass backend
The LLVM backend uses the LLVM compiler framework to generate highly-optimized machine code from the inputted WebAssembly bytecode.
Compilation times with the LLVM backend are generally quite slow, but the runtime performance is roughly equal to native code.
When to use LLVM
- You need optimized machine-code that need to run at same speed as native: Edge-Computing