@callahad

WebAssembly

(Do we all have to learn C now?)

Dan Callahan / @callahad

Slides derived from a deck by
Luke Wagner / @luke_wagner

(Mozillian, Co-chair WebAssembly W3C Community Group)

Outline

  • What is WebAssembly?
  • Tour of WebAssembly
  • Little tour of WebAssembly
  • WebAssembly APIs
  • Beyond compiling C/C++
  • Frequently Asked Questions

What is WebAssembly?

A new standard being developed in a W3C Community Group with Apple, Google, Microsoft and Mozilla 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 developer tools when viewing source

WebAssembly is Many Things

Depending on where you're coming from:

  1. Compiler Target for the Web
  2. Virtual ISA
  3. JavaScript Feature
  4. Evolution of Asm.js

1. Compiler Target for the Web

Not a programming language

Compile from programming languages

2. Virtual ISA

As close to physical machine instructions as safety/portability allow

WebAssembly x86 ARM
i32.add addl ADD
call call BL
i32.load check + mov check + LDR

3. JavaScript Feature

Efficient loading / caching of large programs

+

Predictable, near-native performance

=

Powerful library-building tool

Why would you want that?

Websites are already using compiled libraries for:

  • Client-Side Image Compression / Filters
  • Facial Recognition
  • Cryptography
  • Audio Mixing
  • Media Decoders
  • Games
  • Simulations

4. Evolution of Asm.js

(tl;dr - asm.js is an extraordinarily optimizable, low-level subset of JavaScript that can be compiled from languages like C/C++ and shipped on the Web today)

Motivations for Asm.js

  • Avoid Plugins (Deprecation, Security, Portability)
  • Port large, existing applications
  • Port high-performance C/C++ libraries
  • Provide predictable performance

Short History of Asm.js

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

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

asm.js optimizations shipping in Firefox and Edge
under development in Chrome and Safari

4. Evolution of Asm.js (II)

Limitations of Asm.js

  • Large File Sizes
  • Long Parse / Load Times
  • Potential for Lower Runtime Memory Use
  • Potential for Even Greater Performance

4. Evolution of Asm.js (III)

WebAssembly is effectively a binary encoding of asm.js,
with tweaks to make it a better compiler target

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

A Tour of WebAssembly

WebAssembly is a Work In Progress


Successful interop between Firefox, Chrome, and Edge!

Specifying and shipping iteratively, like JavaScript.
Goal for first release: Compile C/C++, and be a better asm.js.

We can polyfill wasm with asm!

The whole text format is under construction

Examples use a temporary S-Expression syntax:

Other things that are speculative
or not yet implemented are marked ☃

Start with some C code:


// demo.c
DLL_EXPORT
int add(int lhs, int rhs) {
    return lhs + rhs;
}
                
where functions we want to call from JS are exported:

// However DLL/DSO exports are defined in your compiler
#define DLL_EXPORT __attribute__ ((visibility ("default")))
                

then compile to wasm:


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

Render the binary as text:


☃ wasm2text demo.wasm | less
                

(module
    (func $add (param $lhs i32) (param $rhs i32) (result i32)
        (i32.add (get_local $lhs) (get_local $rhs))
    )
    (export "add" $add)
)
                

Today, we load the wasm via JS API:


fetch('demo.wasm').then(response =>
    response.arrayBuffer()
).then(buffer => {
    let codeBytes = new Uint8Array(buffer);
    let instance = Wasm.instantiateModule(codeBytes);
    alert("1 + 2 = " + instance.exports.add(1, 2));
});
                

Demo!

Experimental support requires Firefox Nightly and setting javascript.options.wasm in about:config

  1. Open the Debugger
  2. Click the wasm source

☃ In the future, streaming, async compilation via Streams


fetch('demo.wasm').then(response =>
    Wasm.compile(response.body.getReader())
).then(instance => {
    alert("1 + 2 = " + instance.exports.add(1, 2));
});
                

☃ In the future, with ES6 Module integration:



                

WebAssembly can call JavaScript too!


// main.c
extern DLL_IMPORT void printInt(int);
int main() {
    printInt(42);
}
                
where JS functions we want to call from wasm are imported:

// However DLL/DSO imports are defined in your compiler
#define DLL_IMPORT __attribute__ ((visibility ("default")))
                

then compile to wasm:


