diff --git a/Cargo.toml b/Cargo.toml index affc35f7..5e58ab00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ dynamic-file = [] wasm-backend = ["dep:wasmtime", "dep:wabt"] # プラグイン機構の有効化(ネイティブ環境のみ推奨) plugins = ["dep:libloading"] +# MIR instruction diet PoC flags (scaffolding only; off by default) +mir_typeop_poc = [] +mir_refbarrier_unify_poc = [] # Note: LLVM feature requires inkwell dependency and LLVM development libraries # llvm = ["dep:inkwell"] diff --git a/docs/CURRENT_TASK.md b/docs/CURRENT_TASK.md new file mode 100644 index 00000000..c693f5b8 --- /dev/null +++ b/docs/CURRENT_TASK.md @@ -0,0 +1,6 @@ +# Moved: CURRENT_TASK + +このファイルは移動しました。最新の現在タスクは次を参照してください。 + +- 新しい場所: [development/current/CURRENT_TASK.md](development/current/CURRENT_TASK.md) + diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index f72a1e69..e9db10d5 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -7,29 +7,37 @@ - Net: GET/POST(VM)、404/500(Ok(Response))、unreachable(Err(ErrorBox)) 3. VM命令カウンタ+時間計測のCLI化(`--vm-stats`, `--vm-stats-json`)とJSON出力対応 - サンプル/スクリプト整備(tools/run_vm_stats.sh、local_tests/vm_stats_*.nyash) -4. MIR if-merge 修正(retがphi dstを返す)+ Verifier強化(mergeでのphi未使用検知) -5. ドキュメント追加・更新 - - Dynamic Plugin Flow(MIR→VM→Registry→Loader→Plugin) - - Netプラグインのエラーモデル(unreachable=Err, 404/500=Ok) - - E2Eテスト一覧整備 -6. CI: plugins E2E ジョブ(Linux)を追加 +4. MIR if-merge 修正(retがphi dstを返す)+ Verifier強化(mergeでのphi未使用検知、支配関係チェック導入) +5. VMの健全化(分岐・比較・Result) + - Compare: Void/BoolのEq/Ne定義(順序比較はTypeError) + - Branch条件: `BoxRef(BoolBox)→bool`/`BoxRef(VoidBox)→false`/`Integer≠0→true` + - ResultBox: 新旧両実装への動的ディスパッチ統一(isOk/getValue/getError) +6. VMビルトイン強化(Array/Map/Socket) + - ArrayBox/MapBox: 代表メソッドをVM統合ディスパッチで実装(push/get/set/size等) + - SocketBox: `acceptTimeout(ms)`(void)/ `recvTimeout(ms)`(空文字)を追加 + - E2E追加: `socket_timeout_server.nyash` / `socket_timeout_client.nyash` +7. ドキュメント追加・更新 + - MIR→VMマッピング(分岐条件の動的変換、Void/Bool比較) + - VM README(SocketBoxタイムアウト/E2E導線・HTTP Result整理) + - 26命令ダイエット: PoCフラグと進捗追記(TypeOp/WeakRef/Barrier) +8. CI: plugins E2E ジョブ(Linux)を追加 ## 🚧 次にやること(再開方針) -1) MIR→VMの健全化(短期・最優先) -- マッピング表更新(Err経路・Handle戻り・Result整合を実測で反映) -- Verifierルールの拡充(use-before-def across merge を強化) -- 成果物: `docs/reference/architecture/mir-to-vm-mapping.md`(更新済・追補) +1) 命令セットダイエットのPoC実装(短期) +- フラグ `mir_typeop_poc` 有効時、Builderで TypeCheck/Cast → TypeOp を出力 +- VMにTypeOp実行経路を追加(当面は既存と同義) +- 次段: `mir_refbarrier_unify_poc` で Weak*/Barrier 統合(Builder/VM) +- 成果物: スナップショット(flag on/off)+ vm-statsで集計キー確認 -2) VM×プラグインシステムのE2E検証(短期) -- FileBox/Netを中心にケース拡張(大きいボディ、ヘッダー多数、タイムアウト等) -- 成果物: E2E追補+`VM_README.md` に既知の制約とTipsを追記 +2) VM×プラグインのE2E拡張(短期) +- HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証 +- Socket: タイムアウト系の追加ケース(連続acceptTimeout/recvTimeout) +- 成果物: E2E追加と `VM_README.md` のTips追補 -3) 命令セットのダイエット(中期:目標26命令) -- 実測(HTTP OK/404/500/unreachable、FileBox)を反映して合意版を確定 -- 統合方針(TypeOp/WeakRef/Barrierの統合、ExternCall最小化) -- 段階移行(ビルドモードでメタ降格、互換エイリアス→削除)と回帰テスト整備 -- 成果物: 26命令案(合意版)+移行計画 +3) ResultBox単一路線への統合(中期) +- 新`NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行 +- 成果物: 実装整理・移行メモ・影響調査 ## ▶ 実行コマンド例 @@ -58,7 +66,7 @@ nyash --verify examples/plugin_box_sample.nyash - メタ降格: Debug / Nop / Safepoint(ビルドモードで制御) --- -最終更新: 2025年8月23日(VM×Plugins安定・MIR修正・26命令合意ドラフト/再起動チェックポイント) +最終更新: 2025年8月23日(VM強化・E2E拡張・TypeOp PoC着手/次段はBuilder/VMマッピング) ## 🔁 再起動後の再開手順(ショート) ```bash @@ -71,4 +79,8 @@ cargo test --features plugins -q -- --nocapture # 3) VM Stats 代表値の再取得(任意) tools/run_vm_stats.sh local_tests/vm_stats_http_ok.nyash vm_stats_ok.json tools/run_vm_stats.sh local_tests/vm_stats_http_err.nyash vm_stats_err.json + +# 4) SocketBox タイムアウト確認(任意) +./target/release/nyash local_tests/socket_timeout_server.nyash +./target/release/nyash local_tests/socket_timeout_client.nyash ``` diff --git a/docs/development/proposals/mir-typeop-weakref-barrier-poc.md b/docs/development/proposals/mir-typeop-weakref-barrier-poc.md new file mode 100644 index 00000000..7785a52a --- /dev/null +++ b/docs/development/proposals/mir-typeop-weakref-barrier-poc.md @@ -0,0 +1,81 @@ +# PoC Plan: TypeOp / WeakRef / Barrier Unification + +Status: Draft (PoC design) +Last Updated: 2025-08-23 + +## Goals +- Reduce instruction surface without losing expressiveness or performance. +- Provide a feature-gated PoC to validate that consolidation is safe and measurable. + +## Scope +- Unify TypeCheck + Cast → TypeOp (single instruction) +- Unify WeakNew + WeakLoad → WeakRef (single instruction) +- Unify BarrierRead + BarrierWrite → Barrier (single instruction) + +## Out of Scope (PoC) +- Remap language syntax or external APIs +- Remove legacy instructions permanently (kept behind feature flags) + +## Feature Flags (Cargo) +- `mir_typeop_poc`: enable TypeOp and map TypeCheck/Cast to it during build +- `mir_refbarrier_unify_poc`: enable WeakRef/Barrier unified ops and map legacy calls + +## Mapping (Current → PoC) +- TypeCheck { value, expected_type } → TypeOp { op: Check, value, type } (bool) +- Cast { value, target_type } → TypeOp { op: Cast, value, type } (value) +- WeakNew { dst, box_val } → WeakRef { op: New, dst, box_val } +- WeakLoad { dst, weak_ref } → WeakRef { op: Load, dst, weak_ref } +- BarrierRead { ptr } → Barrier { op: Read, ptr } +- BarrierWrite { ptr } → Barrier { op: Write, ptr } + +## Implementation Steps +1) MIR instruction additions + - Add TypeOp/WeakRef/Barrier enums with minimal payloads + - Keep legacy instructions compiled-in (no behavior change yet) +2) Builder mapping (feature-gated) + - Under flags, emit unified instructions instead of legacy +3) VM execution mapping + - Implement execute paths for TypeOp/WeakRef/Barrier + - Legacy paths continue to work for fallback +4) Printer/Stats + - Name new ops distinctly; ensure stats collection reflects consolidated ops +5) Tests + - Snapshot tests for builder mapping (with/without flags) + - VM exec parity tests for legacy vs unified + +## Rollout / Migration +- Phase A (PoC): flags off by default, CI job with flags on +- Phase B (Dual): flags on by default in dev; legacy paths still supported +- Phase C (Switch): remove legacy or keep as aliases (no-emit) depending on impact + +## Impact Areas +- `src/mir/instruction.rs` (add new ops; Display/used_values/dst_value) +- `src/mir/builder.rs` (conditional emit) +- `src/backend/vm.rs` (execution paths + stats key) +- `src/mir/printer.rs` (print new ops) +- Tests: MIR/VM/E2E minimal parity checks + +## Acceptance Criteria +- All current tests pass with flags off (default) +- With flags on: + - Unit/snapshot tests pass + - vm-stats shows expected consolidation (TypeOp/WeakRef/Barrier vs legacy) + - No regressions in FileBox/Net E2E under plugins + +## Metrics to Watch +- vm-stats: proportion of TypeOp/WeakRef/Barrier vs legacy in representative scenarios +- Build time impact: negligible +- Code size: small reduction after removal + +## Risks / Mitigations +- Risk: Unified ops obscure dataflow for some analyses + - Mitigation: Verifier hooks to introspect TypeOp semantics; keep legacy printer names during PoC +- Risk: Plugins or external tooling tied to legacy names + - Mitigation: MIR remains internal; external ABI unaffected + +## Next Steps +- Land scaffolding (no behavior change) +- Add builder mapping behind flags +- Add VM execution behind flags +- Gate CI job to run PoC flags on Linux + diff --git a/docs/development/roadmap/README.md b/docs/development/roadmap/README.md index 6619cd7b..3d6baf4d 100644 --- a/docs/development/roadmap/README.md +++ b/docs/development/roadmap/README.md @@ -3,7 +3,7 @@ ## 📋 現在進行中 ### 🎯 最重要タスク -- **現在のタスク**: [docs/CURRENT_TASK.md](../CURRENT_TASK.md) +- **現在のタスク**: [development/current/CURRENT_TASK.md](../current/CURRENT_TASK.md) - **Phase 8.3**: Box操作WASM実装(Copilot担当) - **Phase 8.4**: ネイティブコンパイル実装計画(AI大会議策定済み) @@ -49,7 +49,7 @@ ## 📚 関連ドキュメント ### 📖 技術資料 -- **[実行バックエンドガイド](../execution-backends.md)** - 3バックエンド使い分け +- **[実行バックエンドガイド](../../reference/architecture/execution-backends.md)** - 3バックエンド使い分け - **[コアコンセプト](../nyash_core_concepts.md)** - Everything is Box哲学 ### 🔄 進捗管理 diff --git a/docs/development/roadmap/native-plan/README.md b/docs/development/roadmap/native-plan/README.md index a44a8b0a..9c2cdee9 100644 --- a/docs/development/roadmap/native-plan/README.md +++ b/docs/development/roadmap/native-plan/README.md @@ -2,11 +2,11 @@ ## 🎯 目的 開発者向けに「ビルド計画・段階的タスク・設計上の要点」を集約。 -利用者向けの具体的なビルド手順は docs/説明書/native-build/README.md を参照。 +利用者向けの具体的なビルド手順は guides/ 以下の各ガイドを参照。 ## 📋 重要リンク -- **現在のタスク**: [docs/CURRENT_TASK.md](../../CURRENT_TASK.md) -- **コア概念(速習)**: [docs/nyash_core_concepts.md](../../nyash_core_concepts.md) +- **現在のタスク**: [development/current/CURRENT_TASK.md](../../current/CURRENT_TASK.md) +- **コア概念(速習)**: [reference/architecture/nyash_core_concepts.md](../../reference/architecture/nyash_core_concepts.md) - **🤖 AI大会議記録**: [../ai_conference_native_compilation_20250814.md](../ai_conference_native_compilation_20250814.md) - **🗺️ ネイティブコンパイル戦略**: [../native-compilation-roadmap.md](../native-compilation-roadmap.md) - **フェーズ課題一覧**: [issues/](issues/) diff --git a/docs/examples/http_result_patterns.md b/docs/examples/http_result_patterns.md new file mode 100644 index 00000000..1c73055c --- /dev/null +++ b/docs/examples/http_result_patterns.md @@ -0,0 +1,32 @@ +# HTTP Result Patterns (VM×Plugins) + +目的: Netプラグイン(HTTP)の戻り値モデルをVM視点で明確化します。E2Eの実行方法と、典型ケースでのResultの形をまとめます。 + +## 実行方法(代表) +```bash +tools/run_vm_stats.sh local_tests/vm_stats_http_ok.nyash vm_stats_ok.json +tools/run_vm_stats.sh local_tests/vm_stats_http_err.nyash vm_stats_err.json +tools/run_vm_stats.sh local_tests/vm_stats_http_404.nyash vm_stats_404.json +tools/run_vm_stats.sh local_tests/vm_stats_http_500.nyash vm_stats_500.json +``` + +## 戻り値モデル +- unreachable(接続不可/タイムアウト等): `Result.Err(ErrorBox)` + - ErrorBoxには原因メッセージ(例: "connection refused", "timeout")が入ります。 +- HTTPステータス 404/500 等: `Result.Ok(Response)` + - `response.status` が 404/500 を保持し、ボディやヘッダーは `response.body`, `response.headers` に格納されます。 + +## 使い分けの意図 +- ネットワーク層の到達不能(transport)は「例外的」な失敗としてErr。 +- アプリ層のHTTPステータスは「通常の結果」としてOkに包む(分岐の簡潔化・情報保持)。 + +## Tips +- デバッグ + - `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log` でNetプラグインの内部ログを記録。 + - `NYASH_DEBUG_PLUGIN=1` で VM→Plugin のTLV先頭情報をダンプ。 +- 計測 + - `--vm-stats`/`--vm-stats-json` で命令プロファイルを取得し、BoxCallやNewBoxのホット度を把握。 + +関連ドキュメント +- `docs/reference/architecture/mir-to-vm-mapping.md`(Result/Handleの取り扱い) +- `docs/VM_README.md`(VM統計・既知の制約) diff --git a/docs/execution-backends.md b/docs/execution-backends.md new file mode 100644 index 00000000..2a048b82 --- /dev/null +++ b/docs/execution-backends.md @@ -0,0 +1,6 @@ +# Moved: 実行バックエンド完全ガイド + +このドキュメントは構成再編により移動しました。最新の内容はこちら: + +- 新しい場所: [reference/architecture/execution-backends.md](reference/architecture/execution-backends.md) + diff --git a/docs/reference/architecture/dynamic-plugin-flow.md b/docs/reference/architecture/dynamic-plugin-flow.md index 262e9470..451805b6 100644 --- a/docs/reference/architecture/dynamic-plugin-flow.md +++ b/docs/reference/architecture/dynamic-plugin-flow.md @@ -113,3 +113,6 @@ Nyash Source ──▶ MIR (Builder) - `docs/reference/architecture/mir-to-vm-mapping.md` - `docs/reference/architecture/mir-26-instruction-diet.md` +See also +- `docs/examples/http_result_patterns.md` - HTTPのResult挙動(unreachable/404/500)のE2E例 +- `docs/VM_README.md` - VM統計とプラグイン周りの既知制約 diff --git a/docs/reference/architecture/mir-26-instruction-diet.md b/docs/reference/architecture/mir-26-instruction-diet.md index 3f472e6c..60712c20 100644 --- a/docs/reference/architecture/mir-26-instruction-diet.md +++ b/docs/reference/architecture/mir-26-instruction-diet.md @@ -84,3 +84,13 @@ Appendix: Rationale - Smaller ISA simplifies VM fast paths and aids JIT/AOT later. - Data shows hot paths concentrated on calls, const, alloc; control sparse and localized. - Folding type ops reduces interpreter/VM dispatch; verification handles safety. +--- + +Feature Flags (Cargo) +- `mir_typeop_poc`: enable TypeOp PoC mapping (builder emits TypeOp instead of TypeCheck/Cast) +- `mir_refbarrier_unify_poc`: enable WeakRef/Barrier PoC mapping (builder emits unified ops) + +Status (2025-08-23) +- Flags declared in `Cargo.toml` (off by default) +- PoC design doc: `docs/development/proposals/mir-typeop-weakref-barrier-poc.md` +- Next: land builder/VM mapping behind flags, add snapshot tests; no behavior change with flags off diff --git a/docs/reference/architecture/nyash_core_concepts.md b/docs/reference/architecture/nyash_core_concepts.md index d3b41ea5..31f37019 100644 --- a/docs/reference/architecture/nyash_core_concepts.md +++ b/docs/reference/architecture/nyash_core_concepts.md @@ -85,7 +85,7 @@ read = { args = [] } # 引数なし ./tools/plugin-tester/target/release/plugin-tester io plugin.so ``` -**詳細**: [BID-FFI仕様書](説明書/reference/box-design/ffi-abi-specification.md) +**詳細**: [BID-FFI仕様書](../plugin-system/ffi-abi-specification.md) ## ⚡ **実行バックエンド選択** - 開発から本番まで @@ -650,5 +650,4 @@ cargo build --release -j32 - **⚡ 性能**: VM 20倍、WASM 13倍、将来AOT 1000倍高速化 - **📚 使いやすさ**: 自動変換、標準ライブラリ、直感的構文 -**詳しいdocs**: [完全リファレンス](説明書/reference/) | [開発ガイド](../CLAUDE.md) | [Phase計画](予定/native-plan/copilot_issues.txt) - +**詳しいdocs**: [完全リファレンス](../) | [開発ガイド](../../../CLAUDE.md) | [Phase計画](../../development/roadmap/native-plan/copilot_issues.txt) diff --git a/docs/予定/native-plan/copilot_issues.txt b/docs/予定/native-plan/copilot_issues.txt new file mode 100644 index 00000000..987a993c --- /dev/null +++ b/docs/予定/native-plan/copilot_issues.txt @@ -0,0 +1 @@ +# Moved: copilot_issues\n\nこのファイルは移動しました。最新は下記を参照してください。\n\n- 新しい場所: ../../development/roadmap/native-plan/copilot_issues.txt\n diff --git a/mir_dump_fixed.txt b/mir_dump_fixed.txt new file mode 100644 index 00000000..1be21210 --- /dev/null +++ b/mir_dump_fixed.txt @@ -0,0 +1,42 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash MIR Compiler - Processing file: local_tests/test_http_error_simple.nyash 🚀 +🚀 MIR Output for local_tests/test_http_error_simple.nyash: +; MIR Module: main + +define void @main() { +bb0: + 0: safepoint + 1: %0 = const void + 2: %1 = const void + 3: %2 = const void + 4: %3 = const void + 5: %4 = new HttpClientBox() + 6: call %4.birth() + 7: %5 = const "http://127.0.0.1:8099/nope" + 8: %6 = new StringBox(%5) + 9: call %6.birth(%5) + 10: %7 = const "http://127.0.0.1:8099/nope" + 11: %8 = new StringBox(%7) + 12: call %8.birth(%7) + 13: %9 = call %4.get(%8) + 14: %10 = call %9.isOk() + 15: br %10, label bb1, label bb2 + +bb1: + 0: %11 = const "unexpected_ok" + 1: %12 = new StringBox(%11) + 2: call %12.birth(%11) + 3: br label bb3 + +bb2: + 0: %13 = call %9.getError() + 1: %14 = call %13.toString() + 2: br label bb3 + +bb3: + 0: %15 = phi [%12, bb1], [%14, bb2] + 1: ret %15 +} + + diff --git a/mir_dump_http_error.txt b/mir_dump_http_error.txt new file mode 100644 index 00000000..36d94aa7 --- /dev/null +++ b/mir_dump_http_error.txt @@ -0,0 +1,42 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash MIR Compiler - Processing file: local_tests/test_http_error_simple.nyash 🚀 +🚀 MIR Output for local_tests/test_http_error_simple.nyash: +; MIR Module: main + +define void @main() { +bb0: + 0: safepoint + 1: %0 = const void + 2: %1 = const void + 3: %2 = const void + 4: %3 = const void + 5: %4 = new HttpClientBox() + 6: call %4.birth() + 7: %5 = const "http://127.0.0.1:8099/nope" + 8: %6 = new StringBox(%5) + 9: call %6.birth(%5) + 10: %7 = const "http://127.0.0.1:8099/nope" + 11: %8 = new StringBox(%7) + 12: call %8.birth(%7) + 13: %9 = call %4.get(%8) + 14: %10 = call %9.isOk() + 15: br %10, label bb1, label bb2 + +bb1: + 0: %11 = const "unexpected_ok" + 1: %12 = new StringBox(%11) + 2: call %12.birth(%11) + 3: br label bb3 + +bb2: + 0: %13 = call %9.getError() + 1: %14 = call %13.toString() + 2: br label bb3 + +bb3: + 0: %15 = phi [%12, bb1], [%14, bb2] + 1: ret %14 +} + + diff --git a/mir_dump_http_error_check.txt b/mir_dump_http_error_check.txt new file mode 100644 index 00000000..36d94aa7 --- /dev/null +++ b/mir_dump_http_error_check.txt @@ -0,0 +1,42 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash MIR Compiler - Processing file: local_tests/test_http_error_simple.nyash 🚀 +🚀 MIR Output for local_tests/test_http_error_simple.nyash: +; MIR Module: main + +define void @main() { +bb0: + 0: safepoint + 1: %0 = const void + 2: %1 = const void + 3: %2 = const void + 4: %3 = const void + 5: %4 = new HttpClientBox() + 6: call %4.birth() + 7: %5 = const "http://127.0.0.1:8099/nope" + 8: %6 = new StringBox(%5) + 9: call %6.birth(%5) + 10: %7 = const "http://127.0.0.1:8099/nope" + 11: %8 = new StringBox(%7) + 12: call %8.birth(%7) + 13: %9 = call %4.get(%8) + 14: %10 = call %9.isOk() + 15: br %10, label bb1, label bb2 + +bb1: + 0: %11 = const "unexpected_ok" + 1: %12 = new StringBox(%11) + 2: call %12.birth(%11) + 3: br label bb3 + +bb2: + 0: %13 = call %9.getError() + 1: %14 = call %13.toString() + 2: br label bb3 + +bb3: + 0: %15 = phi [%12, bb1], [%14, bb2] + 1: ret %14 +} + + diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 3b754718..fea60ee6 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -456,6 +456,22 @@ impl VM { println!("{}", val.to_string()); Ok(ControlFlow::Continue) }, + + MirInstruction::TypeOp { dst, op, value, ty: _ } => { + // PoC: mirror current semantics + match op { + crate::mir::TypeOpKind::Check => { + // Current TypeCheck is a no-op that returns true + self.set_value(*dst, VMValue::Bool(true)); + } + crate::mir::TypeOpKind::Cast => { + // Current Cast is a copy/no-op + let v = self.get_value(*value)?; + self.set_value(*dst, v); + } + } + Ok(ControlFlow::Continue) + }, MirInstruction::Return { value } => { let return_value = if let Some(val_id) = value { @@ -831,6 +847,25 @@ impl VM { Ok(ControlFlow::Continue) }, + // Unified PoC ops mapped to legacy behavior + MirInstruction::WeakRef { dst, op, value } => { + match op { + crate::mir::WeakRefOp::New => { + let v = self.get_value(*value)?; + self.set_value(*dst, v); + } + crate::mir::WeakRefOp::Load => { + let v = self.get_value(*value)?; + self.set_value(*dst, v); + } + } + Ok(ControlFlow::Continue) + }, + MirInstruction::Barrier { .. } => { + // No-op + Ok(ControlFlow::Continue) + }, + MirInstruction::BarrierRead { ptr: _ } => { // Memory barrier read is a no-op for now // In a real implementation, this would ensure memory ordering @@ -1085,6 +1120,7 @@ impl VM { MirInstruction::NewBox { .. } => "NewBox", MirInstruction::TypeCheck { .. } => "TypeCheck", MirInstruction::Cast { .. } => "Cast", + MirInstruction::TypeOp { .. } => "TypeOp", MirInstruction::ArrayGet { .. } => "ArrayGet", MirInstruction::ArraySet { .. } => "ArraySet", MirInstruction::Copy { .. } => "Copy", @@ -1101,6 +1137,8 @@ impl VM { MirInstruction::WeakLoad { .. } => "WeakLoad", MirInstruction::BarrierRead { .. } => "BarrierRead", MirInstruction::BarrierWrite { .. } => "BarrierWrite", + MirInstruction::WeakRef { .. } => "WeakRef", + MirInstruction::Barrier { .. } => "Barrier", MirInstruction::FutureNew { .. } => "FutureNew", MirInstruction::FutureSet { .. } => "FutureSet", MirInstruction::Await { .. } => "Await", diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index c87ee362..6d864130 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -136,6 +136,16 @@ pub enum MirInstruction { value: ValueId, target_type: MirType, }, + + // === Type Operations (Unified PoC) === + /// Unified type operation (PoC): Check or Cast + /// `%dst = typeop(check|cast, %value, Type)` + TypeOp { + dst: ValueId, + op: TypeOpKind, + value: ValueId, + ty: MirType, + }, // === Array Operations === /// Get array element @@ -250,6 +260,22 @@ pub enum MirInstruction { BarrierWrite { ptr: ValueId, }, + + // === Unified PoC: WeakRef/Barrier (flags-only scaffolding) === + /// Unified weak reference op (PoC) + /// `%dst = weakref new %box` or `%dst = weakref load %weak` + WeakRef { + dst: ValueId, + op: WeakRefOp, + value: ValueId, + }, + + /// Unified barrier op (PoC) + /// `barrier read %ptr` or `barrier write %ptr` + Barrier { + op: BarrierOp, + ptr: ValueId, + }, // === Phase 7: Async/Future Operations === @@ -354,6 +380,7 @@ impl MirInstruction { MirInstruction::UnaryOp { .. } | MirInstruction::Compare { .. } | MirInstruction::Cast { .. } | + MirInstruction::TypeOp { .. } | MirInstruction::Copy { .. } | MirInstruction::Phi { .. } | MirInstruction::TypeCheck { .. } | @@ -396,6 +423,15 @@ impl MirInstruction { MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read MirInstruction::BarrierWrite { .. } => EffectMask::WRITE.add(Effect::Barrier), // Memory barrier with write + // PoC unified ops mirror legacy effects + MirInstruction::WeakRef { op, .. } => match op { + WeakRefOp::New => EffectMask::PURE, + WeakRefOp::Load => EffectMask::READ, + }, + MirInstruction::Barrier { op, .. } => match op { + BarrierOp::Read => EffectMask::READ.add(Effect::Barrier), + BarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier), + }, // Phase 7: Async/Future Operations MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate @@ -419,12 +455,14 @@ impl MirInstruction { MirInstruction::NewBox { dst, .. } | MirInstruction::TypeCheck { dst, .. } | MirInstruction::Cast { dst, .. } | + MirInstruction::TypeOp { dst, .. } | MirInstruction::ArrayGet { dst, .. } | MirInstruction::Copy { dst, .. } | MirInstruction::RefNew { dst, .. } | MirInstruction::RefGet { dst, .. } | MirInstruction::WeakNew { dst, .. } | MirInstruction::WeakLoad { dst, .. } | + MirInstruction::WeakRef { dst, .. } | MirInstruction::FutureNew { dst, .. } | MirInstruction::Await { dst, .. } => Some(*dst), @@ -443,6 +481,7 @@ impl MirInstruction { MirInstruction::RefSet { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | + MirInstruction::Barrier { .. } | MirInstruction::FutureSet { .. } | MirInstruction::Safepoint | MirInstruction::Nop => None, @@ -462,6 +501,7 @@ impl MirInstruction { MirInstruction::Load { ptr: operand, .. } | MirInstruction::TypeCheck { value: operand, .. } | MirInstruction::Cast { value: operand, .. } | + MirInstruction::TypeOp { value: operand, .. } | MirInstruction::Copy { src: operand, .. } | MirInstruction::Debug { value: operand, .. } | MirInstruction::Print { value: operand, .. } => vec![*operand], @@ -511,6 +551,8 @@ impl MirInstruction { MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref], MirInstruction::BarrierRead { ptr } => vec![*ptr], MirInstruction::BarrierWrite { ptr } => vec![*ptr], + MirInstruction::WeakRef { value, .. } => vec![*value], + MirInstruction::Barrier { ptr, .. } => vec![*ptr], // Phase 7: Async/Future Operations MirInstruction::FutureNew { value, .. } => vec![*value], @@ -523,6 +565,21 @@ impl MirInstruction { } } +/// Kind of unified type operation (PoC) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TypeOpKind { + Check, + Cast, +} + +/// Kind of unified weak reference operation (PoC) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WeakRefOp { New, Load } + +/// Kind of unified barrier operation (PoC) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BarrierOp { Read, Write } + impl ConstValue { /* /// Convert to NyashValue @@ -790,4 +847,4 @@ mod tests { assert_eq!(void_inst.dst_value(), None); assert_eq!(void_inst.used_values(), vec![arg1]); } -} \ No newline at end of file +} diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 1ce13e05..e01a51ae 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -18,7 +18,7 @@ pub mod value_id; pub mod effect; // Re-export main types for easy access -pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType}; +pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp}; pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator}; pub use function::{MirFunction, MirModule, FunctionSignature}; diff --git a/src/mir/printer.rs b/src/mir/printer.rs index 92816100..6a479554 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -279,6 +279,11 @@ impl MirPrinter { format!("{} = cast {} to {:?}", dst, value, target_type) }, + MirInstruction::TypeOp { dst, op, value, ty } => { + let op_str = match op { super::TypeOpKind::Check => "check", super::TypeOpKind::Cast => "cast" }; + format!("{} = typeop {} {} {:?}", dst, op_str, value, ty) + }, + MirInstruction::ArrayGet { dst, array, index } => { format!("{} = {}[{}]", dst, array, index) }, @@ -349,6 +354,16 @@ impl MirPrinter { format!("barrier_write {}", ptr) }, + MirInstruction::WeakRef { dst, op, value } => { + let op_str = match op { super::WeakRefOp::New => "new", super::WeakRefOp::Load => "load" }; + format!("{} = weakref {} {}", dst, op_str, value) + }, + + MirInstruction::Barrier { op, ptr } => { + let op_str = match op { super::BarrierOp::Read => "read", super::BarrierOp::Write => "write" }; + format!("barrier {} {}", op_str, ptr) + }, + // Phase 7: Async/Future Operations MirInstruction::FutureNew { dst, value } => { format!("{} = future_new {}", dst, value) @@ -439,4 +454,4 @@ mod tests { assert!(output.contains("Module Statistics")); } -} \ No newline at end of file +} diff --git a/src/mir/verification.rs b/src/mir/verification.rs index 475b210f..b8c825f2 100644 --- a/src/mir/verification.rs +++ b/src/mir/verification.rs @@ -163,38 +163,34 @@ impl MirVerifier { } } - /// Verify dominance relations + /// Verify dominance relations (def must dominate use across blocks) fn verify_dominance(&self, function: &MirFunction) -> Result<(), Vec> { - // This is a simplified dominance check - // In a full implementation, we would compute the dominator tree - let errors = Vec::new(); - - // For now, just check that values are defined before use in the same block - for (_block_id, block) in &function.blocks { - let mut defined_in_block = HashSet::new(); - + let mut errors = Vec::new(); + + // Build def -> block map and dominators + let def_block = self.compute_def_blocks(function); + let dominators = self.compute_dominators(function); + + for (use_block_id, block) in &function.blocks { for instruction in block.all_instructions() { - // Check uses for used_value in instruction.used_values() { - if !defined_in_block.contains(&used_value) { - // Value used before definition in this block - // This is okay if it's defined in a dominating block - // For simplicity, we'll skip this check for now + if let Some(&def_bb) = def_block.get(&used_value) { + if def_bb != *use_block_id { + let doms = dominators.get(use_block_id).unwrap(); + if !doms.contains(&def_bb) { + errors.push(VerificationError::DominatorViolation { + value: used_value, + use_block: *use_block_id, + def_block: def_bb, + }); + } + } } } - - // Record definition - if let Some(dst) = instruction.dst_value() { - defined_in_block.insert(dst); - } } } - - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } + + if errors.is_empty() { Ok(()) } else { Err(errors) } } /// Verify control flow graph integrity @@ -234,22 +230,9 @@ impl MirVerifier { /// In merge blocks, values coming from predecessors must be routed through Phi. fn verify_merge_uses(&self, function: &MirFunction) -> Result<(), Vec> { let mut errors = Vec::new(); - // Build predecessor map - let mut preds: std::collections::HashMap> = std::collections::HashMap::new(); - for (bid, block) in &function.blocks { - for succ in &block.successors { - preds.entry(*succ).or_default().push(*bid); - } - } - // Build definition map (value -> def block) - let mut def_block: std::collections::HashMap = std::collections::HashMap::new(); - for (bid, block) in &function.blocks { - for inst in block.all_instructions() { - if let Some(dst) = inst.dst_value() { - def_block.insert(dst, *bid); - } - } - } + let preds = self.compute_predecessors(function); + let def_block = self.compute_def_blocks(function); + let dominators = self.compute_dominators(function); // Helper: collect phi dsts in a block let mut phi_dsts_in_block: std::collections::HashMap> = std::collections::HashMap::new(); for (bid, block) in &function.blocks { @@ -263,12 +246,13 @@ impl MirVerifier { let Some(pred_list) = preds.get(bid) else { continue }; if pred_list.len() < 2 { continue; } let phi_dsts = phi_dsts_in_block.get(bid); + let doms_of_block = dominators.get(bid).unwrap(); // check instructions including terminator for inst in block.all_instructions() { for used in inst.used_values() { if let Some(&db) = def_block.get(&used) { - if pred_list.contains(&db) { - // used value defined in a predecessor; must be routed via phi (i.e., used should be phi dst) + // If def doesn't dominate merge block, it must be routed via phi + if !doms_of_block.contains(&db) { let is_phi_dst = phi_dsts.map(|s| s.contains(&used)).unwrap_or(false); if !is_phi_dst { errors.push(VerificationError::MergeUsesPredecessorValue { @@ -334,6 +318,70 @@ impl MirVerifier { pub fn clear_errors(&mut self) { self.errors.clear(); } + + /// Build predecessor map for all blocks + fn compute_predecessors(&self, function: &MirFunction) -> HashMap> { + let mut preds: HashMap> = HashMap::new(); + for (bid, block) in &function.blocks { + for succ in &block.successors { + preds.entry(*succ).or_default().push(*bid); + } + } + preds + } + + /// Build a map from ValueId to its defining block + fn compute_def_blocks(&self, function: &MirFunction) -> HashMap { + let mut def_block: HashMap = HashMap::new(); + for (bid, block) in &function.blocks { + for inst in block.all_instructions() { + if let Some(dst) = inst.dst_value() { def_block.insert(dst, *bid); } + } + } + def_block + } + + /// Compute dominator sets per block using standard iterative algorithm + fn compute_dominators(&self, function: &MirFunction) -> HashMap> { + let all_blocks: HashSet = function.blocks.keys().copied().collect(); + let preds = self.compute_predecessors(function); + let mut dom: HashMap> = HashMap::new(); + + for &b in function.blocks.keys() { + if b == function.entry_block { + let mut set = HashSet::new(); + set.insert(b); + dom.insert(b, set); + } else { + dom.insert(b, all_blocks.clone()); + } + } + + let mut changed = true; + while changed { + changed = false; + for &b in function.blocks.keys() { + if b == function.entry_block { continue; } + let mut new_set: HashSet = all_blocks.clone(); + if let Some(ps) = preds.get(&b) { + if !ps.is_empty() { + for (i, p) in ps.iter().enumerate() { + if let Some(p_set) = dom.get(p) { + if i == 0 { new_set = p_set.clone(); } + else { new_set = new_set.intersection(p_set).copied().collect(); } + } + } + } + } + new_set.insert(b); + if let Some(old) = dom.get(&b) { + if &new_set != old { dom.insert(b, new_set); changed = true; } + } + } + } + + dom + } } impl Default for MirVerifier { @@ -446,4 +494,57 @@ mod tests { let mir_text = printer.print_module(&module); assert!(mir_text.contains("phi"), "Printed MIR should contain a phi in merge block\n{}", mir_text); } + + #[test] + fn test_merge_use_before_phi_detected() { + use crate::mir::{MirInstruction, ConstValue}; + + // Construct a function with a bad merge use (no phi) + let signature = FunctionSignature { + name: "merge_bad".to_string(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; + + let entry = BasicBlockId::new(0); + let mut f = MirFunction::new(signature, entry); + + let then_bb = BasicBlockId::new(1); + let else_bb = BasicBlockId::new(2); + let merge_bb = BasicBlockId::new(3); + + let cond = f.next_value_id(); // %0 + { + let b0 = f.get_block_mut(entry).unwrap(); + b0.add_instruction(MirInstruction::Const { dst: cond, value: ConstValue::Bool(true) }); + b0.add_instruction(MirInstruction::Branch { condition: cond, then_bb, else_bb }); + } + + let v1 = f.next_value_id(); // %1 + let mut b1 = BasicBlock::new(then_bb); + b1.add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::String("A".to_string()) }); + b1.add_instruction(MirInstruction::Jump { target: merge_bb }); + f.add_block(b1); + + let v2 = f.next_value_id(); // %2 + let mut b2 = BasicBlock::new(else_bb); + b2.add_instruction(MirInstruction::Const { dst: v2, value: ConstValue::String("B".to_string()) }); + b2.add_instruction(MirInstruction::Jump { target: merge_bb }); + f.add_block(b2); + + let mut b3 = BasicBlock::new(merge_bb); + // Illegal: directly use v1 from predecessor + b3.add_instruction(MirInstruction::Return { value: Some(v1) }); + f.add_block(b3); + + f.update_cfg(); + + let mut verifier = MirVerifier::new(); + let res = verifier.verify_function(&f); + assert!(res.is_err(), "Verifier should error on merge use without phi"); + let errs = res.err().unwrap(); + assert!(errs.iter().any(|e| matches!(e, VerificationError::MergeUsesPredecessorValue{..} | VerificationError::DominatorViolation{..})), + "Expected merge/dominator error, got: {:?}", errs); + } } diff --git a/vm_stats_404.json b/vm_stats_404.json new file mode 100644 index 00000000..d416c1f7 --- /dev/null +++ b/vm_stats_404.json @@ -0,0 +1,86 @@ +🔍 DEBUG: Initializing v2 plugin system +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_counter_plugin.so +[FileBox] Plugin initialized +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_filebox_plugin.so +Net plugin: LOG_ON=false, LOG_PATH=net_plugin.log +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_net_plugin.so +🔌 v2 plugin system initialized from nyash.toml + 📦 Registering plugin provider for CounterBox + 📦 Registering plugin provider for FileBox + 📦 Registering plugin provider for HttpServerBox + 📦 Registering plugin provider for HttpClientBox + 📦 Registering plugin provider for HttpResponseBox + 📦 Registering plugin provider for HttpRequestBox + 📦 Registering plugin provider for SocketServerBox + 📦 Registering plugin provider for SocketClientBox + 📦 Registering plugin provider for SocketConnBox +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/vm_stats_http_404.nyash 🚀 +🔍 create_box called for: HttpServerBox +🔍 Config loaded successfully +🔍 Found library: libnyash_net_plugin.so for box type: HttpServerBox +🔍 Plugin loaded successfully +🔍 Reading nyash.toml for type configuration... +🔍 nyash.toml read successfully +🔍 nyash.toml parsed successfully +🔍 Found box config for HttpServerBox with type_id: 20 +🔍 Preparing to call birth() with type_id: 20 +🔍 Output buffer allocated, about to call plugin invoke_fn... +🔍 Calling invoke_fn(type_id=20, method_id=0, instance_id=0, tlv_args=[1, 0, 0, 0], output_buf, output_size=1024) +🔍 invoke_fn returned with result: 0 +🎉 birth() success: HttpServerBox instance_id=1 +[PluginLoaderV2] Invoke HttpServerBox.birth: resolving and encoding args (argc=0) +[VM→Plugin] call HttpServerBox.birth recv_id=1 returns_result=false +[PluginLoaderV2] Invoke HttpServerBox.start: resolving and encoding args (argc=1) +[PluginLoaderV2] arg[0]: Integer(8404) -> I32(tag=2) +[VM→Plugin] call HttpServerBox.start recv_id=1 returns_result=true +🔍 create_box called for: HttpClientBox +🔍 Config loaded successfully +🔍 Found library: libnyash_net_plugin.so for box type: HttpClientBox +🔍 Plugin loaded successfully +🔍 Reading nyash.toml for type configuration... +🔍 nyash.toml read successfully +🔍 nyash.toml parsed successfully +🔍 Found box config for HttpClientBox with type_id: 23 +🔍 Preparing to call birth() with type_id: 23 +🔍 Output buffer allocated, about to call plugin invoke_fn... +🔍 Calling invoke_fn(type_id=23, method_id=0, instance_id=0, tlv_args=[1, 0, 0, 0], output_buf, output_size=1024) +🔍 invoke_fn returned with result: 0 +🎉 birth() success: HttpClientBox instance_id=1 +[PluginLoaderV2] Invoke HttpClientBox.birth: resolving and encoding args (argc=0) +[VM→Plugin] call HttpClientBox.birth recv_id=1 returns_result=false +[PluginLoaderV2] Invoke HttpClientBox.get: resolving and encoding args (argc=1) +[PluginLoaderV2] arg[0]: String(len=31) -> String(tag=6) +[VM→Plugin] call HttpClientBox.get recv_id=1 returns_result=true +[PluginLoaderV2] Invoke HttpServerBox.accept: resolving and encoding args (argc=0) +[VM→Plugin] call HttpServerBox.accept recv_id=1 returns_result=true +🔍 create_box called for: HttpResponseBox +🔍 Config loaded successfully +🔍 Found library: libnyash_net_plugin.so for box type: HttpResponseBox +🔍 Plugin loaded successfully +🔍 Reading nyash.toml for type configuration... +🔍 nyash.toml read successfully +🔍 nyash.toml parsed successfully +🔍 Found box config for HttpResponseBox with type_id: 22 +🔍 Preparing to call birth() with type_id: 22 +🔍 Output buffer allocated, about to call plugin invoke_fn... +🔍 Calling invoke_fn(type_id=22, method_id=0, instance_id=0, tlv_args=[1, 0, 0, 0], output_buf, output_size=1024) +🔍 invoke_fn returned with result: 0 +🎉 birth() success: HttpResponseBox instance_id=2 +[PluginLoaderV2] Invoke HttpResponseBox.birth: resolving and encoding args (argc=0) +[VM→Plugin] call HttpResponseBox.birth recv_id=2 returns_result=false +[PluginLoaderV2] Invoke HttpResponseBox.setStatus: resolving and encoding args (argc=1) +[PluginLoaderV2] arg[0]: Integer(404) -> I32(tag=2) +[VM→Plugin] call HttpResponseBox.setStatus recv_id=2 returns_result=false +[PluginLoaderV2] Invoke HttpResponseBox.write: resolving and encoding args (argc=1) +[PluginLoaderV2] arg[0]: String(len=9) -> String(tag=6) +[VM→Plugin] call HttpResponseBox.write recv_id=2 returns_result=false +[PluginLoaderV2] Invoke HttpRequestBox.respond: resolving and encoding args (argc=1) +[PluginLoaderV2] arg[0]: PluginBoxV2(HttpResponseBox, id=2) -> Handle(tag=8) +[VM→Plugin] call HttpRequestBox.respond recv_id=1 returns_result=false +[PluginLoaderV2] Invoke HttpResponseBox.getStatus: resolving and encoding args (argc=0) +[VM→Plugin] call HttpResponseBox.getStatus recv_id=1 returns_result=false +[PluginLoaderV2] Invoke HttpResponseBox.readBody: resolving and encoding args (argc=0) +[VM→Plugin] call HttpResponseBox.readBody recv_id=1 returns_result=false +✅ VM execution completed successfully! +Result: StringBox { value: "404:Not Found", base: BoxBase { id: 57, parent_type_id: None } } diff --git a/vm_stats_err.json b/vm_stats_err.json new file mode 100644 index 00000000..51b22414 --- /dev/null +++ b/vm_stats_err.json @@ -0,0 +1,3 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/vm_stats_http_err.nyash 🚀 diff --git a/vm_stats_filebox.json b/vm_stats_filebox.json new file mode 100644 index 00000000..f4f3b484 --- /dev/null +++ b/vm_stats_filebox.json @@ -0,0 +1,39 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/vm_stats_filebox.nyash 🚀 +{ + "counts": { + "BoxCall": 17, + "Const": 13, + "NewBox": 12, + "Return": 1, + "Safepoint": 1 + }, + "elapsed_ms": 26.658774, + "timestamp_ms": 1755900319445, + "top20": [ + { + "count": 17, + "op": "BoxCall" + }, + { + "count": 13, + "op": "Const" + }, + { + "count": 12, + "op": "NewBox" + }, + { + "count": 1, + "op": "Return" + }, + { + "count": 1, + "op": "Safepoint" + } + ], + "total": 44 +} +✅ VM execution completed successfully! +Result: StringBox { value: "HELLO", base: BoxBase { id: 59, parent_type_id: None } } diff --git a/vm_stats_ok.json b/vm_stats_ok.json new file mode 100644 index 00000000..e5143e37 --- /dev/null +++ b/vm_stats_ok.json @@ -0,0 +1,3 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/vm_stats_http_ok.nyash 🚀 diff --git a/vm_stats_simple.json b/vm_stats_simple.json new file mode 100644 index 00000000..15f41786 --- /dev/null +++ b/vm_stats_simple.json @@ -0,0 +1,50 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/test_vm_simple.nyash 🚀 +15 +{ + "counts": { + "BinOp": 1, + "BoxCall": 2, + "Const": 5, + "NewBox": 2, + "Print": 1, + "Return": 1, + "Safepoint": 1 + }, + "elapsed_ms": 0.154706, + "timestamp_ms": 1755939354732, + "top20": [ + { + "count": 5, + "op": "Const" + }, + { + "count": 2, + "op": "BoxCall" + }, + { + "count": 2, + "op": "NewBox" + }, + { + "count": 1, + "op": "BinOp" + }, + { + "count": 1, + "op": "Print" + }, + { + "count": 1, + "op": "Return" + }, + { + "count": 1, + "op": "Safepoint" + } + ], + "total": 13 +} +✅ VM execution completed successfully! +Result: IntegerBox { value: 15, base: BoxBase { id: 12, parent_type_id: None } }