asm.jsxEmscripten:
Thefoundationofthenextlevelwebgames
MozillaJapanテクニカルマーケティング
清水智公(nshimizu@mozilla-japan.org/@chikoski)
第63回HTML5とか勉強会withhtml5jゲーム部発足記念合同勉強会
N.Shimizu
• Mozilla Japan (L10N, Game, Devtools, Web Animation)
• html5j Web プラットフォーム部、

Firefox OS、Firefox OS コードリーディング、

html5j ゲーム部 (NEW!)
• @chikoski
• プログラミング言語、分類、ベイジアン、サッカー、圏論(NEW!)
文字列 実行コード
文字列 実行コード抽象構文木トークン列 中間表現
字句解析 構文解析 意味解析 最適化・コード生成
文字列 実行コード抽象構文木トークン列 中間表現
字句解析 構文解析 意味解析 最適化・コード生成
文字列 AST バイトコード
字句解析 / 構文解析 生成
実行
* AST: Abstract Syntax Tree / 抽象構文木
function addOne(a){
return a + 1;
}
loc op
----- --
main:
00000: getarg 0
00003: one
00004: add
00005: return
00006: retrval
aのデータ型 a + 1 のデータ型
number number
undefined number
null number
string string
object string
Just In Time Compile
データ型
Data Type
文字列
字句解析 /
構文解析
実行
バイトコードAST
Baseline
Compiled
Code
MIR
Iron
Compiled
Code
実行と
プロファイル 実行
Bail
生成
Baseline
Compile
Iron Build
Iron
Compile
* AST: Abstract Syntax Tree / 抽象構文木
 MIR: Medium-level Intermediate Representation / 中間表現
* AST: Abstract Syntax Tree / 抽象構文木
 MIR: Medium-level Intermediate Representation / 中間表現
