# MIR → VM Mapping (Draft)

最終更新: 2025-08-23

目的: 生成されたMIR命令がVMでどう実行されるかを1枚で把握し、欠落や暫定実装を洗い出す。26命令ダイエット検討の足場にする。

記法: 状態 = Implemented / Partial / No-op / TODO。

## コア命令
- Const: Implemented
  - 定数を `VMValue` に格納。
- BinOp: Partial
  - Integer: Add/Sub/Mul/Div 実装（Divは0割チェック）。他の算術/論理/ビット演算は TODO。
  - String: `+` 連結のみ対応。その他は TypeError。
- UnaryOp: Partial
  - `Neg`(int), `Not`(bool) のみ。`BitNot` は TODO。
- Compare: Partial
  - Integer/ String の Eq/Ne/Lt/Le/Gt/Ge 対応。Bool は Eq/Ne のみ。
  - Void は値を持たないため、比較は Eq/Ne のみ定義。
    - `Void == Void` は true、`Void != Void` は false
    - `Void == X` は false、`Void != X` は true（順序比較は TypeError）
- Load / Store: Implemented
  - 現状はVM内の値スロット操作（簡易）。
- Copy: Implemented
  - 値コピー＋クラス名/内部参照印の伝播。

## 制御フロー
- Branch / Jump / Return: Implemented
  - Branchの条件は `Bool` を期待。以下の動的変換を許容：
    - `Integer` → 非ゼロは true
    - `BoxRef(BoolBox)` → 中身のbool
    - `BoxRef(VoidBox)` → false（nullish false）
- Phi: Implemented
  - `LoopExecutor` による選択実装（前BB情報を利用）。

## 呼び出し/Box関連
- Call: Implemented
  - 関数名を `Const String` として解決しVM内ディスパッチ。
- BoxCall: Partial
  - InstanceBox: `{Class}.{method}/{argc}` へ降格呼び出し（MIR関数）。
  - PluginBoxV2: cfg(feature="plugins")下でLoader経由invoke（引数: NyashBox配列）。
  - Builtinの簡易ディスパッチ: `StringBox.length/substr/concat`, `IntegerBox.toString/abs` 等の最小対応。
  - birth 特例: user-definedの `birth` はMIR関数へ直呼。
- NewBox: Implemented
  - `runtime.box_registry` から生成。`scope_tracker` に登録。クラス名マップ更新。
- TypeCheck: No-op (常にtrue)
  - TODO: 正式な型チェックに置換。
- Cast: No-op (コピー)
  - TODO: 正式な型変換に置換。

## 配列
- ArrayGet: ArrayBox.get(index) を呼び出し、戻り値を格納（VM対応済み）
- ArraySet: ArrayBox.set(index, value) を呼び出し（VM対応済み）

## デバッグ/出力
- Debug: No-op（性能優先）
- Print: Implemented（`to_string()`して標準出力）
- Nop: No-op

## 例外/安全ポイント
- Throw: Partial
  - 例外値を表示してVMErrorで中断。ハンドラ探索なし。
- Catch: No-op
  - 例外値スロットを `Void` セットのみ。制御遷移の実装は未対応。
- Safepoint: No-op

## 参照/弱参照/バリア
- RefNew / RefGet / RefSet: Partial
  - `object_fields` に簡易格納。`object_class` と `box_declarations` を用いた可視性（public/private）簡易検査あり。
- WeakNew / WeakLoad: No-op相当（通常コピー/取得と同値）
  - TODO: 実際の弱参照生存判定を導入。
- BarrierRead / BarrierWrite: No-op
  - 効果注釈のみ（将来の最適化/並行実行基盤に備えた形）。

## 非同期
- FutureNew / FutureSet / Await: Implemented
  - `boxes::future::FutureBox` を利用し、同期ブロッキングで結果取得。

## 外部呼び出し
- ExternCall: Implemented
  - `runtime::get_global_loader_v2().extern_call(iface, method, args)` にルーティング。Some/Noneで戻り値void扱いも考慮。

---

## Result / Err / Handle（BoxRef） 取り扱い（重要）

目的: プラグイン呼び出し（BoxCall→PluginLoaderV2）およびVM内の戻り値で、ResultとBoxRef（Handle）を正しく扱うための合意事項。

- HTTP系（Netプラグイン）の約束
  - unreachable（接続不可/タイムアウト等）: `Result.Err(ErrorBox)` にマップする。
  - HTTP 404/500 等のステータス異常: `Result.Ok(Response)` として返す（アプリ層で扱う）。
