WebAssembly のスタンドアロンビルド


## ご挨拶

この記事は [ピクシブ株式会社 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pixiv) 8日目の記事です。

今年の7月からピクシブの福岡オフィスでアルバイトをしています。@printf_moriken です。
JavaScript が得意でフロントエンドの領域ばかりやっていたのですが、ピクシブでバイトするにあたって Android と iOS アプリの開発もやり始めました。

好きな IEEE は [IEEE 754-2008](https://standards.ieee.org/findstds/standard/754-2008.html) です。個人的に JavaScript に半精度浮動小数点数を入れようと活動しています[^float16]。Stage 1 です。よろしくお願いします。

[^float16]: https://qiita.com/printf_moriken/items/1a16d759cefea1b3210e

## WebAssembly について

[WebAssembly](http://webassembly.org/) はブラウザでバイナリを実行できるようにするための技術、ファイル形式のことを言います。
旧来の技術に [asm.js](http://asmjs.org/) がありますが、バイナリ化したことによって以下のような利点があります。

* バイナリ化することによるファイル容量（通信量）の削減
* asm.js と比べてコンパイル時のバリデーションのコストが遥かに小さい（速い）

一方で以下のようなデメリットもあります。

* JavaScript にはバイナリを直接埋め込むことが出来ないため、XMLHttpRequest や Fetch API が必要

asm.js は Javacript のコード中に `"use asm";` 宣言をした特別な函数を埋め込む設計になっている一方で、WebAssembly では一応 Uint8Array でバイナリ配列を埋め込むようなことは出来ますがテキストなので非効率的です[^data-url][^binary-ast]。

WebAssembly の基本的なことについて詳しく知りたい方は @tkihira さんの以下の記事が勉強になります。
[WebAssembly の基礎](http://nmi.jp/2017-11-06-WebAssembly-Basics)

また今年の Advent Calendar にどしどし投稿されているみたいですので、興味のある方はそちらで最新情報を得てはいかがでしょうか。
[WebAssembly Advent Calendar 2017](https://qiita.com/advent-calendar/2017/webassembly)

[^data-url]: data URL を使ったほうが比較的効率的かな。
[^binary-ast]: ECMAScript Stage 1 で [Binary AST](https://github.com/syg/ecmascript-binary-ast) が議論されており、もしかしたらそのままバイナリを埋め込める日が来るかもしれません☺

## Emscripten について

[Emscripten](http://emscripten.org/) は C/C++ を LLVM を経由して asm.js に変換し、それを含んだ JavaScript を生成するツールです。

また [Binaryen](https://github.com/WebAssembly/binaryen) を使うことで asm.js の部分を WebAssembly に変換することが出来ますが、いずれにせよ JavaScript によってラップされる形で出力されます[^preamble]。

実験的機能に JavaScript にラップされないスタンドアロンビルドがあります[^em-standalone]が、まだ安定していないのと、標準ライブラリを自前で用意しないといけない設計でなかなか実用には厳しいです。

[^preamble]: ラップする JavaScript は[こちら](https://github.com/kripken/emscripten/blob/3ebf0eed375120626ae5c2233b26bf236ea90046/src/preamble.js)。
[^em-standalone]: https://github.com/kripken/emscripten/wiki/WebAssembly-Standalone

# WebAssembly のスタンドアロンビルド（脱！Emscripten）

Emscripten では JavaScript でラップされる形となってしまいますが、WebAssembly のバイナリ単体で得たいところです。利点としては以下の通り。

* ~~ブラウザにコンパイル後のキャッシュを持たせられる[^indexeddb]~~
* WebAssembly と JavaScript の相互のやり取りが容易になる
* Emscripten のラッパーの JavaScript が WebAssembly のパスを持つ……みたいなことをしなくてすむ[^path]
* 気分的に良い✨

WebAssembly をビルドする方法については現在色々なアプローチがあり、 Emscripten Night #5 での @chikoski さんの資料にわかりやすくまとまっています。
https://speakerdeck.com/chikoski/20171018-wasm?slide=9

ここでは以下のツール、言語について軽く触れようと思います。

* AssemblyScript
* dcodeIO/webassembly
* LLVM/Clang
* Rust

[^indexeddb]: ~~WebAssembly Advent Calendar 2017 3日目の記事 [巨大 WebAssembly ファイルのコンパイル時間](http://nmi.jp/2017-12-03-Profiling-of-huge-wasm)にも記載されていますが、`IndexedDB` に `WebAssembly.Module` を保存できます。~~[残念ながら廃止されてしまいました](https://www.fxsitecompat.com/ja/docs/2018/indexeddb-serialization-support-has-been-removed-from-webassembly-module/)。
[^path]: Emscripten を使った場合、ビルドした WebAssembly のバイナリのディレクトリを移動するとラッパーの JavaScript から参照できなくなり辛い目に合う……。

## AssemblyScript

[AssemblyScript](https://github.com/AssemblyScript/assemblyscript) は TypeScript のサブセットです。`malloc`, `free` 系も用意してあり、大体のコードは書くことができそうです。

@chikoski さんの記事に使用例が載っています。
[AssemblyScriptを使ってTypeScriptのコードを早くしよう](https://qiita.com/chikoski/items/d98ed0a98acfa896869f)


## dcodeIO/webassembly

[dcodeIO/webassembly](https://github.com/dcodeIO/webassembly) は WebAssembly 用に C の標準ライブラリを用意してコンパイルするツールの他、Node.js で WebAssembly が使える環境を提供してくれます。C で書かれた資源を WebAssembly で使えるようにするにはこちらを選ぶと良さそうです[^libc]。

@ukyo さんの記事に使用例が載っています。
[dcodeIO/webassembly内のツールチェーンでzlibをビルドしてみる](https://qiita.com/ukyo/items/ebc30ebf365718e5d16d)

[^libc]: 使える標準ライブラリの函数は[こちら](https://github.com/dcodeIO/webassembly/blob/master/lib/build.js)に書いてあります。

## LLVM/Clang

直接 C/C++ を WebAssembly にビルドする方法です。標準ライブラリを自前で用意する必要が有るため、C を使う場合だと前述の dcodeIO/webassembly を使ったほうがよさそうです。

具体的には musl, libcxx のヘッダーファイルを読み込みつつも、実体の函数やクラスは WebAssembly 向けではないため clang には食わせないみたいなことをする必要があります。そして吐き出された WebAssembly を使う時には足りないそれらの函数を JavaScript で補って、WebAssembly の import に食わせるみたいな中々ハッキ―なことをしないといけません[^egghead]。

[^egghead]: 今は有料ですが、[こちらの動画](https://egghead.io/courses/get-started-using-webassembly-wasm)が勉強になりました。

## Rust

[Rust](https://www.rust-lang.org/ja-JP/) は Mozilla が開発しているプログラミング言語です。もともと Emscripten を経由して WebAssembly にビルドできる言語でしたが、最近直接 LLVM の wasm32-unknown-unknown target にビルドできるようになったみたいです[^rust]。

Rust 自体は [rust-bindgen](https://github.com/rust-lang-nursery/rust-bindgen) を使うことで C/C++ の資源を使うことが出来るみたいですが、あくまでこれは FFI を生成するツールなので、WebAssembly のビルドに活かすのは厳しそうです。

@bokuweb さんの記事に使用例が載っています。
[Rust + WebAssembly でpngデコードを行うnode_moduleを作ってみる](https://qiita.com/bokuweb/items/87a169429960f0dae2cb)

[^rust]: https://www.hellorust.com/news/native-wasm-target.html

# 個人的な見解

現状だと完全にゼロから WebAssembly を出力したい場合は AssemblyScript を使い、C の資源を使いたい場合は dcodeIO/webassembly を使えばよさそうです。そして腕に自身はある人は Rust を使う。

個人的に vorbis を WebAssembly でデコードしたいのですが、libvorbis が依存している stdio.h 系の一部の函数が dcodeIO/webassembly では使えないみたいなのでなかなか厳しいです……と思っていたら [lewton](https://github.com/RustAudio/lewton) というものを見つけてしまったので、これから Rust を勉強しようかなと思います😂

# おわりに

WebAssembly のビルドツールが出揃ってきていよいよ本格的に使えるようになってきましたね！ みなさんも Rust を勉強して WebAssembly をプロダクトに投入していきましょう！

~~本来なら Rust から WebAssembly を生成する例をここに書く予定でしたが、一週間弱で Rust を習得することができませんでした。~~

明日は [SpiderMonkey に pipeline operator を実装](http://hakatashi.hatenadiary.com/entry/implementing-pipeline-operator)したすごいエンジニアの @hakatashi です。引き続き [ピクシブ株式会社 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pixiv) をお楽しみください。