Files
hakorune/docs/reference/architecture/mir-to-vm-mapping.md

219 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)` → falsenullish 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 / HandleBoxRef 取り扱い(重要)
目的: プラグイン呼び出しBoxCall→PluginLoaderV2およびVM内の戻り値で、ResultとBoxRefHandleを正しく扱うための合意事項。
- HTTP系Netプラグインの約束
- unreachable接続不可/タイムアウト等): `Result.Err(ErrorBox)` にマップする。
- HTTP 404/500 等のステータス異常: `Result.Ok(Response)` として返す(アプリ層で扱う)。
- FileBox等のVoid戻り
- `close()` のような副作用のみのメソッドは `Ok(Void)` を返す。VMではVoidの実体は持たない。
- HandleBoxRef戻り値
- プラグインは TLV tag=8Handle`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を返す実装済み
- TypeOpTypeCheck/Castと命令の整合: Verifierで型注釈に基づく不一致を検出。
## VM統計計測
- `--vm-stats` / `--vm-stats-json` で命令ごとの使用回数と時間(ms)を出力。
- ホット命令抽出によりダイエット候補を定量化。
---
## 実測結果サマリー(初回プローブ)
出所: `local_tests/vm_stats_hello.json`, `local_tests/vm_stats_loop.json`, `simple_math.nyash`
- ループ系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_math18命令: BinOpの使用を確認整数加減乗除
補足:
- Safepoint はMIR上で挿入されるが、VMではNo-op計測には現れる
- NewBox/BoxCall が上位に入るため、命令セットから外すのは不可(コア扱い)。
- Compare/Branch/Jump/Phi は制御フローのコア。26命令の中核として維持が妥当。
## 実測統計2025-08-23
出所: vm-stats正常系HTTP異常系HTTPFileBox
- 正常系HTTP40命令
- BoxCall: 1742.5%/ Const: 1230%/ NewBox: 922.5%/ Return: 1 / Safepoint: 1
- 異常系HTTP21命令 = 正常の52.5%
- BoxCall: 733.3%/ Const: 628.6%/ NewBox: 314.3%
- Branch: 1 / Jump: 1 / Phi: 1エラーハンドリング特有/ Return: 1 / Safepoint: 1
- FileBox44命令
- BoxCall: 1738.6%/ Const: 1329.5%/ NewBox: 1227.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 / RefSetWeak/Barrierは拡張枠へ
- 非同期: AwaitFutureNew/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比較で対応新規
## TypeOpPoC
- 目的: TypeCheck/Castの統合。
- Check: 最小意味論を実装Integer/Float/Bool/String/Void/Box名に対し一致判定
- Cast: 当面コピー等価(将来の変換方針に備える)。
- me 参照
- メソッド/コンストラクタlowering時は `%0` にマップ(パラメータ)。
- それ以外の文脈ではフォールバックとして `Const "__me__"` を一度だけ発行して変数マップに保持し、以降の `me` は同一ValueIdを参照RefGet/RefSetの整合性を保証