Slides derived from a deck by
Luke Wagner / @luke_wagner
(Mozillian, Co-chair WebAssembly W3C Community Group)
A new standard being developed in a W3C Community Group with Apple, Google, Microsoft and Mozilla which defines:
Depending on where you're coming from:
Not a programming language
Compile from programming languages
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 |
Efficient loading / caching of large programs
+
Predictable, near-native performance
=
Powerful library-building tool
Websites are already using compiled libraries for:
Motivations for 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
Limitations of Asm.js
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 |
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));
});
Experimental support requires Firefox Nightly and setting javascript.options.wasm
in about:config
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"
What we've seen so far:
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
Today, WebAssembly only gets to Web APIs by
"thunking" through JavaScript
☃ In the future, support calling Web APIs directly
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
Which produces a default HTML harness:
Currently, WebAssembly only has linear memory
Great if your language has a low-level memory model
But if your language has GC:
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
Also need some fundamental extensions
to the (shared JS + WebAssembly) GC:
Need even more features in WebAssembly to
run dynamic languages efficiently:
Use the asm.js toolchain
C/C++ → Emscripten (LLVM) → Binaryen
Add a WebAssembly backend
Still iterating, and we'll need time for
review and feedback once the spec stabilizes
The first browsers are likely to ship this year
Hard to predict, but we can extrapolate from asm.js today:
Critical for compiled languages to feel "first class"
...Experiments are ongoing
Not useful. You'd have to reimplement a JS engine in wasm.
Your browser already has a really good one of those.
No. Why?
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...
More info at webassembly.github.io