Files
hakorune/docs/reference/architecture/mir-to-vm-mapping.md
Moe Charm 2949648e71 fix: MIR builder me resolution for static box methods
- Fixed me ValueId inconsistency in static box methods
- Previously, each me reference generated a new const __me__ ValueId
- Now caches the first me ValueId in variable_map for reuse
- This ensures RefSet and RefGet operate on the same object
- ArrayBox get/set/push now working correctly in VM mode
- Test results: 1, 42, 3 (as expected)

🔧 Technical Details:
- build_me_expression() now stores fallback ValueId in variable_map
- Subsequent me references reuse the same ValueId
- VM BoxCall debug logs confirm ArrayBox methods dispatch correctly

Co-Authored-By: ChatGPT5
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 21:13:02 +09:00

12 KiB
Raw Blame History

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: Implementedto_string()して標準出力)
  • Nop: No-op

例外/安全ポイント

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

参照/弱参照/バリア

  • RefNew / RefGet / RefSet: Partial
    • object_fields に簡易格納。object_classbox_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=8Handletype_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.mdplugin-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の整合性を保証