WebAssembly

Birth of a Virtual ISA
Sep 8, 2016

Ben Smith @binjimint

WebAssembly

We Be Assemblin'
Sep 8, 2016

Ben Smith @binjimint

WebAssembly

The Right To Peaceably Assemble
Sep 8, 2016

Ben Smith @binjimint

WebAssembly

Simply 'ssembling
Sep 8, 2016

Ben Smith @binjimint

WebAssembly

Make WebAssembly Great Again
Sep 8, 2016

Ben Smith @binjimint

WebAssembly

On Mars
Sep 8, 2016

Ben Smith @binjimint

Some Stuff I've Worked On

Games
  • DS: Incredibles 2, Cars, Ratatouille
  • Wii: Boom Blox, Boom Blox Bash Party
  • PC: Overwatch (before it was Overwatch)
@ Google
  • Chrome
  • Native Client
  • WebAssembly!

What is WebAssembly?

  • New text and binary format spec
  • Designed to be fast to load and execute
  • Compilation target for languages like C, C++, Rust, etc.
  • Safe
  • Portable
  • Designed by W3C Community Group that includes representatives from all major browsers

"shortcut to your JavaScript engine's optimizer"

Aside - JavaScript Optimization

  • Tiered; hotter code is optimized more
  • Gather type information while executing
  • Make assumptions based on type information
  • Failed assumption => "Deoptimization"

What is WebAssembly not?

  • Replacement for JavaScript
  • Compilation target for languages that compile to JavaScript
  • Programming language

Why WebAssembly?

  • Image/video editing
  • Image recognition
  • Live video augmentation
  • VR and low-latency
  • CAD applications
  • Scientific visualization and simulation
  • Platform simulation (DOSBox, QEMU, MAME, ...)
  • Games
  • Language interpreters and VMs
  • POSIX user-space environment
  • Developer tools (editors, compilers, debuggers, ...)
  • Remote desktop
  • VPN
  • Compression
  • Encryption

and more! (see here)

Demo!

Let's start with a simple function...

In C


int fib(int n) {
  int a = 0;
  int b = 1;
  while (n > 0) {
    int t = b;
    b = a + b;
    a = t;
    n--;
  }
  return b;
}
          

Let's start with a simple function...

In C++


int fib(int n) {
  int a = 0;
  int b = 1;
  while (n > 0) {
    int t = b;
    b = a + b;
    a = t;
    n--;
  }
  return b;
}
          

Let's start with a simple function...

In C++14


auto fib(int n) {
  auto a = 0;
  auto b = 1;
  while (n > 0) {
    auto t = b;
    b = a + b;
    a = t;
    n--;
  }
  return b;
}
          

Let's compile this...

$ emcc -s BINARYEN=1 -s 'EXPORTED_FUNCTIONS=["_fib"]' fib.c -o fib.js

Let's compile this...

$ emcc -s BINARYEN=1 -s 'EXPORTED_FUNCTIONS=["_fib"]' fib.c -o fib.js

Let's compile this...

$ emcc -s BINARYEN=1 -s 'EXPORTED_FUNCTIONS=["_fib"]' fib.c -o fib.js

OK, what's happening here?

  1. Compile C code using Clang via Emscripten
  2. Emscripten generates asm.js

(a little) History - asm.js

  • Low-level, optimizable subset of JavaScript
  • Linear memory accessed via TypedArrays
  • Import and export functions
  • Functions have type annotations on expressions, parameters and return values

Let's take a look at the asm.js


function module(global, env, buffer) {
  "use asm";
  ...
  function fib(n) {
    n = n|0;
    var a = 0, b = 1, t = 0;
    while ((n|0) >= 0) {
      t = b;
      b = a + b|0;
      a = t;
      n = n - 1|0;
    }
    return b|0;
  }
  return {fib: fib};
}
          

Let's take a look at the asm.js


function module(global, env, buffer) {
  "use asm";          // <- notify engine to compile as asm.js
  ...
  function fib(n) {
    n = n|0;          // <- parameter type annotation
    var a = 0, b = 1, t = 0;
    while ((n|0) >= 0) {
      t = b;
      b = a + b|0;    // <- expression annotation
      a = t;
      n = n - 1|0;
    }
    return b|0;       // <- return value type annotation
  }
  return {fib: fib};  // <- export function
}
          

Aside - Why not just use asm.js?

Pros:
  • asm.js-aware engines can produce good code
  • "It's just JavaScript" = polyfill for free
Cons:
  • Parsing can be expensive
  • Validation failure = fallback to JavaScript
  • Hard to extend functionality
  • No 64-bit integers!

