A WebAssembly Compiler tale

How we abstracted our API to be independent of the IR, allowing Wasmer to support multiple compiler backends.

Syrus Akbary
Wasmer

--

Originally, the Wasmer runtime was designed around Cranelift, a compiler framework written in Rust.

Wasmer old API

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.

Wasmer new Runtime API, with support for multiple backends

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.

Single-pass backend

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

Cranelift backend

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

LLVM 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

Hope you enjoyed this article!

You can visit our website: https://wasmer.io
Our Github: https://github.com/wasmerio/wasmer
Or our Twitter: https://twitter.com/wasmerio

--

--