Files
hakorune/docs/reference/architecture/mir-to-vm-mapping.md
Moe Charm ff14d489d3 feat: Enhance VM with dynamic type conversion and comprehensive method support
- Add dynamic bool conversion for BoxRef(BoolBox)→bool and BoxRef(VoidBox)→false
- Implement String concatenation with Bool and BoxRef types via toString()
- Add Void/Bool comparison support (Eq/Ne only) to prevent VM crashes
- Implement comprehensive ArrayBox methods in VM:
  - push/pop/length/get/set/remove
  - contains/indexOf/clear/join/sort/reverse/slice
- Implement comprehensive MapBox methods in VM:
  - set/get/has/delete/keys/values/size/clear
- Add SocketBox timeout methods (acceptTimeout/recvTimeout)
- Update VM documentation with all new operations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 18:37:38 +09:00

201 lines
10 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: TODO一時的に0を返す
- ArraySet: TODO現在はno-op
## デバッグ/出力
- 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`