What's happening here?

  1. Compile C using Clang via Emscripten
  2. Emscripten generates asm.js
  3. asm2wasm generates WebAssembly text format

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)          ;; $a = 0; $b = 0
    (set_local $b (i32.const 1))           ;; $b = 1
    (loop $exit $next
      (br_if $exit
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))
      (set_local $n
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))
    (return (get_local $b))))
          

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)
    (set_local $b (i32.const 1))
    (loop $exit $next                      ;; do {
      (br_if $exit
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))
      (set_local $n
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))                          ;; continue; } while(0)
    (return (get_local $b))))
          

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)
    (set_local $b (i32.const 1))
    (loop $exit $next
      (br_if $exit                         ;; if ($n <= 0) break
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))
      (set_local $n
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))
    (return (get_local $b))))
          

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)
    (set_local $b (i32.const 1))
    (loop $exit $next
      (br_if $exit
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b                        ;; $b = $b + $a
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))  ;; $a = $b
      (set_local $n
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))
    (return (get_local $b))))
          

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)
    (set_local $b (i32.const 1))
    (loop $exit $next
      (br_if $exit
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))
      (set_local $n                        ;; $n = $n - 1
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))
    (return (get_local $b))))
          

Let's take a look at the Wasm text


(module
  (func "fib" (param $n i32) (result i32)
    (local $a i32) (local $b i32)
    (set_local $b (i32.const 1))
    (loop $exit $next
      (br_if $exit
        (i32.le_s (get_local $n) (i32.const 0)))
      (set_local $b
        (i32.add
          (get_local $a)
          (tee_local $a (get_local $b))))
      (set_local $n
        (i32.sub
          (get_local $n)
          (i32.const 1)))
      (br $next))
    (return (get_local $b))))              ;; return $b
          

Stack machine


i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=3$a=0$b=0
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=3$a=0$b=0
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
3 $n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
30 $n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
01 $n=3$a=0$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
01 $n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
3 $n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
31 $n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
2 $n=3$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
2 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
20 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
11 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
11 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
2 $n=2$a=1$b=1
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=2$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
2 $n=2$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
21 $n=2$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=2$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
10 $n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
12 $n=1$a=1$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
12 $n=1$a=2$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
3 $n=1$a=2$b=2
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=1$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=1$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
11 $n=1$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=1$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
0 $n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
00 $n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
1 $n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
$n=0$a=2$b=3
codestacklocals

i32.const 1
set_local $b
block $done
  loop $top
    get_local $n
    i32.const 0
    i32.le_s
    br_if $done
    get_local $a
    get_local $b
    tee_local $a
    i32.add
    set_local $b
    get_local $n
    i32.const 1
    i32.sub
    set_local $n
    br $top
  end
end
get_local $b
return
          
3 $n=0$a=2$b=3

What's happening here?

  1. Compile C using Clang via Emscripten
  2. Emscripten generates asm.js
  3. asm2wasm generates WebAssembly text format
  4. wasm-as generates WebAssembly binary format

Let's take a look at the Wasm binary


00000000: 0061 736d 0c00 0000 0474 7970 6506 0140  .asm.....type..@
00000010: 0101 0101 0866 756e 6374 696f 6e02 0100  .....function...
00000020: 0665 7870 6f72 7406 0100 0366 6962 0463  .export....fib.c
00000030: 6f64 652b 0129 0102 0110 0115 0201 0214  ode+.)..........
00000040: 0010 0050 0700 0114 0114 0219 0140 1502  ...P.........@..
00000050: 1400 1001 4115 0006 0000 0f0f 1402 09    ....A..........
          

header

(version 0xc)

Let's take a look at the Wasm binary


00000000: 0061 736d 0c00 0000 0474 7970 6506 0140  .asm.....type..@
00000010: 0101 0101 0866 756e 6374 696f 6e02 0100  .....function...
00000020: 0665 7870 6f72 7406 0100 0366 6962 0463  .export....fib.c
00000030: 6f64 652b 0129 0102 0110 0115 0201 0214  ode+.)..........
00000040: 0010 0050 0700 0114 0114 0219 0140 1502  ...P.........@..
00000050: 1400 1001 4115 0006 0000 0f0f 1402 09    ....A..........
          

type section

(1 type; i32 -> i32)

Let's take a look at the Wasm binary


00000000: 0061 736d 0c00 0000 0474 7970 6506 0140  .asm.....type..@
00000010: 0101 0101 0866 756e 6374 696f 6e02 0100  .....function...
00000020: 0665 7870 6f72 7406 0100 0366 6962 0463  .export....fib.c
00000030: 6f64 652b 0129 0102 0110 0115 0201 0214  ode+.)..........
00000040: 0010 0050 0700 0114 0114 0219 0140 1502  ...P.........@..
00000050: 1400 1001 4115 0006 0000 0f0f 1402 09    ....A..........
          

function section

(1 function; using function type 0)

Let's take a look at the Wasm binary


