WebAssembly Advent Calendar 2017の9日目の記事です。

WebAssemblyを使っていて気づいたことの小ネタ集です。

デバッグ

EmscriptenでC/C++から出力する場合、デフォルトではasm.jsを経由して生成されます。したがって何かバグが発生した場合には、次のような可能性があります。

  1. そもそもC/C++に間違いがある
  2. C/C++からasm.jsへの変換で何か想定外のことが起きている
  3. asm.jsからwasmへの変換で何か想定外のことが起きている
  4. ブラウザ(実行エンジン)がおかしい

基本的には、1.から順番に可能性を潰していくのが確実です。

自分の場合はasm.jsの出力を使って1.と2.両方まとめてデバッグすることが多いです。ブラウザのデバッガはJSファイルが大きすぎて役に立たない(ブラウザが固まる)ので、printfデバッグならぬconsole.logデバッグを用います。

asm.jsに直接デバッグコードを書き込んで調べていくのですが、JavaScriptの変数はasm.jsで使われるメモリとは独立なので、ヒープやスタックの状態を壊さずに内部の状態を調べることができます。また、リビルドが必要ないことも大きな利点です。条件式なども自由に書けるので、下手にデバッガを使うよりは柔軟に調査ができます。難点は、asm.jsのコードが読みにくいことですが、一から書くのは大変でも軽くパッチを当てたりする程度なら問題ないでしょう。

asm.jsに問題がないことが確認できたら、3.の確認を行います。asm.jsからwasmへの変換は、binaryenのasm2wasmが行っています。wasm-dis(binaryenの付属コマンド)でwastに直したり、JSコンソールからwasmの関数を直接呼び出してみるなどしたりして、asm2wasmの動作が想定通りであるかを確認します。しかし、ここ問題が見つかることは、ほとんどありませんでした。どちらかと言うと、wasmを実行するためのJSに問題があることが多いです。(特に利用者が少なそうな機能。例えばdynamic link周りとか)

4.は、もう完全に自分の手に余るので、最小再現コードを作って、しかるべき場所にissueを投げることを目指します。

今後はwasmを直接出力するケース増えると思うので、この方法は使えなくなりそうです。しかし、大きなファイルを出力するケースも増えていますし、近い将来使いやすいデバッガがブラウザに組み込まれるんじゃないかと希望的観測を持っています。

64bit整数型

WebAssemblyの変数には、i32(32bit整数), i64(64bit整数), f32(32bit浮動小数点数), f64(64bit浮動小数点数)という4つの型が存在します。一方で、JavaScriptは数値は64bit浮動小数点しか存在しません。そのため、i32,f32,f64は1対1に対応する値が存在しますが、i64はそのままでは扱えません。

ですが、実はEmscriptenではasm.jsの時代から決まった受け渡し方法があります。おそらくEmscripten固有でundocumentedな動作なので将来に渡って動く保証はありません。

まず、引数に関しては、下位32bitと、上位32bitの2つに分けて渡します。返り値は、下位32bitが直接返却され、上位32bitはModule.getTempRet0()から取得します。

例えば、

int64_t mul_i64(int64_t a, int64_t b)
{
        return a * b;
}

という関数をJavaScriptから利用する際には、

var ret_low = Module._mul_i64(a_low, a_high, b_low, b_high);
var ret_high = Module.getTempRet0();

とする必要があります。

なお、wasm2disなどでwasmバイナリを覗いてみるとわかるのですが、中では64bitをそのまま受け渡しする関数とラッパー関数の2つが定義されています。内部の呼び出しでは前者が使われるのでオーバーヘッドの心配はないでしょう。

カスタムセクション

wasmバイナリは、4byteのmagic number、4byteのバージョンのあとは、「セクションID(1byte)+コンテンツのサイズ(4byte)+コンテンツ」という「セクション」の繰り返しでできています。

ここで、セクションID=0はカスタムセクションとして任意のデータを格納できるようになっており(ブラウザは無視する)、デバッグ情報などに用いる想定のようです。例えば、Emscriptenではダイナミックリンクで用いています。 https://github.com/kripken/emscripten/blob/3bc1f9f08b9f420680124af703c787244468cedc/src/runtime.js#L362-L387

だからどうしたという話ですが、バイナリをいじるのが好きな人はいろいろ妄想を掻き立てられるのではないでしょうか?