asm.js x emscripten: the foundation of the next level web games
Post on 15-Apr-2017
1.419 Views
Preview:
TRANSCRIPT
asm.js x Emscripten: The foundation of the next level web games
Mozilla Japan テクニカルマーケティング 清水智公 (nshimizu@mozilla-japan.org / @chikoski)
第63回 HTML5とか勉強会 with html5jゲーム部発足記念合同勉強会
N. Shimizu
• Mozilla Japan (L10N, Game, Devtools, Web Animation)
• html5j Web プラットフォーム部、
Firefox OS、Firefox OS コードリーディング、
html5j ゲーム部 (NEW!)
• @chikoski
• プログラミング言語、分類、ベイジアン、サッカー、圏論(NEW!)
文字列字句解析 / 構文解析
実行
バイトコードASTBaseline
Compiled Code
MIRIron
Compiled Code
実行と プロファイル 実行Bail
生成Baseline Compile
Iron BuildIron Compile
* AST: Abstract Syntax Tree / 抽象構文木 MIR: Medium-level Intermediate Representation / 中間表現
* AST: Abstract Syntax Tree / 抽象構文木 MIR: Medium-level Intermediate Representation / 中間表現
文字列字句解析 / 構文解析
実行
バイトコードASTBaseline
Compiled Code
MIRIron
Compiled Code
実行と プロファイル 実行Bail
生成Baseline Compile
Iron BuildIron Compile• JITは重い
• hot code(よく実行されるコード)しかコンパイルされない
• JITが始まるまでは、遅いまま • 型推論に失敗するなどして、JITをやり直すことがある
ダウンロード 実行開始 コンパイル
asm.js : an extraordinarily optimizable, low-level subset of JavaScript
• JavaScript として評価可能
• 数値計算、関数の定義と呼び出し、ArrayBufferの操作のみ可能
• その他のことは不可能
• 型アノテーションと、フォーマルな検証プロセス
• FFIを通じたJavaScript との相互呼び出し
• ArrayBuffer の共有によるデータの受け渡し
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関数のインポート
型アノテーション
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 の関数
function Peano(stdlib, ffi, heap){"use asm";var abs = stdlib.Math.abs; // 標準ライブラリからのインポート
var z = ffi.zero | 0; // 定数の受け渡し
var log = ffi.log; // JS関数のインポート
標準ライブラリ、外部関数、ヒープ
標準ライブラリ 型Infinity
NaNdouble
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?) → floatMath.min Math.max
(int, int…) → signed ∧ (double, double…) → double
Math.atan2 Math.pow
(double?, double?) → double
標準ライブラリ 型Math.imul (int, int) → signed
Math.fround froundMath.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); // これはリンクできる
外部関数の呼び出しにも配慮が必要
単項演算子 型
+ (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.jshello world
% emcc -o hello.html hello.c% open hello.html
-o オプションで html / js へ出力
Emscripten compiles C / C++ into JavaScript in asm.js format
• ファイル入出力: XHR に書き換え / ファイルを JS に結合
• JavaScript からC/C++コードの呼び出し
• cwrap / ccall を利用
• WebIDL を作成し、binding を生成
• CからJavaScriptコードの呼び出し
• EM_ASMマクロの利用したコード埋め込み
• 外部関数 (extern)として呼び出し、リンク時に解決
% emcc —preload-file hello.txt -o file.html fileio.c
% emcc --embed-file hello.txt -o hello.html file.c
—embed-file オプションで、ファイルを埋め込む
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からの呼び出し: 識別子、型情報の指定が必要
class Peano{public: Peano(); int current(); void suc();};
interface Peano{ void Peano(); long current(); void suc();};
インタフェースを定義するWebIDLファイルを作成
% 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 オブジェクトの属性として参照できる
#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 = %d\n", x);
引数、返り値のあるコードの埋め込み例
int main(int argc, char **argv){ int x = 10;
printf("id(%d) = %d\n", 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
top related