WebAssemblyのバリデーションとフォールバック

---

## はじめに
WebAssembly (以下、Wasm) はリリース当初の仕様に加え、少しずつ新しい機能が追加され続けています。こうした新機能は必ずしも一斉にすべてのブラウザへ実装されるわけではなく、**ブラウザごとの対応状況に差がある** のが現状です。

参考：[主要ブラウザの機能拡張の実装状況](https://webassembly.org/features/)

たとえば、メモリ空間の根幹に関わる **64 ビットメモリ (Memory64)** のように実行環境へ大きな影響を及ぼす拡張や、一見すると実行時に自動で通常のSIMDへフォールバックできそうな **Relaxed SIMD 対応** など、各ブラウザでの対応段階は様々です。

本記事では、そうした **「Wasm の新機能が追加された一方で、ブラウザ対応がまちまちだが、どのように対処すればよいか？」** という疑問に対して、**バリデーションとフォールバック** を中心に解説します。

---

## WebAssembly のバリデーションとインスタンス化

Wasm のモジュールは以下のフローで読み込まれます。

1. **バイトコード（`.wasm`）を取得**  
   - ネットワークからダウンロードする、`fetch` を用いる、あるいはバンドル済みのバイナリをローカルから読み込むなど、さまざまな方法があります。

2. **コンパイル**  
   - ブラウザやランタイムが、Wasm バイトコードをネイティブコード（またはエンジン内部形式）へ変換します。

3. **バリデーション**  
   - モジュール内で使用している命令や機能が、現在のブラウザ（またはランタイム）のサポート範囲内かどうかを確認します。

4. **インスタンス化**  
   - 実行時用のメモリや関数テーブルを確保して初期化し、JavaScript などのホスト言語から呼び出せるようにします。

もし **3. バリデーション** の段階で「サポート外の命令や拡張機能を使っている」と判定されると、**モジュールのインスタンス化（4.）に進む前にエラーが発生** してしまい、実行に至りません。

### フロー図

以下は、この読み込み手順を視覚化した図です。

```mermaid
flowchart TD
    A[Wasmファイルの取得<br>.wasm のダウンロードなど] --> B[コンパイル<br><br>Wasmバイトコード<br>↓<br>ネイティブコード]
    B --> C[バリデーション<br>ブラウザが対応している命令か検証]
    C -- バリデーションOK --> D[インスタンス化<br>メモリや関数テーブルの初期化]
    C -- バリデーションNG --> E[エラー<br>実行不可]
    D --> F[実行可能<br>JSから呼び出し]
```

- **バリデーションNG** の場合は、その場でエラーとなり、インスタンス化や後続の処理は行われません。

:::note info
開発者がバリデーション結果を判断する際には、**「ブラウザ（ランタイム）が ‘`ある機能をサポート’ しているかどうか」** の結果に注目すれば十分です。CPUのハードウェア命令セットは実装ごとに抽象化されており、Wasm開発者は気にする必要はありません。
:::

---

## 64 ビットメモリ（Memory64）

### Memory64 とは

WebAssembly の **Memory64** 機能は、従来の WebAssembly が持つ  
$2^{32}\ \text{バイト} \quad (\text{約 4GB})$
というメモリ制限を超え、より大きなメモリ空間を扱えるようにする拡張仕様です。これにより、巨大な配列を扱う科学技術計算や大規模ゲームなどのアプリケーションでも、Wasm を利用できる可能性が広がります。

### WebAssembly 2.0 ドラフト仕様での扱い

- 現時点の **WebAssembly 2.0** ドラフト仕様においても、線形メモリのインデックスは **32 ビット** のままです。  
- 一方、**Memory64** 拡張 は "**Phase 4 - Standardize the Feature**" に到達しており、将来的に **WebAssembly 2.x** やその後のバージョンで正式統合される見込みです。

つまり、WebAssembly 2.0 には含まれていないものの、**Memory64** が標準仕様として追加される日は近い状況です。

### バリデーション失敗の例

ただし、**すべてのブラウザが Memory64 をサポートしているわけではありません**。そのため、以下のような事態が起こり得ます。

1. モジュールが **Memory64** を使うコードを含んでいる  
2. ブラウザが Memory64 を未サポート  
3. バリデーションで「未知の拡張機能」と見なされエラー  
4. インスタンス化に進めず、モジュール実行ができない

**「実行時に自動で 64 ビットから 32 ビットに落としてくれる」** といった仕組みは、WebAssembly の仕様上用意されていません。

### 各ブラウザでの対応状況

2025年2月6日時点での主なブラウザにおける **Memory64** 対応状況は以下の通りです。

- **Chromium 系ブラウザ（Chrome、Edge、Opera など）**  
  - Chromium エンジン内に実験的実装が存在。  
  - `chrome://flags` などでフラグをオンにする、またはオリジントライアルを利用すると 4GB 超のメモリアクセスが可能。  

- **Firefox**  
  - まだ正式リリースされておらず、実験または検討中という情報が多い。  
  - 2025年2月時点では有効化できないため、実用上 Memory64 は使用不可。

- **Safari**  
  - Safari (WebKit) でも公式には未実装。  
  - Apple 側からの正式発表がないため、利用は難しい状況。

大容量メモリを必要とするアプリケーションを WebAssembly で開発する場合、対象ブラウザの対応状況を調査し、**未対応環境ではフォールバック** などの対策を講じる必要があります。

:::note info
なお、Memory64 をサポートしているブラウザであっても、32ビット環境の場合は、ブラウザが Memory64 を「サポートしない」と報告すると考えられます。結果として、バリデーションでエラーとなり、Memory64 を使うモジュールは実行できません。
:::

---

## SIMD の Relaxed SIMD 拡張命令なども「自動フォールバックはない」

64 ビットメモリだけでなく、**SIMD (Single Instruction, Multiple Data) 命令** などの **拡張命令** についても同様です。  
たとえば **Relaxed SIMD** を使ったモジュールを作成した場合、未対応ブラウザではバリデーション段階でエラーとなり、実行できません。

### なぜソフトウェアエミュレーションへ自動で切り替わらないのか

C/C++ といったネイティブ環境であれば、CPU が SSE/AVX 命令をサポートしていない際にソフトウェア実装へフォールバックする――という仕組みを提供するコンパイラやランタイムがあります。しかし、Wasm では

- **「モジュールに含まれる命令は、すべてホストがサポートしている前提でインスタンス化する」**  
- 実行前の **バリデーション** で「未知・未サポートの命令」が含まれるモジュールは即エラー  
- セキュリティやパフォーマンスを重視した設計

といった理由から、**自動的なエミュレーション** は行われません。そのため、Relaxed SIMD 命令を含むモジュールを未対応ブラウザでロードしようとしても失敗します。

---

## 実験段階の機能と正式仕様化後の違い

実験段階（たとえば Chromium 系ブラウザでフラグやオリジントライアルを通じて有効化する Memory64 等）の機能と、**正式仕様としてデフォルト有効になった機能** では、いずれも **「未対応環境でエラーが起きる」** 点は同じです。  
したがって、**フォールバック策の重要性** はどちらの場合も変わりません。

ただし、実験段階の機能を活用する場合には以下の点に注意してください。

1. **ブラウザ側の設定が必要**  
   - `chrome://flags` でフラグを有効化、オリジントライアルのトークンを適用するなどの操作が必要で、一般ユーザにはデフォルト無効なことが多い。  
2. **仕様や実装が変わるリスク**  
   - 正式仕様になる前は、破壊的変更や名称変更などを行う可能性が高い。  
   - ブラウザ開発版のアップデートで挙動が急変することもある。  
3. **対応ブラウザが限られる**  
   - デフォルトで無効化されているので、ユーザが意識的に設定しない限り使えず、実際に有効化しているユーザは少ないことが多い。

**正式仕様化後** は、多くのブラウザでデフォルト有効となるため設定作業は不要になります。しかし、**古いバージョンや別のエンジン** が未対応のままの可能性があるため、最終的には **Feature Detection** と **フォールバック** が必要です。

---

## 図解: Feature Detection とモジュール読み込みフロー

以下は、**Memory64** を例に機能検出（Feature Detection）と **フォールバック** を組み合わせたモジュール読み込みフローを、図で表したものです。

```mermaid
flowchart LR
    A[ブラウザ起動<br>JSコード実行] --> B{Memory64 は<br>サポートされている？}
    B -- Yes --> C[Memory64 を含む<br>.wasm をロード]
    B -- No --> D[32ビット版<br>.wasm をロード]
    C --> E[バリデーション<br>インスタンス化]
    D --> E[バリデーション<br>インスタンス化]
    E[実行可能<br>Wasm呼び出し]
```

1. JavaScript 側で「Memory64 を含む最小限のモジュール」を **WebAssembly.validate()** などでテストし、対応しているか判定する。  
2. 対応していれば Memory64 版の `.wasm` をロードし、未対応なら 32 ビット版をロード。  
3. それぞれバリデーション → インスタンス化を行い、実行可能となる。

---

## Feature Detection（フィーチャーディテクション）のサンプルコード

### 事前チェックと条件分岐の例

ブラウザが **Memory64** や **SIMD 拡張**（Relaxed SIMD など）に対応しているかを **事前にチェック** し、未対応の場合は別のモジュールを読み込む方法が一般的です。

```js
// Memory64 を含む簡単なモジュールをバイト列として用意
// （実際には適切な wasm-binary を生成して埋め込むことが多い）
const memory64WasmBytes = new Uint8Array([
  /* ここに Memory64 (または Relaxed SIMD 等) を使う wasm バイナリを埋め込む */
]);

async function supportsMemory64() {
  try {
    // WebAssembly.validate() は、モジュールのバリデーションが通るかどうかをチェック
    return WebAssembly.validate(memory64WasmBytes);
  } catch {
    // エラーが発生した場合は非対応と判断
    return false;
  }
}

async function loadWasmModule() {
  if (await supportsMemory64()) {
    console.log("64-bit Memory supported. Loading memory64 module...");
    const response = await fetch("module_memory64.wasm");
    const bytes = await response.arrayBuffer();
    const { instance } = await WebAssembly.instantiate(bytes);
    // インスタンス利用
  } else {
    console.log("64-bit Memory not supported. Loading 32-bit module...");
    const response = await fetch("module_32.wasm");
    const bytes = await response.arrayBuffer();
    const { instance } = await WebAssembly.instantiate(bytes);
    // インスタンス利用
  }
}

loadWasmModule();
```

**SIMD の拡張命令** (Relaxed SIMD 等) も、`supportsRelaxedSIMD()` のような関数を用意して同様に検出し、モジュールを切り替えられます。

---

## フォールバックのための別モジュールの用意

もう一つの一般的な手法として、**あらかじめ複数の WebAssembly モジュールをビルドしておく** 方法があります。  
たとえば以下のように準備しておくと、環境に応じて最適なものをロードできます。

- **64 ビット版モジュール**（**Memory64** を使用）  
- **32 ビット版モジュール**（従来の **Memory32** のみ）  
- **SIMD + Relaxed SIMD 対応版**  
- **SIMD なし／Relaxed SIMD なし版**  

新しいブラウザが十分に機能をサポートしていれば「高機能・高性能」版のモジュールをロードし、古い環境では「従来機能」版にフォールバックするといった構成が可能です。

### より正確な切り分け方法：No SIMD / Base SIMD / Relaxed SIMD

ただし、**Relaxed SIMD** は “既存の SIMD 命令を拡張した上位セット” として扱われることが多いため、  
- **SIMD に対応していても Relaxed SIMD はサポートされない** ブラウザ  
- **Relaxed SIMD にも対応している** ブラウザ  
- **そもそも SIMD をサポートしていない** ブラウザ  

といった段階的な状況が生じる可能性もゼロではありません。そこで、**フォールバックをより正確に行う** ためには、次の 3 種類にモジュールを分ける方が望ましい場合があります。

1. **No SIMD**（SIMD 拡張を一切使わない版）  
2. **Base SIMD**（従来の SIMD 命令のみ利用）  
3. **Relaxed SIMD**（Base SIMD + Relaxed SIMD 命令を利用）

こうすることで、  
- Relaxed SIMD は非対応だが通常 SIMD は使える環境  
- SIMD は非対応だが Memory64 は使える環境  
- そもそも拡張がどこまで使えるか分からない混在環境

など、さまざまなバリエーションに対して正しくモジュールを切り替えることが可能です。実際のビルドの手間やユーザへの負荷は増えますが、**「ブラウザがどの段階の機能をサポートしているか」** に合わせて最適化されたモジュールをロードしやすくなる利点があります。

---

## 参考比較表

拡張機能の有無による挙動の違いを以下のようにまとめました。

| 機能               | 従来 (未拡張)                  | 拡張 (例: Memory64 / SIMD Relaxed SIMD)                     | 状況                                       | フォールバックの必要性               |
|--------------------|--------------------------------|-------------------------------------------------------------|--------------------------------------------|---------------------------------------|
| Memory Model       | 32ビット (最大4GB)             | 64ビット (4GB超利用可能)                                   | 実験的サポート段階 (一部ブラウザのみ)      | 対応していない場合は32ビット版に切替   |
| SIMD (Relaxed SIMD)| SIMDなし、または一部SIMDのみ   | Relaxed SIMD命令など新しいSIMD命令                         | 一部で実験的サポート。ブラウザ設定が必要。 | 通常SIMD版・非SIMD版での切り替え      |
| バリデーション結果 | サポート外命令がなければ成功   | サポート外命令が含まれていると失敗（エラー）               | 自動で下位互換にはならない (エミュレーションなし) | Feature Detection や複数モジュール |

- 「拡張機能を使ったモジュール」は、未サポートブラウザ上で **バリデーション失敗** となります。  
- 各ブラウザの状況に応じて「従来版」をロードするか、「拡張版」をロードするかを切り替える **Feature Detection** が重要です。

---

## まとめ

1. **WebAssembly はモジュールインスタンス化前のバリデーション** で「未サポートの命令や拡張機能」が含まれたモジュールをエラーとして弾く。  
2. **64 ビットメモリ（Memory64）や SIMD (Relaxed SIMD 等)** の拡張命令も、**自動で 32 ビットやソフトウェアエミュレーションに切り替える仕組みはない**。  
3. 未対応ブラウザでエラーを回避するには、**Feature Detection** や **別モジュールへのフォールバック** が必要。  
4. **ブラウザの対応状況** は絶えず変化しているため、実験段階（フラグやオリジントライアル）から正式仕様化へ進むケースも含め、**最新情報を常にチェック** することが重要。  
5. **WebAssembly 2.0 ドラフト** では依然として 32 ビットインデックスが基本だが、**Memory64** 拡張 はフェーズ 4 に到達 しており、将来的に標準への統合が期待される。  
6. 実験段階でも正式仕様化後でも、**フォールバック戦略の重要性は変わらない**。  
   - 実験中はブラウザ設定が必要で、仕様変更リスクが高い。  
   - 正式化後はデフォルト有効ブラウザが増えるものの、古いバージョンや他エンジンでの対応は別問題。

Wasm の新機能はとても活発に追加・提案され続けており、ブラウザ側の実装も日々進んでいます。しかし、その進捗がすべてのブラウザで同時に揃うわけではありません。  
**大きなメモリを必要とするアプリケーション**（科学技術計算、巨大ゲームなど）や、**高い演算性能が必要なアプリケーション**（機械学習、物理シミュレーションなど）では、これら拡張機能による性能向上が期待できます。一方で、まだ未サポートの環境も多いため、**フォールバックの設計や実装が不可欠** です。

本記事では、**Memory64** や **SIMD (Relaxed SIMD)** などブラウザごとに対応状況が異なる機能について、**バリデーションからフォールバックまでの実践的な対処方法** を解説しました。  
（2025 年 2 月現在の情報であり、ブラウザや仕様のアップデートにより将来は挙動が変わる可能性があります。**常に公式仕様や各ブラウザのリリースノート** で最新動向を確認していただければ幸いです。）

---

## よくある質問（FAQ）

### Q1. Memory64 を試す際、フラグを有効化する以外の方法はありますか？
A1. ほとんどのブラウザでデフォルト無効化されているため、**オリジントライアル** などを利用するか、**開発版ブラウザ**（Chrome Canary など）でフラグをオンにするのが一般的です。将来的に正式対応されれば、フラグなしで利用できるようになるかもしれません。

### Q2. Feature Detection に `WebAssembly.validate()` を使わず、「とりあえずインスタンス化してみてエラーを捕捉」するのはアリですか？
A2. それでも構いませんが、失敗時の原因が不明瞭になる場合があります。たとえば「サポート非対応で落ちたのか、それ以外の要因か」を区別しにくいため、`WebAssembly.validate()` を用いた事前チェックが一般には推奨されます。

### Q3. SIMD の Relaxed SIMD を含む拡張命令が非対応な場合、通常の SIMD 命令も同様に使えないのですか？
A3. 「SIMD の一部機能はサポートしているが、Relaxed SIMD はまだ」というケースもあり得ます。**Relaxed SIMD 専用命令が含まれるモジュール** はバリデーションで落ちますが、Relaxed SIMD を使わない（通常の SIMD 命令のみ使用する）モジュールなら通る可能性があります。結局は「モジュールが利用している拡張命令をブラウザがサポートしているか」で決まります。

### Q4. フォールバックを導入しておけば、いつか全ブラウザが対応した際に不要になりますか？
A4. 理論的には、ほぼすべてのブラウザが最新拡張をデフォルト有効にする段階になればフォールバックは不要になるかもしれません。ただし、レガシー環境をサポートする企業やユーザが一定数いる限り、フォールバックを継続するケースも多いです。

### Q5. フォールバックによる性能低下は大きいのですか？
A5. フォールバックは、主に「32 ビット版モジュールを使う」「SIMD を使わないモジュールを使う」といった手段を指すため、高機能版や高速化拡張を使う場合に比べると性能が低下することはあります。ただし「まったく動かない」よりは「動くほうが良い」場合が多いため、フォールバックは現実的な選択肢として用いられます。

### Q6. Memory64 を対応していないブラウザで使おうとすると、仮に4GB以下のメモリしかアクセスしないケースでも動作しませんか？
A6. 残念ながら動作しません。バリデーション段階で「Memory64 機能を使っている」だけで弾かれるため、実行時に 4GB 以下しか使わないとしてもロード時点でエラーになります。自動ダウングレードは行われない仕様です。

### Q7. まだ正式仕様が固まっていない機能を導入していいのでしょうか？
A7. 本番サービスで利用する場合は、仕様変更や対応状況の変化を追いかけ続ける必要があります。小規模な実験や一部ユーザを対象としたベータテストであれば、最新機能を先行して試すメリットは大きいでしょう。

### Q8. SIMD をサポートしているブラウザでも、PC の CPU が SSE/AVX 命令をサポートしていない場合はどうなりますか？
A8. **WebAssembly における SIMD 対応かどうかは「ブラウザ（ランタイム）が SIMD 命令をサポートしているか」が最も重要な基準** になります。CPU が実際に SSE/AVX を持っているかは、Wasm開発者からは直接チェックできません。  
ブラウザによっては「CPU に対応するハードウェア命令があればそれを使う」かもしれませんし、古い CPU ではソフトウェアエミュレーションに切り替える場合もあります。逆に、CPU が SSE/AVX を備えていてもブラウザ側の実装が対応していなければ、Wasm SIMD はバリデーションでエラーとなる可能性があります。  
したがって、開発者が Feature Detection やバリデーション結果を判断する際には、**「ブラウザ（ランタイム）が ‘SIMD をサポート’ と認識しているかどうか」** に注目すれば十分です。CPU のハードウェア命令セットは実装ごとに抽象化されており、直接気にする必要はありません。

### Q9. 32 ビット CPU でもブラウザが Memory64 をサポートしていると報告することはありますか？
A9. 仕様上は、ブラウザが **「Memory64 をソフトウェアエミュレーションで実装する」** と宣言すれば、CPU が物理的に 32 ビットでもバリデーションを通すことは可能です。しかし性能コストが大きく、4GB 超のアドレス空間を扱う意義も薄いため、実際には **32 ビット CPU + OS** で Memory64 をサポートするブラウザ実装は存在しないと考えられます。  
通常は、**32 ビット環境ならブラウザが Memory64 を「サポートしない」と報告** する方が合理的です。結果として、バリデーションでエラーとなり、Memory64 を使うモジュールは実行できません。


---

## 用語集

- **バリデーション (Validation)**  
  Wasm のモジュールをインスタンス化する前に、使用している命令やセクションなどが現在のブラウザ（またはランタイム）でサポートされているかどうかを検証する仕組み。サポート外の機能が含まれる場合はエラーとなり、インスタンス化できない。

- **フォールバック (Fallback)**  
  ある機能（例: 64 ビットメモリ、SIMD 拡張）が未対応の環境でも動作させるために、代替の処理や異なるモジュールに切り替える対応策。性能面や機能面で一部制限があることが多いが、「エラーで何も動かない」状態を回避できる。

- **Memory64**  
  従来の 32 ビットメモリモデル（最大 4GB）の制限を超える、64 ビットアドレス空間を使用可能にする拡張。メモリ使用量が非常に大きいアプリケーションでも Wasm を使いやすくなる。

- **Feature Detection (フィーチャーディテクション)**  
  ブラウザやランタイムが特定の機能をサポートしているかどうかを事前に検出し、サポート結果によって異なるモジュールやコードパスを使用する手法。

- **オリジントライアル (Origin Trial)**  
  Chrome などの一部ブラウザが提供する仕組みで、特定の実験的機能をサイト単位で有効化できる。開発者がトークンを取得・適用することで正式リリース前の機能を試すことが可能。

- **フラグ (Flags)**  
  `chrome://flags` や `about:flags` などで設定できる開発者向けパラメータ。メモリ拡張や SIMD 拡張など、実験的機能をオンにする手段としてよく使われる。

- **SIMD (Single Instruction, Multiple Data)**  
  単一の命令で複数のデータを同時処理する命令セット。従来のネイティブ環境では SSE/AVX などで広く使われている。Wasm でも SIMD 拡張が進められているが、ブラウザによって対応状況が異なる。

- **Relaxed SIMD**  
  既存の SIMD に加えて性能や実装の柔軟性を高めるために追加された拡張命令群。対応していないブラウザやランタイムではバリデーションでエラーになり、自動的に通常 SIMD 命令に落ちるわけではない。

---

以上