WebAssembly

lukewagner.github.io/wasm-talk-2017

Luke Wagner / @luke_wagner

Questions welcome!

This is pretty low-level stuff; if you have a question, chances are everyone else does too!

About me

Mozilla Firefox/SpiderMonkey hacker for 8 years


2008 - 2011

2010 - 2013

2012 -

2013 -

2016 -

Member of WebAssembly W3C Community Group

caniuse?

caniuse?

Outline

  • What is WebAssembly?
  • Short Tour of WebAssembly
  • Accessing Web APIs from WebAssembly
  • Beyond compiling C/C++
  • Frequently Asked Questions

What is WebAssembly?

An emerging standard which defines:

  1. a compact, portable binary format which is fast to load and runs safely at predictably near-native speed
  2. a 1:1 text format rendered by tools when viewing source

Multiple valid ways to look at WebAssembly:

  1. virtual CPU
  2. evolution of asm.js
  3. new JavaScript power

Depends on where you are coming from

1. Virtual CPU

Not a programming language

Compile from programming languages

As close to real CPUs as safety/portability allow

Safety matters!

WebAssembly doesn't change the security model of the Web

Pointers are checked indices, not raw addresses

Memory access is like JS array access: out-of-bounds throws

Portability matters!

Changing browser/version/device shouldn't break things

Just like upgrading your x86 CPU shouldn't break things

So WebAssembly specifies everything*

WebAssembly x86/x64 ARM/ARM64
i32.add addl ADD
call call BL
i32.load check + mov check + LDR
infinite stack/locals 8/16 registers 16/32 registers

2. Evolution of asm.js

asm.js is an extraordinarily optimizable, low-level subset of JavaScript that can be compiled from languages like C/C++.

Because it's just JavaScript, runs well on all browsers today.

Special optimizations added to Firefox, Edge and Chrome.

Safari includes asm.js in their JetStream benchmark.

How do you get asm.js?

C/C++ ⇒ clang/LLVM ⇒ Emscripten ⇒ asm.js

asm.js timeline

2009 - 2012 Mozilla Research experiments:
Emscripten: C/C++ to JS compiler/toolchain
asm.js: optimize Emscripten-style output
2013 - 2014 Published asm.js subset, shipped optimizations in Firefox, demonstrated on large game engines
2015 - 2016

Other browsers add optimizations

Adobe, AutoDesk, Epic, Facebook, Mega, Unity, and more shipping with Emscripten/asm.js

Cross-browser work begins on WebAssembly

Why are developers using asm.js?

  • avoid plugins (deprecation, friction, security)
  • bring existing applications to the Web (too big to rewrite)
  • port high-performance C/C++ libraries for use by JS
  • predictable near-native performance (compared to JS)

Why wasn't asm.js enough?

  1. parse time (especially on mobile)
  2. over-the-wire size (with and without compression)
  3. new features hard in JS: threads, dynamic linking, ...
  4. reduce browser variance

WebAssembly as binary encoding of asm.js (with tweaks)

asm.js WebAssembly
(x+y)|0 i32.add
i64.add
f()|0 call
HEAP32[i>>2]|0 i32.load

raw: WebAssembly is ~42% smaller

gzipped: WebAssembly is ~23% smaller

Decoding WebAssembly ~10x faster than parsing JS

3. New JavaScript power

Ability to efficiently load large code +

predictable near-native performance =

Powerful JavaScript library-building tool

What sort of libraries? Today, using asm.js:

  • compress before upload (Facebook)
  • video (de)muxing (Twitch)
  • image filters (Adobe Lightroom)
  • codec polyfill (Wikipedia)
  • 3D mapping (BA3)
  • custom audio mixing (Faust)
  • crypto (Mega)
  • physics engine (PlayCanvas)
  • language detection (FFOS)
  • face detection (various)
  • ...

Tomorrow: Web Frameworks!

What?!! But it's all DOM and strings and stuff!

Not anymore: virtual DOM, reconciliation, bytecode VMs, ...

From Tom Dale's 2017 JSConf.eu talk

From Lin Clark's 2017 ReactEurope talk

Outline

  • What is WebAssembly? ✔
  • Short Tour of WebAssembly
  • Accessing Web APIs from WebAssembly
  • Beyond compiling C/C++
  • Frequently Asked Questions

WebAssembly is being specified and shipped iteratively

What's released today is the Minimum Viable Product (MVP)

There is a lot more to do: standard and toolchain

Speculative features are marked with ☃

Today, WebAssembly primarily compiled from C/C++:

C/C++ ⇒ clang/LLVM ⇒ Emscripten ⇒ WebAssembly

Ongoing work on:

  • Upstream LLVM backend
  • Rust-to-WebAssembly

Start with some C code:


// demo.c

MODULE_EXPORT
int add(int lhs, int rhs) {
    return lhs + rhs;
}
                

then compile to wasm:


☃ clang-wasm demo.c -o demo.wasm
                

Render the binary as text:


$ wasm2wat demo.wasm | less
                