- FileBox等のVoid戻り
  - `close()` のような副作用のみのメソッドは `Ok(Void)` を返す。VMではVoidの実体は持たない。
- Handle（BoxRef）戻り値
  - プラグインは TLV tag=8（Handle）で `type_id:u32, instance_id:u32` を返す。
  - Loader は返り値の `type_id` に対応する正しい `fini_method_id` を設定し、`PluginBoxV2` を生成してVMへ返す。
  - 注意: 生成元のBoxと返り値のBoxの型が異なるケースがあるため、「呼び出し元のfini値を流用しない」。必ず返り値 `type_id` を基に解決する。
- Resultの整合
  - VMは `Result<T, ErrorBox>` をネイティブ表現し、`match` 等で分岐可能。
  - `Ok(Void)` は `match Ok(_)` と等価に扱える（Voidは値を持たない）。

参考: TLV/Loader仕様は `docs/reference/plugin-system/ffi-abi-specification.md` と `plugin-tester.md` を参照。

---

## 既知の課題（抜粋）
- BinOp/UnaryOp/Compare の型拡張（浮動小数・Bool/Box等）。
- ArrayGet/ArraySet の実装。
- TypeCheck/Cast の正規化（型表現と整合）。
- 例外ハンドリング（Throw/Catchの制御フロー接続）。
- WeakRef/Barrier の実体化（必要性評価の上、命令ダイエット候補）。
- PluginBoxV2 のVM側統合強化（引数/戻り値のTLV全型対応、Handle戻り値→BoxRef化）。

Verifier（検証）に関する追加事項（方針）
- use-before-def across merge の強化: merge後にphiが未使用/未定義を選択するパスを禁止。
- if-merge の戻り: retはphiのdstを返す（実装済み）。
- TypeOp（TypeCheck/Cast）と命令の整合: Verifierで型注釈に基づく不一致を検出。

## VM統計（計測）
- `--vm-stats` / `--vm-stats-json` で命令ごとの使用回数と時間(ms)を出力。
- ホット命令抽出によりダイエット候補を定量化。

---

## 実測結果サマリー（初回プローブ）
出所: `local_tests/vm_stats_hello.json`, `local_tests/vm_stats_loop.json`, `simple_math.hako`

- ループ系（139命令 / 0.158ms）トップ:
  - Const: 25, BoxCall: 23, NewBox: 23, BinOp: 11, Branch: 11, Compare: 11, Jump: 11, Phi: 11, Safepoint: 11
  - 所見: ループloweringで Branch/Jump/Phi/Safepoint が並び、Box初期化とBoxCallが多い。
- Hello系（6命令）: 期待どおりの最小構成（Const/Print/Return中心）。
- simple_math（18命令）: BinOpの使用を確認（整数加減乗除）。

補足:
- Safepoint はMIR上で挿入されるが、VMではNo-op（計測には現れる）。
- NewBox/BoxCall が上位に入るため、命令セットから外すのは不可（コア扱い）。
- Compare/Branch/Jump/Phi は制御フローのコア。26命令の中核として維持が妥当。

## 実測統計（2025-08-23）
出所: vm-stats（正常系HTTP／異常系HTTP／FileBox）

- 正常系HTTP（40命令）
  - BoxCall: 17（42.5%）/ Const: 12（30%）/ NewBox: 9（22.5%）/ Return: 1 / Safepoint: 1
- 異常系HTTP（21命令 = 正常の52.5%）
  - BoxCall: 7（33.3%）/ Const: 6（28.6%）/ NewBox: 3（14.3%）
  - Branch: 1 / Jump: 1 / Phi: 1（エラーハンドリング特有）/ Return: 1 / Safepoint: 1
- FileBox（44命令）
  - BoxCall: 17（38.6%）/ Const: 13（29.5%）/ NewBox: 12（27.3%）/ Return: 1 / Safepoint: 1

設計含意:
- BoxCallが常に最頻出（33〜42%）。呼び出しコスト最適化が最優先。
- Const/NewBoxが次点。定数・生成の最適化（定数畳み込み／軽量生成・シェアリング）が効果的。
- 異常系は早期収束で命令半減。if/phi修正は実戦で有効（Branch/Jump/Phiが顕在化）。

補足（HTTP 404/500の比較）
- 404（"Not Found"）、500（"Internal Server Error"）ともに同一プロファイル（合計55命令）
  - 内訳例: BoxCall: 23 / Const: 16 / NewBox: 12 / BinOp: 2 / Return: 1 / Safepoint: 1
