A WebAssembly Toolchain Story

Alon Zakai / 2016

We begin in ancient times...


2015


Our asm.js toolchain looked like this:


LLVM IR asm.js backend JS optimizer JS

WebAssembly (wasm) is coming, we need ways to compile to it


Two parallel efforts are begun:



1. LLVM WebAssembly backend: from scratch, not limited by asm.js (lacks i64s, etc.); long-term effort (still in progress today)

2. asm2wasm: asm.js → wasm; a quick hack to get something working


Indeed, we quickly got asm2wasm up and running:


LLVM IR asm.js backend JS optimizer asm2wasm wasm

(only thing that changed is in bold)

asm2wasm is part of Binaryen, which has a WebAssembly optimizer


Can run standalone, also asm2wasm can run it in parallel:


LLVM IR asm.js backend JS optimizer asm2wasm & wasm optimizer wasm

2016


WebAssembly optimizer improves, removes need for JS one:


LLVM IR asm.js backend JS optimizer asm2wasm
& wasm optimizer
wasm

Which brings us to this:


LLVM IR asm.js backend asm2wasm
& wasm optimizer
wasm

Note: we don't need to emit proper JS or asm.js anymore...

wasm-only mode: Adds special intrinsics to the asm.js backend's output, for i64s and other things asm.js couldn't handle well



  function truncate(x) {
    x = i64(x); // declare param as i64
    return i64_trunc(x) | 0; // truncate to low 32 bits
  }


asm2wasm no longer limited by asm.js, just like WebAssembly backend

current state of asm2wasm toolchain:


LLVM IR "asm.js" backend asm2wasm
& wasm optimizer
wasm

Box2D code size measurements

phasegzipped size (K)

asm.js55.3
+ asm2wasm50.0(a)
+ wasm opts45.2
– js opts44.1
wasm-only43.1(b)

(lower is better)

(a) going to wasm is a 10% improvement

(b) the rest add a further 14%

Box2D speed measurements

phasetime (seconds)

asm.js5.97
+ asm2wasm5.66(a)
+ wasm opts5.28
– js opts5.24(b)
wasm-only5.24

(lower is better)

(a) going to wasm is a 5% improvement

(b) the rest add a further 7%


Hmm, wasm-only doesn't help much here...

An i64-specific benchmark, corrections-i64:

phasetime (seconds)

asm.js28.46
+ asm2wasm13.21(a)
+ wasm opts12.99
– js opts12.88
wasm-only6.88(b)

(lower is better)

(a) asm2wasm uses i64s in runtime, ~2X faster

(b) wasm-only uses the full power of WebAssembly, i64s everywhere, for a further ~2X

Which brings us to The End, and the moral of our story:


Sometimes a hack like asm2wasm can become a stepping stone to something nice