WebAssemblyを試してみた

# なにこれ

名前そのままブラウザからアセンブリを実行できるようにする技術であり、高速化手段、あるいはJavaScriptの処理系にできないことをするといった目的で提案された。

現在多くのブラウザでサポートされているasm.jsとの違いは、JavaScriptのサブセットであるためにロードタイムに課題があったasm.jsに対し、WebAssemblyでは直接バイナリを読み込むことでこの問題を解決している。

現在、Chrome(Canary)に実装され、簡単に試すことができるようになった。

# 試したよ

## 参考にした記事

* [WebAssemblyを使ってみる(C/C++をWebAssemblyに変換してChromeで実行)](http://qiita.com/HirokiMiyaoka@github/items/89975a9e8205ced3603f)
* [WebAssembly を使って自作言語をブラウザで動かしてみよう](http://rhysd.hatenablog.com/entry/2016/03/24/235639)

## つかうもの

* Google Chrome Canary
* llvm
* clang
* [binaryen](https://github.com/WebAssembly/binaryen)
* [sexpr-wasm-prototype](https://github.com/WebAssembly/sexpr-wasm-prototype)

### Google Chrome Canary

WebAssemblyを使うには `chrome://flags/#enable-webassembly` にチェックをつけて有効化する。

### llvm

`-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly`を付けて`cmake`する。

brew経由でインストールする場合には`brew edit llvm`で追加。

### clang

Macに標準で入ってるclangではうまくいかなかったので`clang-3.8`を使った。おそらく`brew install llvm38 --with-clang`のものだと思う。

### binaryen

[https://github.com/WebAssembly/binaryen](https://github.com/WebAssembly/binaryen)

クローンしてきてビルド&インストール

### sexpr-wasm-prototype

[https://github.com/WebAssembly/sexpr-wasm-prototype](https://github.com/WebAssembly/sexpr-wasm-prototype)

クローンしてきてビルド&インストール

(クローンしたあと`git submodule update --init`忘れずに)

## 変換してみる

変換はこんな感じの流れ

```
C/C++ -> LLVM bitecode -> Assembly -> S-expression -> binary
```

今回はC++のコードを変換する。

```cpp:sample.cpp
extern "C" {
  int fib(int n) {
    int i, t, a = 0, b = 1;
    for (i = 0; i < n; i++) {
      t = a + b; a = b; b = t;
    }
    return b;
  }
}
```

C++の場合、関数名がmanglingされてしまうので、回避するために`extern "C" {}`をしてる。

#####1. まずC++からLLVMバイトコードに変換

```
clang++-3.8 sample.cpp -emit-llvm --target=wasm32 -Oz -c -o sample.bc
```

#####2. LLVMバイトコードからアセンブリに変換

```
llc sample.bc -march=wasm32 -filetype=asm -o sample.s
```

#####3. アセンブリからS式に変換

```
s2wasm sample.s > sample.wast
```

#####4. S式からWebAssemblyバイナリに変換

```
sexpr-wasm sample.wast -o sample.wasm
```

これで読み込むための`.wasm`ができあがる。

前述の参考にした記事ではLLVM IR形式(`.ll`)に変換してからアセンブリに変換していた。この方法でも同様の結果が得られる。

## 実行してみる

jsはこんな感じ

```js:index.js
var xhr = new XMLHttpRequest();
xhr.open('GET', 'sample.wasm', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
  var binary = xhr.response;
  var binarray = new Uint8Array(binary);
  var module = Wasm.instantiateModule(binarray);
  console.log(module.exports);
  var fib = module.exports.fib;
  var arr = [];
  for (var i = 0; i < 20; i++) {
    arr.push(fib(i));
  }
  console.log(arr);
};
xhr.send(null);
```

出来上がったバイナリをXHRで取ってきて`Uint8Array`に変換して`Wasm.instantiateModule`に投げると、オブジェクトが返ってくる。

<img width="624" alt="スクリーンショット 2016-05-03 2.33.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/14939/9c690587-6a41-b082-f5de-3f40da975ff8.png">

返ってきたオブジェクトの`exports`の中にC++で定義した関数が入っているのでそのまま引数を渡してアクセスできる。

今回は20個のフィボナッチ数列を表示するだけ。

# で

原因がわからないのだが、clangでのビルドで最適化を切るとWebAssemblyのバイナリはできるが、実行するとエラーが出てしまった。いつか解決したい。

<img width="335" alt="スクリーンショット 2016-05-03 2.33.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/14939/03e581ac-e19e-af59-e44a-837a56a812c8.png">

WebのフロントエンドにC/C++のコードが介入してくると考えるとフレームワークやライブラリのパラダイムも変わってくるのかもしれない。

いま熱くなってきているWebAssemblyだが、まだ情報は少ないため、再び試す機会があればやってみようと思う。

今回のコードはgithubにポイしておいた。

[https://github.com/pnlybubbles-gomibako/wasm-sample](https://github.com/pnlybubbles-gomibako/wasm-sample)