WebAssembly
移植性があり効率的なウェブ向けのバイトコード形式のアセンブリ風言語 ウィキペディアから
WebAssembly(ウェブアセンブリ、公式の略称はWasm[2])は、実行可能ファイルを表現するための可搬なバイナリコード形式(およびそれに対応するテキスト形式[3])を定め、同時にそのような実行可能プログラムとホスト環境との間のやりとりを容易にするためのソフトウェアインタフェースを提供するものである[4][5][6][7]。
![]() | |
パラダイム | 式指向 |
---|---|
最新リリース | 2.0 / 2022年6月1日[1] |
型付け | 静的 |
影響を受けた言語 | asm.js, PNaCl |
ライセンス | Apache License |
ウェブサイト | webassembly.org |
拡張子 | .wast, .wasm |
Wasmの当初の目的は、ウェブページ上で高パフォーマンスなアプリケーションを実現することであった。しかし「ウェブ特有の仮定は一切しておらず、ウェブ特有の機能を提供するわけでもないので、他の環境で同様に採用できる」[8]としている。Wasmはオープン標準であり[9][10]、あらゆるオペレーティングシステム上であらゆる言語をサポートすることを目指している[11]。実際に、最も有名な言語のすべてが、少なくともいくらかのレベルでWasmに対応している。
Wasmは、仮想の命令セットアーキテクチャあるいはプログラミング言語の一種[12]とも捉えられる。多くの場合、C/C++, Rustといったプログラミング言語のコンパイル先としてWasmバイナリが生成され、ウェブブラウザやその他のソフトウェア環境内で、スタックマシンによって実行される。ネイティブコードに近い高速さ、隔離環境でのメモリ安全な実行による安全性、仮想マシンによる可搬性やソースプログラミング言語中立性などを特徴とする[13]。
Wasmは2015年に発表され、2017年3月に初めてリリースされたWebAssemblyは、2019年12月5日にW3C勧告となり、2021年の ACM SIGPLAN のプログラミング言語ソフトウェア賞を受賞した[14]。World Wide Web Consortium (W3C) が技術標準を管理しており、有名な大企業なども加盟する非営利団体「Bytecode Alliance」が貢献している[15][16]。
歴史
要約
視点
WebAssembly という命名は、1950年代にまで遡る「アセンブリ言語」の概念を想起させるためのものであり、アセンブリのようなプログラミングを、Webのクライアントサイド(つまりユーザのWebブラウザを介したユーザのコンピュータ上)で実行される世界に持ち込むことを示唆している。これを実現するために、WebAssemblyは、真のアセンブリ言語に比べて圧倒的にハードウェア非依存でなければならない。
WebAssemblyの最初のアナウンスは2015年6月17日に行われ[17]、2016年3月15日に公開された初のデモは、主要ブラウザ (Firefox, Chromium, Google Chrome, Microsoft Edge) 上でUnityのAngry Botsを動かすものだった[18][19][20]。前身となった技術は、Mozillaによる asm.jsと Google Native Client であり、最初の実装は asm.js の機能セットに基づいて行われた。asm.jsはすでにネイティブに近いコード実行速度を提供しており[21][22]、WebAssembly非対応の環境やセキュリティ上の理由で無効化している環境での実用可能な代替とみなすことができた。
最初の目標としてCとC++からのコンパイルをサポートすることを目指し[23]、Rustがバージョン1.14以降で[24]、Goがバージョン1.11以降で[25]、Kotlin/Nativeがバージョン0.4以降で[26]で対応するなど、他のプログラミング言語のサポートも進められた。
2017年3月にMVPの設計が完了したことが宣言され、WebAssemblyに標準対応した初のブラウザとなるFirefox 52.0がリリースされた[27]。2017年11月、MozillaはMicrosoft EdgeがWebAssemblyに対応し、すでに対応しているChromeとFirefoxを含め、主要なブラウザすべてでサポートされたことを発表した[28]。2018年2月には、WebAssembly Working Groupが、3つのワーキングドラフト「Core Specification」「JavaScript Interface」「Web API」を公開した。
2019年6月に、Chrome 75は、WebAssemblyスレッドが有効にされた状態でリリースされた。
2019年12月5月に、W3C勧告「WebAssembly Core Specification」が策定され、WebAssemblyは正式なウェブ標準に認定された[29]。
2022年8月から、WebAssembly 2.0がドラフトの状態になり[30][31]、SIMD関連の多数の命令やv128データ型、関数が多値を返せる機能、大量のメモリの初期化・コピー、などが追加されている。
2023年10月から12月にかけて、Wasmランタイム側でガベージコレクション (GC) に対応する WebAssembly Garbage Collection (WasmGC) が、Chrome, Firefox, Edge の最新版でデフォルトで有効になった。
実装
要約
視点
WebAssembly (Wasm) は当初、Webブラウザでネイティブに近いコード実行速度を実現するために設計されたものの、今ではWebブラウザの枠を出た、より一般的な文脈や用途でも価値があるとみなされている[32][33]。
Wasm自体は命令セットなどの面を定めるだけであり、Linuxカーネルが提供するようなシステムコール(例: ファイルI/O)やWebブラウザが提供するようなDOMアクセスなどを提供しない[34]。安全性や可搬性はこの特徴に由来する。応用に必要なAPIは別途に定義され、Wasmランタイムがそれを実装することでそれらの機能を提供する[35]。これによってWasmエコシステムは高い拡張性を有する。
Webブラウザ
2017年11月にMozillaは、Microsoft Edge 16でWebAssemblyがデフォルトで有効になった後[36]、「すべての主要なブラウザでの」Wasm対応を宣言した[37]。この対応にはiOSとAndroidのモバイルWebブラウザも含まれる。 2024年3月現在[update]。99%のブラウザがWebAssembly(バージョン1.0)をサポートしており[38]、その前身であるasm.js(Safariなどで使えなかった)を上回っている。 バージョン2.0ドラフト標準に由来するいくつかの拡張については対応度は下がりうるが、それでも9割以上のブラウザが既にサポートしていると推測される[39]。
スタンドアローン環境およびWASI
Wasmのランタイム環境 (RE) は、(JVMやFlash VMと同様の)低レベルの仮想スタックマシンであり、ホストアプリケーションに埋め込むことができるため、Webブラウザにとらわれない活用の道があり、WasmtimeやWasmerといったスタンドアローンのランタイムが作られた[40][41]。
WebAssembly System Interface (WASI) は、Mozillaによって設計された、どのプラットフォームにも移植できるように意図されたシンプルなインタフェース(ABIおよびAPI)である[42]。これは、POSIXライクの機能を提供するもので、例えば「ケーパビリティベースのセキュリティによって制約されたファイルI/O」のようなものを提供する[43][44]。他にもいくつかのABI/APIが提案されている[45]。WASIは、CapsicumのCloudABIの影響を受けている。
Docker の共同創業者であるソロモン・ハイクスは2019年に、「もし2008年にWASM+WASIが存在していたらDockerを作る必要はなかっただろう、というくらい重要なものだ。サーバ上でのWebAssemblyはコンピューティングの未来だ」と書いた[46]。
仕様
要約
視点
WebAssemblyはポータブルなスタックマシン[47]であり、既存のウェブブラウザで広く用いられているJavaScriptと比べ、構文解析と実行が高速になるよう設計されている[23]。WASM specification はWASMの言語仕様および実行ファイル形式を定義する。
言語
Wasmは高水準のアセンブリ言語として設計されている。x64等のアセンブリ言語にみられない特徴として以下がある。
- 制御命令:
if
やloop
。直接のJUMP命令が無い、安全かつ高水準の制御フローを実現(構造化プログラミング)[48] - 関数: 型・ローカル変数・ボディで定義され[49]、
call
される関数。コードの組織化を実現[50]- ローカル変数: 関数スコープの自動変数。
get
/set
/tee
命令でアクセスでき疑似レジスタとして利用可能[51]
- ローカル変数: 関数スコープの自動変数。
値型
値に定義される型は i32
/ i64
/ f32
/ f64
の4種類である[52]。char/stringは型としてサポートされない。また、高級言語のように関数が構造体型を取り回すようなコードが直接的には記述できない。
入出力
デフォルトでは外部と隔離されている(サンドボックス)。計算結果を渡したり外部関数を呼び出したりするために、WASMは imports/exports 機能を提供する。対象となるオブジェクトは関数・テーブル・メモリ・グローバル変数の4種類。exports要素に対象のインデックスを登録することで、WASM外からそのオブジェクトへアクセスできる(関数呼び出しやメモリ読み書き)。またimports要素に対象の名前と型を登録することで、WASM外に存在する対象へのアクセスをWASMランタイムが提供する。
フォーマット
Wasmはバイナリフォーマット(英: binary format)およびテキストフォーマット(英: text format)を定義している。フォーマットの設計方針として Compact / Modular / Efficient / Streamable / Parallelizable / Portable を掲げている。
言語のバイナリ/テキスト表現(例: オペコード)に加え、実行ファイル形式(コンテナフォーマット)を定義する。module が1つの実行ファイルに相当し、マジックナンバー等のメタ情報が冒頭に記述され、関数やexportsなど11種類のセクションが続く。セクションはサイズ情報を持っており、並行処理可能に設計されている。
以下に、C言語のソースコードが、wasmのリニアアセンブリバイトコードとバイナリにそれぞれ変換された例を示す。
C(変換元) | WASMテキストフォーマット (中間表現) | WASMバイナリフォーマット (16進数で表記) |
---|---|---|
int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n-1);
}
|
(func (param i64) (result i64)
local.get 0
i64.eqz
if (result i64)
i64.const 1
else
local.get 0
local.get 0
i64.const 1
i64.sub
call 0
i64.mul
end)
|
20 00
42 00
51
04 7e
42 01
05
20 00
20 00
42 01
7d
10 00
7e
0b
|
内部的には、wasmコンパイラシステムは中間コードを扱うためにS式を使用している。サンプルを以下に示す。[54]
(module
(memory 256 256)
(export "memory" memory)
(type $FUNCSIG$dd (func (param f64) (result f64)))
(import $exp "global.Math" "exp" (param f64) (result f64))
(export "doubleExp" $doubleExp)
(func $doubleExp (param $0 f64) (result f64)
(f64.mul
(call_import $exp
(get_local $0)
)
(f64.const 2)
)
)
)
SIMD
Wasmのバージョン 2.0(ドラフト)には128ビット幅のベクトル型(v128
)と、SIMD命令が追加されている[55]。
連携
Wasmはホスト環境に埋め込まれるサンドボックスであり、import/exportを介したホストとの連携によって意味ある結果が得られる。WasmがシンプルなVMである反面として、Wasm-ホスト間の連携にはいくつかのテクニックが必要となる。また各moduleが1つのサンドボックスとして働くため、Wasmモジュール間の連携にも同様のことが言える。
基本的な問題として、Wasm-ホスト間の型システム不一致解消(マーシャリング)を取り扱うことになる。
文字列
char/stringは型としてサポートされない。Wasm上で扱うためにはcharをintとして操作し、線形メモリ上にchar配列を構築する形になる。export関数も数値型しか返せないため、stringの表現として線形メモリのオフセットと長さを返し(intなので返せる)これに基づきランタイム側で該当メモリ区間を文字列型として読む、といった工夫が必要となる。あるいはmalloc等の外部アロケータをimportした上で線形メモリの内容を外部メモリへ割り当て、そのアドレス・参照をintでランタイムへ返す必要がある。
ツール
要約
視点
- Emscripten - 元々asm.js向けであったが、その後、WebAssemblyにも対応した。C言語/C++からWebAssemblyへのコンパイルでは、フロントエンドにclangもしくはそのforkであるfastcomp-clangを、中間層にLLVMもしくはそのforkであるfastcompを、バックエンドにbinaryen (後述)を使用する[56]。なお、LLVMのWebAssembly実装とFastcompのWebAssembly実装は別物である。
- GCC asm.js backend - asm.js及びWebAssemblyに対応している[57]。
- LLVM - WebAssemblyバックエンドを持ち、WebAssemblyバイナリを直接出力できる。また、LLDによるWebAssemblyバイナリのリンクも可能。LLVM 8.0で正式に対応した[58]。
バックエンド
- Binaryen
- WABT
- wat2wasm - WebAssemblyテキストからWebAssemblyバイナリへのコンバータ。
- wasm-link - WebAssemblyバイナリのリンカー
ランタイム
仮想マシンの命令セットであるWASMはコンピュータの実行ファイルではないため、そのまま機械語として実行はできない。そのためWASMファイルはランタイムを介し解釈・実行される。ランタイムにはインタプリタ/JITコンパイラ/AOTコンパイラがあり用途に合わせて選択される。
実行環境 | エンジン/ランタイム | コンパイラ |
---|---|---|
Google Chrome & Node.js | V8 | Liftoff (baseline)[59] + TurboFan (optimizing)[60] |
Mozilla Firefox | SpiderMonkey[61] | rabaldr (baseline) + BaldrMonkey (optimizing, backed by IonMonkey) |
wasmtime[62] | - | Cranelift[63] |
Wasmer[64] | - | pluggable (LLVM, Cranelift)[65] |
ランタイムはウェブブラウザへの組み込み(JavaScript実行環境からの呼び出し)や独立したWASMネイティブランタイム (例: CLIにおける $ wasmtime foo.wasm
[66]) として存在する。
開発環境
- WebAssembly Studio - WebベースのWebAssembly向け開発環境。C言語及びRustに対応している。オープンソース[69]。
ライブラリ
- Qt for WebAssembly - アプリケーションフレームワークのQtをWebAssemblyに移植したもの。2018年4月現在、テクノロジープレビュー。
参考文献
- V8.dev. WebAssembly compilation pipeline.
- [70]
脚注
関連項目
外部リンク
Wikiwand - on
Seamless Wikipedia browsing. On steroids.