- ステータス差はアプリ層で完結し、VM命令の分布には有意差なし（設計の意図どおり）

## 26命令ダイエット（検討のたたき台）
方針: 「命令の意味は保ちつつ集約」。代表案：
- 維持: Const / Copy / Load / Store / BinOp / UnaryOp / Compare / Jump / Branch / Phi / Return / Call / BoxCall / NewBox / ArrayGet / ArraySet
- 参照: RefNew / RefGet / RefSet（Weak/Barrierは拡張枠へ）
- 非同期: Await（FutureNew/SetはBox APIへ寄せる案も可）
- I/O: Print は開発モード限定 or ExternCall統合（ExternCall自体はBoxCallへ統合方針）
- 調整: TypeCheck/Cast はVerifier/型系に寄せる（命令から外す or 1命令に集約）
- Debug/Nop/Safepoint: メタ扱い（命令数からは外す）

次ステップ:
- サンプル/テストをVMで実行し、`vm-stats`結果から実使用命令セットを抽出。
- 上記案に対し互換影響を洗い出し、段階移行（エイリアス→削除）を設計。

---

## 26命令ダイエットの指針（実測反映）
- 維持（ホット・コア）: BoxCall / NewBox / Const / BinOp / Compare / Branch / Jump / Phi / Return / Copy / Load / Store / Call
- 実装方針: ExternCallは原則BoxCallへ集約（必要なら限定的に残す）。
- メタ降格: Debug/Nop/Safepoint（ビルドモードで制御）。
- 型系: TypeCheck/Castは折りたたみ or 検証時に処理（1命令に集約も可）。
- 参照/弱参照/バリア: 需要ベースで拡張枠へ（vm-statsに登場しない限りコア外）。

提案（ドラフト）:
- コア候補（例）: Const, Copy, Load, Store, BinOp, UnaryOp, Compare, Jump, Branch, Phi, Return, Call, BoxCall, NewBox, ArrayGet, ArraySet, RefNew, RefGet, RefSet, Await, Print, ExternCall(集約可), TypeOp(=TypeCheck/Cast), 予備2（将来枠）
- 予備はWeak*/Barrierや将来の非同期拡張等に割当（実測で常用化したら昇格）。

---

## E2E更新（VM経由の実働確認）

成功ケース（VM）:
- FileBox.open/write/read: 引数2個のTLVエンコード（String, String）で成功（HELLO往復）
- FileBox.copyFrom(handle): Handle引数（tag=8, size=8, type_id+instance_id）で成功
- HttpClientBox.get + HttpServerBox: 基本GETの往復（ResultBox経由でResponse取得）
- HttpClientBox.post + headers: Status/ヘッダー/ボディをVMで往復確認
 - HttpClientBox.get unreachable: 接続失敗時はResult.Err(ErrorBox)（ローダーがstring/bytesをErrにマップ）
 - HTTP 404/500: Result.Ok(Response)（ステータスはResponse上に保持）

デバッグ小技:
- `NYASH_DEBUG_PLUGIN=1` で VM→Plugin 呼び出しTLVの ver/argc/先頭バイトをダンプ
- Netプラグインの内部ログ: `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`
## 型・Null/Void・比較の扱い（更新）

- NullはVM内部でVoidに折りたたみ（`Const Null → VMValue::Void`）。
- VoidとNullは同一視されない（等価比較は `Void == Void` のみtrue）。
- Compareの対応：
  - 整数/文字列: Eq/Ne/Lt/Le/Gt/Ge（実装済）
  - 真偽値: Eq/Ne のみ
  - Void: Eq/Ne のみ（Void==Voidはtrue、それ以外はfalse）
  - 浮動小数点: Eq/Ne/Lt/Le/Gt/Ge（新規）
  - 整数と浮動小数点の混在: 双方をf64比較で対応（新規）

## TypeOp（PoC）
- 目的: TypeCheck/Castの統合。
- Check: 最小意味論を実装（Integer/Float/Bool/String/Void/Box名に対し一致判定）。
- Cast: 当面コピー等価（将来の変換方針に備える）。
- me 参照
  - メソッド/コンストラクタlowering時は `%0` にマップ（パラメータ）。
  - それ以外の文脈ではフォールバックとして `Const "__me__"` を一度だけ発行して変数マップに保持し、以降の `me` は同一ValueIdを参照（RefGet/RefSetの整合性を保証）。
