WebAssembly は、Web 上でコードを実行するためのバイナリ形式であり、高速な起動時間(JS や asm.js と比較して、ダウンロードサイズが小さく、ブラウザでの解析がはるかに高速)を実現します。Emscripten はデフォルトで WebAssembly にコンパイルしますが、古いブラウザ向けに JS にコンパイルすることもできます。
歴史的な背景については、これらのスライドとこのブログ記事を参照してください。
WebAssembly は、特別なフラグを必要とせずにデフォルトで出力されます。
注意
WebAssembly を使用したくない場合は、次のような設定で無効にできます。
emcc [..args..] -sWASM=0
注意
Wasm または JS のどちらにコンパイルするかは、リンク段階で決定できます。これは、オブジェクトファイルには影響しません。
Emscripten は、バージョン 1.39.0
(2019年10月) 以降、**アップストリーム LLVM Wasm バックエンド** を使用して WebAssembly を出力します。以前、emscripten は古い **fastcomp** バックエンドもサポートしていましたが、2.0.0
(2020年8月) で削除されました。
fastcomp からアップストリームにアップグレードすると、2つのバックエンド間でいくつかの違いに気付く場合があります。
Wasm バックエンドは、異なる機能セットを持つファイルのリンクに厳密です。たとえば、あるファイルがアトミックでビルドされ、別のファイルがビルドされていない場合、リンク時にエラーが発生します。これにより、起こりうるバグを防ぐことができますが、ビルドシステムを修正する必要がある場合があります。
WASM=0
は、2 つのバックエンドで動作が異なります。fastcomp では asm.js を出力しますが、アップストリームでは JS を出力します(すべての Wasm 構成要素を asm.js で表現できるわけではないため)。また、JS サポートは同じ外部 WebAssembly.*
API を実装しているため、特に起動はデフォルトで Wasm のように非同期になります。WASM_ASYNC_COMPILATION
で制御できます(WASM=0
でも)。
Wasm バックエンドは、デフォルトで Wasm オブジェクトファイルを使用します。つまり、コンパイルステップでコード生成が行われ、リンクステップが(通常のネイティブコンパイラのように)はるかに高速になります。比較として、fastcomp では、コンパイルステップで LLVM IR がオブジェクトファイルに出力されます。
通常はこれに気付くことはありませんが、DISABLE_EXCEPTION_CATCHING
など、一部のコンパイラフラグはコード生成に影響します。このようなフラグはコード生成時に渡す必要があります。簡単で安全な方法は、-s
フラグをコンパイル時とリンク時の両方に渡すことです。
通常の llvm フラグ (-flto
, -flto=full
, -flto=thin
) をコンパイル時とリンク時の両方に使用して、リンク時最適化(LTO)を有効にできます(ただし、thin LTO は現時点では十分にテストされていないため、通常の LTO が推奨されます)。
fastcomp では、LTO 最適化パスはデフォルトでは実行されませんでした。そのためには、--llvm-lto 1
を渡す必要がありました。llvm バックエンドでは、LTO パスはビットコード形式のオブジェクトファイルに対して実行されます。
もう 1 つ気付くかもしれないことは、fastcomp のリンク段階では、LTO を設定していなくても、一部の軽微なリンク時最適化を実行できることです。LLVM バックエンドでは、これらのことを行うには実際に LTO を設定する必要があります。
Wasm バックエンドで使用されるリンカーである wasm-ld では、ライブラリ(.a アーカイブ)にシンボルインデックスが含まれている必要があります。これは、ネイティブ GNU リンカーの動作と一致します。emar はデフォルトでこのようなインデックスを作成しますが、GNU ar や GNU strip などのネイティブツールは WebAssembly オブジェクト形式を認識せず、アーカイブインデックスを作成できません。特に、WebAssembly オブジェクトファイルを含むアーカイブファイルで GNU strip を実行すると、インデックスが削除され、リンク時にアーカイブが使用できなくなります。
また、Wasm バックエンドのブロッカーバグ、およびWasm バックエンドでタグ付けされた問題も参照してください。
WebAssembly は、ゼロ除算や、非常に大きな浮動小数点数を整数に丸めるなどの場合に、トラップ(例外をスロー)できます。asm.js では、このような処理は JavaScript で例外が発生しないため、暗黙的に無視されていました。これは JavaScript と WebAssembly の違いであり、ブラウザで float unrepresentable in integer range
、integer result unrepresentable
、integer overflow
、または Out of bounds Trunc operation
のようなエラーが報告されることがあります。
LLVM Wasm バックエンドは、発生する可能性のある各トラップの周囲にコードを追加することで(基本的には、トラップが発生する場合に値をクランプすることで)トラップを回避します。これにより、余分なコードが必要ない場合は、コードサイズが増加し、速度が低下する可能性があります。このための適切な解決策は、-mnontrapping-fptoint
を付けて emcc または clang を呼び出すことによって、トラップしない新しい Wasm 命令を使用することです。ただし、そのコードは古い VM では実行されない場合があります。
emcc
を使用して WebAssembly にビルドする場合、そのコードを含む .wasm
ファイルと、コンパイルの主なターゲットである通常の .js
ファイルが表示されます。これら 2 つは連携するように構築されています。.js
(または要求した場合は .html
)ファイルを実行すると、WebAssembly コードが読み込まれて設定され、インポートとエクスポートなどが適切に設定されます。基本的には、コンパイルされたコードが asm.js か WebAssembly かを気にする必要はありません。単なるコンパイラフラグであり、それ以外はすべて正常に動作するはずです(WebAssembly の方が高速になることを除いて)。
.wasm
ファイルはスタンドアロンではないことに注意してください。JSとの統合に必要な適切なインポートを取得する必要があるため、.js
コードなしで手動で実行するのは簡単ではありません。たとえば、コンソールへの出力などの処理を行うために、システムコール用のインポートを受け取ります。スタンドアロンの .wasm
ファイルを作成する方法については、現在作業が進行中です。詳細については、WebAssembly Standalone のページを参照してください。
仮想ファイルシステムにファイルをプリロードしている場合は、.data
ファイルのような追加のファイルが生成される場合もあります。これはすべて、asm.js にビルドする場合とまったく同じです。asm.js の静的メモリ初期化データを含む .mem file
がないことに気づくかもしれません。WebAssembly では、これを WebAssembly バイナリ自体に、より効率的にパックできます。
WebAssembly は、Firefox 52、Chrome 57、Safari 11、Opera 44 以降のすべての主要ブラウザでサポートされています。
さまざまなブラウザでサポートされている WebAssembly の機能の詳細については、WebAssembly ロードマップを参照してください。
.wasm
ファイルとコンパイル¶WebAssembly コードは asm.js とはやや異なる方法で準備されます。asm.js はメインの JS ファイル内にバンドルできますが、前述のように WebAssembly は別のバイナリファイルであるため、配布するファイルが複数になります。
もう 1 つ注目すべき点は、WebAssembly はデフォルトで非同期的にコンパイルされることです。つまり、コンパイルされたコードを呼び出す前に、コンパイルが完了するのを待つ必要があります(main()
を待つか、onRuntimeInitialized
コールバックなどを待つ必要があります。これは、asm.js の .mem
ファイルやプリロードされたファイルデータなど、起動を非同期にする他のものがある場合にも行う必要があります)。WASM_ASYNC_COMPILATION=0
を設定することで非同期コンパイルをオフにできますが、現在の Chrome の制限により、これは機能しない場合があります。
非同期コンパイルをオフにした場合でも、WebAssembly バイナリのフェッチは非同期操作である必要がある場合があります(Web では、メインスレッドでの同期バイナリダウンロードが許可されていないため)。自分でバイナリをフェッチできる場合は、Module['wasmBinary']
を設定すると、そこから使用され、(非同期コンパイルがオフの場合)コンパイルは同期的に行われるはずです。
ネットワーク上で Wasm を最も効率的な方法で提供するには、Web サーバーが .wasm
ファイルの適切な MIME タイプ(application/wasm)を持っていることを確認してください。これにより、ブラウザがダウンロードと同時にコードのコンパイルを開始できるストリーミングコンパイルが可能になります。
Apache では、次のように設定できます。
AddType application/wasm .wasm
また、gzip が有効になっていることを確認してください。
AddOutputFilterByType DEFLATE application/wasm
大きな .wasm
ファイルを提供する場合、Web サーバーは各リクエストでオンザフライでそれらを圧縮するために CPU を消費します。代わりに、.wasm.gz
に事前に圧縮し、コンテンツネゴシエーションを使用できます。
Options Multiviews
RemoveType .gz
AddEncoding x-gzip .gz
AddType application/wasm .wasm