(module
    (func (export "add") (param i32 i32) (result i32)
        get_local 0
        get_local 1
        i32.add
    )
)
                

Tour of opcodes out of scope, see draft spec

Compile the .wasm using JS WebAssembly API


fetch('demo.wasm').then(response =>
    response.arrayBuffer()
).then(bytecode =>
    WebAssembly.instantiate(bytecode)
).then(({instance}) =>
    alert("1 + 2 = " + instance.exports.add(1, 2));
);
                

Debug Demo

  1. Currently, works best in Firefox Nightly
  2. Open the Debugger
  3. See wasm:// URL appear
  4. Place breakpoint in wasm and re-click

Standardized and coming soon:


WebAssembly.instantiate(fetch('demo.wasm')).then(({instance}) =>
    alert("1 + 2 = " + instance.exports.add(1, 2));
);
                

Allows browser to do streaming and caching

☃ ES Module integration:



                

WebPack integration starting

Lot more work to do here to integrate with modular workflow!

WebAssembly can call JavaScript too!


// main.c

// Think: import {printInt} from 'logger';
extern MODULE_IMPORT("logger") void printInt(int);

int main() {
    printInt(42);
}
                

JS functions are imported (just as with an ES Module)

☃ With ES Module integration, we could write:


// logger.js
export var printInt = i => console.log(i);
                



                

Today, pass in JS function using the WebAssembly API:


function printInt(i) { console.log(i) }
WebAssembly.instantiate(fetch('main.wasm'), {logger:{printInt}});
                  

What we really want to see is the source language

Source Maps actually map naturally onto WebAssembly

Still need to add mappings for variable names

Demo

More work to do here...

Outline

  • What is WebAssembly? ✔
  • Short Tour of WebAssembly ✔
  • Accessing Web APIs from WebAssembly
  • Beyond compiling C/C++
  • Frequently Asked Questions

What APIs look like on a traditional VM:

 

The Web started out different:

But the Web has changed a lot since then

and is starting to resemble a traditional virtual platform

... with some special "Webby" properties like:

So where does WebAssembly fit in?

Today, WebAssembly only gets to Web APIs by "thunking" through JavaScript

Emscripten maps common C/C++ interfaces to Web APIs:


#include <SDL/SDL.h>
#include <stdio.h>

int main(int argc, char **argv) {
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Surface *s = SDL_SetVideoMode(200, 200, 32, SDL_HWSURFACE);
  SDL_Rect rect = { 50, 50, 100, 100 };
  SDL_FillRect(s, &rect, 0xffff0000);
  printf("Done!\n");
  return 0;
}
                

Compiled by Emscripten with:


emcc -O2 test.c -o test.html
                

Which produces a default HTML harness:

☃ In the future, import and call Web APIs directly from WebAssembly

  • Access DOM directly from C++/Rust
  • Important for Web Frameworks using WebAssembly

Outline

  • What is WebAssembly? ✔
  • Short Tour of WebAssembly ✔
  • Accessing Web APIs from WebAssembly ✔
  • Beyond compiling C/C++
  • Frequently Asked Questions

Currently, WebAssembly only has linear memory

Great if your language has a low-level memory model (C++, Rust, FORTRAN)

But if your language has GC:

  • would need to implement/ship your own GC
  • which misses optimizations in browser GC
  • cannot collect cycles involving browser objects

To provide first-class support for GC languages, WebAssembly needs direct GC access

☃ Goal: add low-level GC primitives to avoid baking in one language's class model

☃ Goal: share GC heap with JS, allow objects/strings/values to flow back and forth

Follow on GitHub GC proposal repo

☃☃ So what could you do with GC support? ☃☃

  • TypeScript*
  • Typed functional programming + VDOM
    • E.g.: Elm, Reason + React
    • GC for functional code, linear memory for VDOM

Need even more features in WebAssembly to run dynamic languages efficiently:

  • Code Patching
  • Fine-grained compilation
  • dynamic/any-type

Precedent with invokedynamic in the JVM

☃☃ But this is all rather far off ☃☃

Outline

  • What is WebAssembly? ✔
  • Short Tour of WebAssembly ✔
  • Accessing Web APIs from WebAssembly ✔
  • Beyond compiling C/C++ ✔
  • Frequently Asked Questions

Will WebAssembly Replace JS?

No. Why?

  • JS remains the privileged high-level language of the Web
  • JS has huge and growing momentum, vibrant ecosystem

In fact, WebAssembly may have quite the opposite effect:

If you're going to target WebAssembly and your app needs a scripting language, JS is a natural choice...

Compile JS to WebAssembly?

Sure, if you want to go slower

  • JS VMs do many low-level optimizations
  • WebAssembly needs a lot more capabilities to compete

...theoretically you could get a startup win by shipping your own bytecode + JS interpreter...

Find out more at webassembly.org

Lin Clark has an awesome blog post and JSConf.eu talk introducing WebAssembly

Slides at lukewagner.github.io/wasm-talk-2017