☃ clang -mwasm main.c -o main.wasm
                

Rendering the binary as text:


☃ wasm2text main.wasm | less
                

(module
    (import "imports" "printInt" (param i32))
    (func $main (call_import 0 (i32.const 42)))
    (start $main)
)
                

Now write the ES6 module which is called by wasm:


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

Imports can be functions passed to the JS API:


var codeBytes = ...;
var imports = {printInt:i => console.log(i)};
Wasm.instantiateModule(codeBytes, {imports});
                

☃ Or, load the wasm from a <script type='module'> tag:



                

Now let's look at an actual computation:


// accum.c
DLL_EXPORT
int accum(int* i, int* end) {
    int sum = 0;
    for (; i != end; i++)
        sum += *i;
    return sum;
}
                

This compiles to the function:


(func $accum (param $i i32) (param $end i32) (result i32)
    (local $sum i32)
    (loop $break $top
        (br_if $break (i32.eq (get_local $i) (get_local $end)))
        (set_local $sum (i32.add (get_local $sum)
                                 (i32.load (get_local $i))))
        (set_local $i (i32.add (get_local $i) (i32.const 4)))
        (br $top)
    )
    (get_local $sum)
) \o/
                

The containg module declares and exports memory


(module
    (memory 1)  ;; declare one page of memory = 64KiB
    (export "memory" memory)
    (func $accum ...)
    (export "accum" $accum)
)
                

Which we can then access from JS:


var codeBytes = ...;
var instance = Wasm.instantiateModule(codeBytes);

// Create an array of integers 1..10
var i32 = new Int32Array(instance.exports.memory);
for (var i = 0; i < 10; i++)
    i32[i] = i + 1;

console.log(instance.exports.accum(0, 10 * 4));  // "55"
                

End of the Tour

What we've seen so far:

  • Calling to and from JavaScript
  • ☃ Streams and ES6 module integration
  • Computation

But what about APIs?

On a traditional virtual platform:

 

On a modern Web browser:

The Web is starting to resemble a traditional virtual platform

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

... open standards, multiple implementations, etc

Q: So what are WebAssembly APIs? A: Web APIs!

This is a key difference from plugin architectures

Web API integration

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

☃ In the future, support calling Web APIs directly

Web API integration

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

For example, using libc and SDL:


#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
                

Web API integration

Which produces a default HTML harness:

Beyond just C/C++

Currently, WebAssembly only has linear memory

Great if your language has a low-level memory model

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

Beyond just C/C++

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

Beyond just C/C++

Also need some fundamental extensions
to the (shared JS + WebAssembly) GC:

  • Typed Objects
  • Postmortem Notification
  • Weak References

Beyond just C/C++

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

  • Patching: immediates, branch targets, return address, ...
  • Fine-grained (stub) compilation
  • Good dynamic/any value support

FAQ

How do I compile to wasm now?

Use the asm.js toolchain

C/C++ → Emscripten (LLVM) → Binaryen

Add a WebAssembly backend

When Can I Use WebAssembly?

Still iterating, and we'll need time for
review and feedback once the spec stabilizes

The first browsers are likely to ship this year

How will WebAssembly be Used?

Hard to predict, but we can extrapolate from asm.js today:

  • WebAssembly driving one-big-canvas ports
    • Games / VR / Completely custom UIs
    • Bespoke canvas reimplementations of HTML/CSS
  • WebAssembly driving a big canvas with JS chrome
  • Whole WebAssembly engine + JS frontend
  • Explicitly through individual WebAssembly libraries
  • Implicitly through JS frameworks
    • E.g., early experiments in Ember's Glimmer 2 VM

What about source maps?

Critical for compiled languages to feel "first class"

...Experiments are ongoing

What if I compile my JS to Wasm?

Not useful. You'd have to reimplement a JS engine in wasm.

Your browser already has a really good one of those.

Will WebAssembly Replace JS?

No. Why?

  • JS has huge and growing momentum, vibrant ecosystem
  • JS remains the privileged, built-in language of the Web
  • Many Web APIs designed specifically for JS


In fact, WebAssembly may have quite the opposite effect:

If you're targeting WebAssembly and your app needs a scripting language, JS is the natural choice...

Thanks! Questions?

More info at webassembly.github.io

Dan Callahan / @callahad


github.com/callahad/mjs16-wasm