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:
So what is WebAssembly really?
It's a couple things, depending on what you're doing:
Not a programming language
Compile from programming languages
Ability to efficiently load large code +
predictable near-native performance =
Powerful library-building tool
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 |
Can think of WebAssembly as a binary encoding of asm.js with tweaks to make it a better compiler target
asm.js | WebAssembly | |
---|---|---|
(x+y)|0 |
↦ | i32.add |
f()|0 |
↦ | call |
HEAP32[i>>2]|0 |
↝ | i32.load |
Why do we need this new target / feature / CPU / 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 |
asm.js optimizations shipping or under development in Edge, Chrome and Safari Adobe, AutoDesk, Epic, Facebook, Mega, Unity, and more shipping with Emscripten/asm.js |
asm.js use today demonstrates real demand and excitement
asm.js use cases:
Because we need to go farther than we can with asm.js.
Based on feedback from developers, we need to:
(Also shared memory, but that's coming in JS too.)
WebAssembly is a Work In Progress
Recently reached Firefox/Chrome/Edge interop milestone!
More iteration before stable spec and ship in browsers
Want to ship this year (with usual standardization caveats)
WebAssembly is being specified and shipped iteratively.
The initial release focuses on a Minimum Viable Product: compiling C/C++, better asm.js.
(This means we can use asm.js to write polyfills!)
But there is a lot more we want to do next!
The whole text format is under construction, so I'll use the current temporary S-Expression syntax in this presentation:
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);
☃ and load the wasm
from a <script type='module'>
tag:
Imports can also just be functions passed to the JS API:
var codeBytes = ...;
var imports = {printInt:i => console.log(i)};
Wasm.instantiateModule(codeBytes, {imports});
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:
Also need a good source-maps story
Hard to predict, but we can extrapolate from asm.js today:
No. Why?
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...
find out more at webassembly.github.io