00000000: 0061 736d 0c00 0000 0474 7970 6506 0140  .asm.....type..@
00000010: 0101 0101 0866 756e 6374 696f 6e02 0100  .....function...
00000020: 0665 7870 6f72 7406 0100 0366 6962 0463  .export....fib.c
00000030: 6f64 652b 0129 0102 0110 0115 0201 0214  ode+.)..........
00000040: 0010 0050 0700 0114 0114 0219 0140 1502  ...P.........@..
00000050: 1400 1001 4115 0006 0000 0f0f 1402 09    ....A..........
          

export section

(1 export; function 0, named "fib")

Let's take a look at the Wasm binary


00000000: 0061 736d 0c00 0000 0474 7970 6506 0140  .asm.....type..@
00000010: 0101 0101 0866 756e 6374 696f 6e02 0100  .....function...
00000020: 0665 7870 6f72 7406 0100 0366 6962 0463  .export....fib.c
00000030: 6f64 652b 0129 0102 0110 0115 0201 0214  ode+.)..........
00000040: 0010 0050 0700 0114 0114 0219 0140 1502  ...P.........@..
00000050: 1400 1001 4115 0006 0000 0f0f 1402 09    ....A..........
          

code section

(... really?)

oh, why not!


0000036: 01           ; local decl count
0000037: 02 01        ; 2 locals of type i32
0000039: 10 01        ; i32.const 1
000003b: 15 02        ; set_local 2
000003d: 01           ; block
000003e: 02           ;   loop
000003f: 14 00        ;     get_local 0
0000041: 10 00        ;     i32.const 0
0000043: 50           ;     i32.le_s
0000044: 07 00 01     ;     br_if (arity = 0, depth = 1)
0000047: 14 01        ;     get_local 1
0000049: 14 02        ;     get_local 2
000004b: 19 01        ;     tee_local 1
000004d: 40           ;     i32.add
000004e: 15 02        ;     set_local 2
0000050: 14 00        ;     get_local 0
0000052: 10 01        ;     i32.const 1
0000054: 41           ;     i32.sub
0000055: 15 00        ;     set_local 0
0000057: 06 00 00     ;     br (arity = 0, depth = 0)
000005a: 0f           ;   end
000005b: 0f           ; end
000005c: 14 02        ; get_local 2
000005e: 09           ; return
          

What's happening here?

  1. Compile C using Clang via Emscripten
  2. Emscripten generates asm.js
  3. asm2wasm generates WebAssembly text format
  4. wasm-as generates WebAssembly binary format
  5. Load the binary file via JavaScript API

JS API


var buf = new ArrayBuffer(...);
WebAssembly.compile(buf).then(
    module => {
      var instance = new WebAssembly.Instance(module, {});
      instance.exports.fib(10);
    });
          

JS API


var buf = new ArrayBuffer(...);
WebAssembly.compile(buf).then(
    module => {
      var instance = new WebAssembly.Instance(module, {});
      instance.exports.fib(10);
    });
          

My claim to fame!

What's happening here?

  1. Compile C using Clang via Emscripten
  2. Emscripten generates asm.js
  3. asm2wasm generates WebAssembly text format
  4. wasm-as generates WebAssembly binary format
  5. Load the binary file via JavaScript API
  6. Translate the JavaScript to machine code

TurboFan graph

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx          ; a = 0
15  movl rbx,0x1          ; b = 1
20  nop
31  nop
32  cmpl rax,0x0
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0          ; if (n <= 0) break
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0
35  jle 58
41  leal rdx,[rdx+rbx*1]  ; a = b + a
44  subl rax,0x1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1          ; n = n - 1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1
47  REX.W movq r10,rdx    ; t = a
50  REX.W movq rdx,rbx    ; a = b
53  REX.W movq rbx,r10    ; b = t
56  jmp 32
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0          ; here:
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32                ; goto here
58  REX.W movq rax,rbx
61  retl
          

Generated x86-64 assembly


0   REX.W cmpq rsp,[r13+0xb90]
7   jna 62
13  xorl rdx,rdx
15  movl rbx,0x1
20  nop
31  nop
32  cmpl rax,0x0
35  jle 58
41  leal rdx,[rdx+rbx*1]
44  subl rax,0x1
47  REX.W movq r10,rdx
50  REX.W movq rdx,rbx
53  REX.W movq rbx,r10
56  jmp 32
58  REX.W movq rax,rbx    ; return b
61  retl
          

A lot more to mention...

  • Linear memory
  • Other control flow operators (br_table, if/else)
  • Function calls - direct and indirect
  • Importing functions
  • Importing/exporting memory and function tables
  • 32- and 64-bit floating-point operations
  • "Start" function
  • Global variables

Want to know more?

Want to help?

github.com/WebAssembly

Thanks!