文字列
字句解析 /
構文解析
実行
バイトコードAST
Baseline
Compiled
Code
MIR
Iron
Compiled
Code
実行と
プロファイル 実行
Bail
生成
Baseline
Compile
Iron Build
Iron
Compile
• JITは重い
• hot code(よく実行されるコード)しかコンパイルされない
• JITが始まるまでは、遅いまま
• 型推論に失敗するなどして、JITをやり直すことがある
ダウンロード 実行開始 コンパイル
Ahead-Of-Time Compile
AOT ダウンロード コンパイル 実行開始
JIT ダウンロード 実行開始 コンパイル
Native ダウンロードコンパイル 実行開始
データ型
Data Type
asm.js
asm.js:anextraordinarilyoptimizable,low-levelsubsetofJavaScript
• JavaScript として評価可能
• 数値計算、関数の定義と呼び出し、ArrayBufferの操作のみ可能
• その他のことは不可能
• 型アノテーションと、フォーマルな検証プロセス
• FFIを通じたJavaScript との相互呼び出し
• ArrayBuffer の共有によるデータの受け渡し
function add1(a){
return a + 1;
}
function Peano(stdlib, ffi, heap){
"use asm";
function add1(a){
a = a | 0;
return (a + 1) | 0;
}
return {
suc: add1
}
}
const add1 = Peano(window).suc;
const zero = 0;
const one = add1(zero);
console.log(one); // 1
asm.js で定義したモジュールの利用
function Peano(stdlib, ffi, heap){
"use asm";
// 外部からインポートするシンボルの宣言
// 関数宣言
// 関数表の宣言
// モジュールのエキスポート
}
asm モジュールの構造
#include <stdio.h> // 標準ライブラリのインクルード
#include "log.h"   // その他のライブラリのインクルード
extern void log(int); // 外部からインポートするシンボルの宣言
int add1(int n){ // 関数宣言
log(n);
return n + 1;
}
function Peano(stdlib, ffi, heap){
"use asm";
var abs = stdlib.Math.abs; // 標準ライブラリからのインポート
var z = ffi.zero | 0; // 定数の受け渡し
var log = ffi.log; // JS関数のインポート
外部からインポートするシンボルの宣言
const ffi = {
zero: 0,
log: text => console.log(text)
};
const add1 = Peano(window, ffi).suc;
JS から asm.js へのシンボルの受け渡し
function Peano(stdlib, ffi, heap){
"use asm";
var abs = stdlib.Math.abs; // 標準ライブラリからのインポート
var z = ffi.zero | 0; // 定数の受け渡し
var log = ffi.log; // JS関数のインポート
型アノテーション
変数x のアノテーション 引数 x の型
x = x | 0; int
x = +x; double
x = f(x); float
function addOne(n){
// 引数の型宣言
// 変数宣言
// 関数本体の記述
}
関数宣言の構造
function addOne(n){
n = n | 0; // 引数の型宣言
var one = 1; // 変数宣言
one = (one + n) | 0; // 関数本体
return one | 0;
}
返り値の ret アノテーション 返り値の型
return +ret; double
return ret | 0; int
return 3; double
return f(ret); float
return; void
function addOne(n){
n = n | 0; // 引数の型宣言
var one = 1; // 変数宣言
one = (one + n) | 0; // 関数本体
return one | 0;
}
関数の型: (引数の型) → 返り値の型
function addOne(n){
n = n | 0;
return (n + 1)| 0;
}
function addTwo(n){
n = n | 0;
return (n + 2) | 0;
}
int -> int の関数
var addFunctions = [addOne, addTwo];
関数表宣言(関数ポインタの列)
function Peano(stdlib, ffi, heap){
"use asm";
var abs = stdlib.Math.abs; // 標準ライブラリからのインポート
var z = ffi.zero | 0; // 定数の受け渡し
var log = ffi.log; // JS関数のインポート
標準ライブラリ、外部関数、ヒープ
標準ライブラリ 型
Infinity
NaN
double
Math.acos
Math.asin
Math.atan
Math.cos
Math.sin
Math.tan
Math.exp
(double?) → double
標準ライブラリ 型
Math.ceil
Math.floor
Math.sqrt
(double?) → double ∧
(float?) → float
Math.abs (signed) → signed ∧
(double?) → double ∧
(float?) → float
Math.min
Math.max
(int, int…) → signed ∧
(double, double…) → double
Math.atan2
Math.pow
(double?, double?) → double
標準ライブラリ 型
Math.imul (int, int) → signed
Math.fround fround
Math.E
Math.LN10
Math.LN2
Math.LOG2E
Math.LOG10E
Math.PI
Math.SQRT1_2
Math.SQRT2
double
var log = ffi.log; // 外部関数
log(n | 0);     // これはリンク時にエラー
log((n | 0) >>> 0); // これはリンクできる
外部関数の呼び出しにも配慮が必要
asm.js における型(矢印は継承を表す)
JavaScript の環境と行き来できる型
単項演算子 型
+ (signed) → double ∧

(unsigned) → double ∧
(double?) → double ∧

(float?) → double
- (int) → intish ∧

(double?) → double ∧
(float?) → floatish
~ (intish) → signed
! (int) → int
二項演算子 型
+ (double, double) → double ∧
(float?, float?) → floatish
- (double?, double?) → double ∧
(float?, float?) → floatish
* (double?, double?) → double ∧
(float?, float?) → floatish
/ (signed, signed) → intish ∧
(unsigned, unsigned) → intish ∧
(double?, double?) → double ∧
(float?, float?) → floatish
二項演算子 型
% (signed, signed) → intish ∧
(unsigned, unsigned) → intish ∧
(double?, double?) → double
|, &, ^, <<, >> (intish, intish) → signed
>>> (intish, intish) → unsigned
<, <=, >, >=, ==, != (signed, signed) → int ∧
(unsigned, unsigned) → int ∧
(double, double) → int ∧
(float, float) → int
var memo = new stdlib.Uint32Array(heap);
if(memo[n >> 2] | 0 != 0){
return memo[n >> 2] | 0;
}
if(n >>> 0 > 2){
result = ((fib(n - 1 | 0) | 0) + (fib(n - 2 | 0) | 0)) | 0;
}
return result | 0; heap の利用例
Heap View Type 要素サイズ load type store type
Uint8Array 1 intish intish
Int8Array 1 intish intish
Uint16Array 2 intish intish
Int16Array 2 intish intish
Uint32Array 4 intish intish
Int32Array 4 intish intish
Float32Array 4 float? floatish,
double?
Float64Array 8 double? float?, double?
var memo = new stdlib.Uint32Array(heap);
if(memo[n >> 2] | 0 != 0){
return memo[n >> 2] | 0;
}
if(n >>> 0 > 2){
result = ((fib(n - 1 | 0) | 0) + (fib(n - 2 | 0) | 0)) | 0;
}
return result | 0;log2(要素のバイト数) ぶんだけ右にシフトしなくてはならない
• 例外を投げることなく return まで到達すること
• 全ての属性アクセスは、データアクセスとして解決されること
• heap があるなら、それは ArrayBuffer であること
• heap の大きさは [212
, 224
) 、もしくは 224
の倍数であること
• stdlibから取られたシンボルは、

標準ライブラリ中のものを指すこと
リンクに成功する条件
% emcc -o hello.js hello.c
% node hello.js
hello world
% emcc -o hello.html hello.c
% open hello.html
-o オプションで html / js へ出力
EmscriptencompilesC/C++intoJavaScriptinasm.jsformat
• ファイル入出力: XHR に書き換え / ファイルを JS に結合
• JavaScript からC/C++コードの呼び出し
• cwrap / ccall を利用
• WebIDL を作成し、binding を生成
• CからJavaScriptコードの呼び出し
• EM_ASMマクロの利用したコード埋め込み
• 外部関数 (extern)として呼び出し、リンク時に解決
ファイルアクセス
% emcc —preload-file hello.txt -o file.html fileio.c
—preload-file オプションをつけると read を XHR に変更
% emcc —preload-file hello.txt -o file.html fileio.c
% emcc --embed-file hello.txt -o hello.html file.c
—embed-file オプションで、ファイルを埋め込む
ccall / cwrap
extern "C" {
unsigned int fib(unsigned int n){
if(n < 3){
return 1;
}
return fib(n - 1) + fib(n - 2);
}
}
C++ での記述: エキスポートする関数名のマングリングを防ぐ
% emcc -o fib.html 
-s EXPORTED_FUNCTIONS="['_fib']" 
fib.cpp
不要なコードの削除を防ぐために EXPORTED_FUNCTIONS を指定
var fib = Module.cwrap("fib", "number", ["number"]);
console.log(fib(30));
var result = Module.ccall("fib",
"number", ["number"],
[30]);
console.log(result);
JavaScriptからの呼び出し: 識別子、型情報の指定が必要
WebIDL
class Peano{
public:
Peano();
int current();
void suc();
};
C++ でクラスを定義
class Peano{
public:
Peano();
int current();
void suc();
};
interface Peano{
void Peano();
long current();
void suc();
};
インタフェースを定義するWebIDLファイルを作成
% python tools/webidl_binder.py peano.webidl peano-glue
グルーコードを生成
#include "peano.cpp"
#include "peano-glue.cpp"
ラッパーを作成
% emcc -o peano.html 
--post-js peano-glue.js 
peano.cpp 
peano-wrapper.cpp
—post-js オプションをつけてemcc コンパイル
p = new Module.Peano();
console.log(p.current()); // 0 を出力
p.next();
console.log(p.current()); // 1 を出力
JavaScriptからはModule オブジェクトの属性として参照できる
CへのJavaScriptの埋め込み
#include <stdio.h>
#include <emscripten.h>
int main(int argc, char **argv){
EM_ASM({
alert("hello!");
});
}
EM_ASM マクロを使って JavaScript コードを埋め込める
x = EM_ASM_INT({
console.log("argument:"+ [$0, $1]);
return Math.pow($0, $1);
}, 2, 24);
printf("x = %dn", x);
引数、返り値のあるコードの埋め込み例
CからJavaScriptコードの呼び出し
#include <stdio.h>
extern "C"{
extern int id(int);
extern void hi();
}
外部関数を宣言
int main(int argc, char **argv){
int x = 10;
printf("id(%d) = %dn", x, id(x));
hi();
return 0;
}
リンクされる前提で呼び出す
mergeInto(LibraryManager.library, {
id: function(n){
return n;
},
hi: function(){
alert("hi");
}
});
library.js に関数を実装
-rw-r--r-- 1 chiko staff 81B 2 17 19:25 hello.c
-rw-r--r-- 1 chiko staff 100K 3 24 18:17 hello.html
-rw-r--r-- 1 chiko staff 466K 3 24 18:17 hello.js
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games

asm.js x emscripten: The foundation of the next level Web games