feat: MIR TypeOp/WeakRef/Barrier PoC implementation
- Add TypeOpKind, WeakRefOp, BarrierOp enums for unified instructions - Implement TypeOp instruction combining TypeCheck/Cast - Implement WeakRef instruction combining WeakNew/WeakLoad - Implement Barrier instruction combining BarrierRead/BarrierWrite - Update VM to handle new unified instructions - Update MIR printer for new instruction formats - Add feature flags mir_typeop_poc and mir_refbarrier_unify_poc - Maintain backward compatibility with legacy instructions This is Phase 8.5 MIR instruction diet PoC implementation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -20,6 +20,9 @@ dynamic-file = []
|
|||||||
wasm-backend = ["dep:wasmtime", "dep:wabt"]
|
wasm-backend = ["dep:wasmtime", "dep:wabt"]
|
||||||
# プラグイン機構の有効化(ネイティブ環境のみ推奨)
|
# プラグイン機構の有効化(ネイティブ環境のみ推奨)
|
||||||
plugins = ["dep:libloading"]
|
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
|
# Note: LLVM feature requires inkwell dependency and LLVM development libraries
|
||||||
# llvm = ["dep:inkwell"]
|
# llvm = ["dep:inkwell"]
|
||||||
|
|
||||||
|
|||||||
6
docs/CURRENT_TASK.md
Normal file
6
docs/CURRENT_TASK.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Moved: CURRENT_TASK
|
||||||
|
|
||||||
|
このファイルは移動しました。最新の現在タスクは次を参照してください。
|
||||||
|
|
||||||
|
- 新しい場所: [development/current/CURRENT_TASK.md](development/current/CURRENT_TASK.md)
|
||||||
|
|
||||||
@ -7,29 +7,37 @@
|
|||||||
- Net: GET/POST(VM)、404/500(Ok(Response))、unreachable(Err(ErrorBox))
|
- Net: GET/POST(VM)、404/500(Ok(Response))、unreachable(Err(ErrorBox))
|
||||||
3. VM命令カウンタ+時間計測のCLI化(`--vm-stats`, `--vm-stats-json`)とJSON出力対応
|
3. VM命令カウンタ+時間計測のCLI化(`--vm-stats`, `--vm-stats-json`)とJSON出力対応
|
||||||
- サンプル/スクリプト整備(tools/run_vm_stats.sh、local_tests/vm_stats_*.nyash)
|
- サンプル/スクリプト整備(tools/run_vm_stats.sh、local_tests/vm_stats_*.nyash)
|
||||||
4. MIR if-merge 修正(retがphi dstを返す)+ Verifier強化(mergeでのphi未使用検知)
|
4. MIR if-merge 修正(retがphi dstを返す)+ Verifier強化(mergeでのphi未使用検知、支配関係チェック導入)
|
||||||
5. ドキュメント追加・更新
|
5. VMの健全化(分岐・比較・Result)
|
||||||
- Dynamic Plugin Flow(MIR→VM→Registry→Loader→Plugin)
|
- Compare: Void/BoolのEq/Ne定義(順序比較はTypeError)
|
||||||
- Netプラグインのエラーモデル(unreachable=Err, 404/500=Ok)
|
- Branch条件: `BoxRef(BoolBox)→bool`/`BoxRef(VoidBox)→false`/`Integer≠0→true`
|
||||||
- E2Eテスト一覧整備
|
- ResultBox: 新旧両実装への動的ディスパッチ統一(isOk/getValue/getError)
|
||||||
6. CI: plugins E2E ジョブ(Linux)を追加
|
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の健全化(短期・最優先)
|
1) 命令セットダイエットのPoC実装(短期)
|
||||||
- マッピング表更新(Err経路・Handle戻り・Result整合を実測で反映)
|
- フラグ `mir_typeop_poc` 有効時、Builderで TypeCheck/Cast → TypeOp を出力
|
||||||
- Verifierルールの拡充(use-before-def across merge を強化)
|
- VMにTypeOp実行経路を追加(当面は既存と同義)
|
||||||
- 成果物: `docs/reference/architecture/mir-to-vm-mapping.md`(更新済・追補)
|
- 次段: `mir_refbarrier_unify_poc` で Weak*/Barrier 統合(Builder/VM)
|
||||||
|
- 成果物: スナップショット(flag on/off)+ vm-statsで集計キー確認
|
||||||
|
|
||||||
2) VM×プラグインシステムのE2E検証(短期)
|
2) VM×プラグインのE2E拡張(短期)
|
||||||
- FileBox/Netを中心にケース拡張(大きいボディ、ヘッダー多数、タイムアウト等)
|
- HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証
|
||||||
- 成果物: E2E追補+`VM_README.md` に既知の制約とTipsを追記
|
- Socket: タイムアウト系の追加ケース(連続acceptTimeout/recvTimeout)
|
||||||
|
- 成果物: E2E追加と `VM_README.md` のTips追補
|
||||||
|
|
||||||
3) 命令セットのダイエット(中期:目標26命令)
|
3) ResultBox単一路線への統合(中期)
|
||||||
- 実測(HTTP OK/404/500/unreachable、FileBox)を反映して合意版を確定
|
- 新`NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行
|
||||||
- 統合方針(TypeOp/WeakRef/Barrierの統合、ExternCall最小化)
|
- 成果物: 実装整理・移行メモ・影響調査
|
||||||
- 段階移行(ビルドモードでメタ降格、互換エイリアス→削除)と回帰テスト整備
|
|
||||||
- 成果物: 26命令案(合意版)+移行計画
|
|
||||||
|
|
||||||
## ▶ 実行コマンド例
|
## ▶ 実行コマンド例
|
||||||
|
|
||||||
@ -58,7 +66,7 @@ nyash --verify examples/plugin_box_sample.nyash
|
|||||||
- メタ降格: Debug / Nop / Safepoint(ビルドモードで制御)
|
- メタ降格: Debug / Nop / Safepoint(ビルドモードで制御)
|
||||||
|
|
||||||
---
|
---
|
||||||
最終更新: 2025年8月23日(VM×Plugins安定・MIR修正・26命令合意ドラフト/再起動チェックポイント)
|
最終更新: 2025年8月23日(VM強化・E2E拡張・TypeOp PoC着手/次段はBuilder/VMマッピング)
|
||||||
|
|
||||||
## 🔁 再起動後の再開手順(ショート)
|
## 🔁 再起動後の再開手順(ショート)
|
||||||
```bash
|
```bash
|
||||||
@ -71,4 +79,8 @@ cargo test --features plugins -q -- --nocapture
|
|||||||
# 3) VM Stats 代表値の再取得(任意)
|
# 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_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_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
|
||||||
```
|
```
|
||||||
|
|||||||
81
docs/development/proposals/mir-typeop-weakref-barrier-poc.md
Normal file
81
docs/development/proposals/mir-typeop-weakref-barrier-poc.md
Normal file
@ -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
|
||||||
|
|
||||||
@ -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.3**: Box操作WASM実装(Copilot担当)
|
||||||
- **Phase 8.4**: ネイティブコンパイル実装計画(AI大会議策定済み)
|
- **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哲学
|
- **[コアコンセプト](../nyash_core_concepts.md)** - Everything is Box哲学
|
||||||
|
|
||||||
### 🔄 進捗管理
|
### 🔄 進捗管理
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
## 🎯 目的
|
## 🎯 目的
|
||||||
開発者向けに「ビルド計画・段階的タスク・設計上の要点」を集約。
|
開発者向けに「ビルド計画・段階的タスク・設計上の要点」を集約。
|
||||||
利用者向けの具体的なビルド手順は docs/説明書/native-build/README.md を参照。
|
利用者向けの具体的なビルド手順は guides/ 以下の各ガイドを参照。
|
||||||
|
|
||||||
## 📋 重要リンク
|
## 📋 重要リンク
|
||||||
- **現在のタスク**: [docs/CURRENT_TASK.md](../../CURRENT_TASK.md)
|
- **現在のタスク**: [development/current/CURRENT_TASK.md](../../current/CURRENT_TASK.md)
|
||||||
- **コア概念(速習)**: [docs/nyash_core_concepts.md](../../nyash_core_concepts.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)
|
- **🤖 AI大会議記録**: [../ai_conference_native_compilation_20250814.md](../ai_conference_native_compilation_20250814.md)
|
||||||
- **🗺️ ネイティブコンパイル戦略**: [../native-compilation-roadmap.md](../native-compilation-roadmap.md)
|
- **🗺️ ネイティブコンパイル戦略**: [../native-compilation-roadmap.md](../native-compilation-roadmap.md)
|
||||||
- **フェーズ課題一覧**: [issues/](issues/)
|
- **フェーズ課題一覧**: [issues/](issues/)
|
||||||
|
|||||||
32
docs/examples/http_result_patterns.md
Normal file
32
docs/examples/http_result_patterns.md
Normal file
@ -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統計・既知の制約)
|
||||||
6
docs/execution-backends.md
Normal file
6
docs/execution-backends.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Moved: 実行バックエンド完全ガイド
|
||||||
|
|
||||||
|
このドキュメントは構成再編により移動しました。最新の内容はこちら:
|
||||||
|
|
||||||
|
- 新しい場所: [reference/architecture/execution-backends.md](reference/architecture/execution-backends.md)
|
||||||
|
|
||||||
@ -113,3 +113,6 @@ Nyash Source ──▶ MIR (Builder)
|
|||||||
- `docs/reference/architecture/mir-to-vm-mapping.md`
|
- `docs/reference/architecture/mir-to-vm-mapping.md`
|
||||||
- `docs/reference/architecture/mir-26-instruction-diet.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統計とプラグイン周りの既知制約
|
||||||
|
|||||||
@ -84,3 +84,13 @@ Appendix: Rationale
|
|||||||
- Smaller ISA simplifies VM fast paths and aids JIT/AOT later.
|
- Smaller ISA simplifies VM fast paths and aids JIT/AOT later.
|
||||||
- Data shows hot paths concentrated on calls, const, alloc; control sparse and localized.
|
- Data shows hot paths concentrated on calls, const, alloc; control sparse and localized.
|
||||||
- Folding type ops reduces interpreter/VM dispatch; verification handles safety.
|
- 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
|
||||||
|
|||||||
@ -85,7 +85,7 @@ read = { args = [] } # 引数なし
|
|||||||
./tools/plugin-tester/target/release/plugin-tester io plugin.so
|
./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倍高速化
|
- **⚡ 性能**: 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)
|
||||||
|
|
||||||
|
|||||||
1
docs/予定/native-plan/copilot_issues.txt
Normal file
1
docs/予定/native-plan/copilot_issues.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Moved: copilot_issues\n\nこのファイルは移動しました。最新は下記を参照してください。\n\n- 新しい場所: ../../development/roadmap/native-plan/copilot_issues.txt\n
|
||||||
42
mir_dump_fixed.txt
Normal file
42
mir_dump_fixed.txt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
42
mir_dump_http_error.txt
Normal file
42
mir_dump_http_error.txt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
42
mir_dump_http_error_check.txt
Normal file
42
mir_dump_http_error_check.txt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -457,6 +457,22 @@ impl VM {
|
|||||||
Ok(ControlFlow::Continue)
|
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 } => {
|
MirInstruction::Return { value } => {
|
||||||
let return_value = if let Some(val_id) = value {
|
let return_value = if let Some(val_id) = value {
|
||||||
let val = self.get_value(*val_id)?;
|
let val = self.get_value(*val_id)?;
|
||||||
@ -831,6 +847,25 @@ impl VM {
|
|||||||
Ok(ControlFlow::Continue)
|
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: _ } => {
|
MirInstruction::BarrierRead { ptr: _ } => {
|
||||||
// Memory barrier read is a no-op for now
|
// Memory barrier read is a no-op for now
|
||||||
// In a real implementation, this would ensure memory ordering
|
// In a real implementation, this would ensure memory ordering
|
||||||
@ -1085,6 +1120,7 @@ impl VM {
|
|||||||
MirInstruction::NewBox { .. } => "NewBox",
|
MirInstruction::NewBox { .. } => "NewBox",
|
||||||
MirInstruction::TypeCheck { .. } => "TypeCheck",
|
MirInstruction::TypeCheck { .. } => "TypeCheck",
|
||||||
MirInstruction::Cast { .. } => "Cast",
|
MirInstruction::Cast { .. } => "Cast",
|
||||||
|
MirInstruction::TypeOp { .. } => "TypeOp",
|
||||||
MirInstruction::ArrayGet { .. } => "ArrayGet",
|
MirInstruction::ArrayGet { .. } => "ArrayGet",
|
||||||
MirInstruction::ArraySet { .. } => "ArraySet",
|
MirInstruction::ArraySet { .. } => "ArraySet",
|
||||||
MirInstruction::Copy { .. } => "Copy",
|
MirInstruction::Copy { .. } => "Copy",
|
||||||
@ -1101,6 +1137,8 @@ impl VM {
|
|||||||
MirInstruction::WeakLoad { .. } => "WeakLoad",
|
MirInstruction::WeakLoad { .. } => "WeakLoad",
|
||||||
MirInstruction::BarrierRead { .. } => "BarrierRead",
|
MirInstruction::BarrierRead { .. } => "BarrierRead",
|
||||||
MirInstruction::BarrierWrite { .. } => "BarrierWrite",
|
MirInstruction::BarrierWrite { .. } => "BarrierWrite",
|
||||||
|
MirInstruction::WeakRef { .. } => "WeakRef",
|
||||||
|
MirInstruction::Barrier { .. } => "Barrier",
|
||||||
MirInstruction::FutureNew { .. } => "FutureNew",
|
MirInstruction::FutureNew { .. } => "FutureNew",
|
||||||
MirInstruction::FutureSet { .. } => "FutureSet",
|
MirInstruction::FutureSet { .. } => "FutureSet",
|
||||||
MirInstruction::Await { .. } => "Await",
|
MirInstruction::Await { .. } => "Await",
|
||||||
|
|||||||
@ -137,6 +137,16 @@ pub enum MirInstruction {
|
|||||||
target_type: MirType,
|
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 ===
|
// === Array Operations ===
|
||||||
/// Get array element
|
/// Get array element
|
||||||
/// `%dst = %array[%index]`
|
/// `%dst = %array[%index]`
|
||||||
@ -251,6 +261,22 @@ pub enum MirInstruction {
|
|||||||
ptr: ValueId,
|
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 ===
|
// === Phase 7: Async/Future Operations ===
|
||||||
|
|
||||||
/// Create a new Future with initial value
|
/// Create a new Future with initial value
|
||||||
@ -354,6 +380,7 @@ impl MirInstruction {
|
|||||||
MirInstruction::UnaryOp { .. } |
|
MirInstruction::UnaryOp { .. } |
|
||||||
MirInstruction::Compare { .. } |
|
MirInstruction::Compare { .. } |
|
||||||
MirInstruction::Cast { .. } |
|
MirInstruction::Cast { .. } |
|
||||||
|
MirInstruction::TypeOp { .. } |
|
||||||
MirInstruction::Copy { .. } |
|
MirInstruction::Copy { .. } |
|
||||||
MirInstruction::Phi { .. } |
|
MirInstruction::Phi { .. } |
|
||||||
MirInstruction::TypeCheck { .. } |
|
MirInstruction::TypeCheck { .. } |
|
||||||
@ -396,6 +423,15 @@ impl MirInstruction {
|
|||||||
MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects
|
MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects
|
||||||
MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read
|
MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read
|
||||||
MirInstruction::BarrierWrite { .. } => EffectMask::WRITE.add(Effect::Barrier), // Memory barrier with write
|
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
|
// Phase 7: Async/Future Operations
|
||||||
MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate
|
MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate
|
||||||
@ -419,12 +455,14 @@ impl MirInstruction {
|
|||||||
MirInstruction::NewBox { dst, .. } |
|
MirInstruction::NewBox { dst, .. } |
|
||||||
MirInstruction::TypeCheck { dst, .. } |
|
MirInstruction::TypeCheck { dst, .. } |
|
||||||
MirInstruction::Cast { dst, .. } |
|
MirInstruction::Cast { dst, .. } |
|
||||||
|
MirInstruction::TypeOp { dst, .. } |
|
||||||
MirInstruction::ArrayGet { dst, .. } |
|
MirInstruction::ArrayGet { dst, .. } |
|
||||||
MirInstruction::Copy { dst, .. } |
|
MirInstruction::Copy { dst, .. } |
|
||||||
MirInstruction::RefNew { dst, .. } |
|
MirInstruction::RefNew { dst, .. } |
|
||||||
MirInstruction::RefGet { dst, .. } |
|
MirInstruction::RefGet { dst, .. } |
|
||||||
MirInstruction::WeakNew { dst, .. } |
|
MirInstruction::WeakNew { dst, .. } |
|
||||||
MirInstruction::WeakLoad { dst, .. } |
|
MirInstruction::WeakLoad { dst, .. } |
|
||||||
|
MirInstruction::WeakRef { dst, .. } |
|
||||||
MirInstruction::FutureNew { dst, .. } |
|
MirInstruction::FutureNew { dst, .. } |
|
||||||
MirInstruction::Await { dst, .. } => Some(*dst),
|
MirInstruction::Await { dst, .. } => Some(*dst),
|
||||||
|
|
||||||
@ -443,6 +481,7 @@ impl MirInstruction {
|
|||||||
MirInstruction::RefSet { .. } |
|
MirInstruction::RefSet { .. } |
|
||||||
MirInstruction::BarrierRead { .. } |
|
MirInstruction::BarrierRead { .. } |
|
||||||
MirInstruction::BarrierWrite { .. } |
|
MirInstruction::BarrierWrite { .. } |
|
||||||
|
MirInstruction::Barrier { .. } |
|
||||||
MirInstruction::FutureSet { .. } |
|
MirInstruction::FutureSet { .. } |
|
||||||
MirInstruction::Safepoint |
|
MirInstruction::Safepoint |
|
||||||
MirInstruction::Nop => None,
|
MirInstruction::Nop => None,
|
||||||
@ -462,6 +501,7 @@ impl MirInstruction {
|
|||||||
MirInstruction::Load { ptr: operand, .. } |
|
MirInstruction::Load { ptr: operand, .. } |
|
||||||
MirInstruction::TypeCheck { value: operand, .. } |
|
MirInstruction::TypeCheck { value: operand, .. } |
|
||||||
MirInstruction::Cast { value: operand, .. } |
|
MirInstruction::Cast { value: operand, .. } |
|
||||||
|
MirInstruction::TypeOp { value: operand, .. } |
|
||||||
MirInstruction::Copy { src: operand, .. } |
|
MirInstruction::Copy { src: operand, .. } |
|
||||||
MirInstruction::Debug { value: operand, .. } |
|
MirInstruction::Debug { value: operand, .. } |
|
||||||
MirInstruction::Print { value: operand, .. } => vec![*operand],
|
MirInstruction::Print { value: operand, .. } => vec![*operand],
|
||||||
@ -511,6 +551,8 @@ impl MirInstruction {
|
|||||||
MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref],
|
MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref],
|
||||||
MirInstruction::BarrierRead { ptr } => vec![*ptr],
|
MirInstruction::BarrierRead { ptr } => vec![*ptr],
|
||||||
MirInstruction::BarrierWrite { ptr } => vec![*ptr],
|
MirInstruction::BarrierWrite { ptr } => vec![*ptr],
|
||||||
|
MirInstruction::WeakRef { value, .. } => vec![*value],
|
||||||
|
MirInstruction::Barrier { ptr, .. } => vec![*ptr],
|
||||||
|
|
||||||
// Phase 7: Async/Future Operations
|
// Phase 7: Async/Future Operations
|
||||||
MirInstruction::FutureNew { value, .. } => vec![*value],
|
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 {
|
impl ConstValue {
|
||||||
/*
|
/*
|
||||||
/// Convert to NyashValue
|
/// Convert to NyashValue
|
||||||
|
|||||||
@ -18,7 +18,7 @@ pub mod value_id;
|
|||||||
pub mod effect;
|
pub mod effect;
|
||||||
|
|
||||||
// Re-export main types for easy access
|
// 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 instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
||||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||||
pub use function::{MirFunction, MirModule, FunctionSignature};
|
pub use function::{MirFunction, MirModule, FunctionSignature};
|
||||||
|
|||||||
@ -279,6 +279,11 @@ impl MirPrinter {
|
|||||||
format!("{} = cast {} to {:?}", dst, value, target_type)
|
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 } => {
|
MirInstruction::ArrayGet { dst, array, index } => {
|
||||||
format!("{} = {}[{}]", dst, array, index)
|
format!("{} = {}[{}]", dst, array, index)
|
||||||
},
|
},
|
||||||
@ -349,6 +354,16 @@ impl MirPrinter {
|
|||||||
format!("barrier_write {}", ptr)
|
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
|
// Phase 7: Async/Future Operations
|
||||||
MirInstruction::FutureNew { dst, value } => {
|
MirInstruction::FutureNew { dst, value } => {
|
||||||
format!("{} = future_new {}", dst, value)
|
format!("{} = future_new {}", dst, value)
|
||||||
|
|||||||
@ -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<VerificationError>> {
|
fn verify_dominance(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||||
// This is a simplified dominance check
|
let mut errors = Vec::new();
|
||||||
// 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
|
// Build def -> block map and dominators
|
||||||
for (_block_id, block) in &function.blocks {
|
let def_block = self.compute_def_blocks(function);
|
||||||
let mut defined_in_block = HashSet::new();
|
let dominators = self.compute_dominators(function);
|
||||||
|
|
||||||
|
for (use_block_id, block) in &function.blocks {
|
||||||
for instruction in block.all_instructions() {
|
for instruction in block.all_instructions() {
|
||||||
// Check uses
|
|
||||||
for used_value in instruction.used_values() {
|
for used_value in instruction.used_values() {
|
||||||
if !defined_in_block.contains(&used_value) {
|
if let Some(&def_bb) = def_block.get(&used_value) {
|
||||||
// Value used before definition in this block
|
if def_bb != *use_block_id {
|
||||||
// This is okay if it's defined in a dominating block
|
let doms = dominators.get(use_block_id).unwrap();
|
||||||
// For simplicity, we'll skip this check for now
|
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() {
|
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify control flow graph integrity
|
/// Verify control flow graph integrity
|
||||||
@ -234,22 +230,9 @@ impl MirVerifier {
|
|||||||
/// In merge blocks, values coming from predecessors must be routed through Phi.
|
/// In merge blocks, values coming from predecessors must be routed through Phi.
|
||||||
fn verify_merge_uses(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
fn verify_merge_uses(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
// Build predecessor map
|
let preds = self.compute_predecessors(function);
|
||||||
let mut preds: std::collections::HashMap<BasicBlockId, Vec<BasicBlockId>> = std::collections::HashMap::new();
|
let def_block = self.compute_def_blocks(function);
|
||||||
for (bid, block) in &function.blocks {
|
let dominators = self.compute_dominators(function);
|
||||||
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<ValueId, BasicBlockId> = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Helper: collect phi dsts in a block
|
// Helper: collect phi dsts in a block
|
||||||
let mut phi_dsts_in_block: std::collections::HashMap<BasicBlockId, std::collections::HashSet<ValueId>> = std::collections::HashMap::new();
|
let mut phi_dsts_in_block: std::collections::HashMap<BasicBlockId, std::collections::HashSet<ValueId>> = std::collections::HashMap::new();
|
||||||
for (bid, block) in &function.blocks {
|
for (bid, block) in &function.blocks {
|
||||||
@ -263,12 +246,13 @@ impl MirVerifier {
|
|||||||
let Some(pred_list) = preds.get(bid) else { continue };
|
let Some(pred_list) = preds.get(bid) else { continue };
|
||||||
if pred_list.len() < 2 { continue; }
|
if pred_list.len() < 2 { continue; }
|
||||||
let phi_dsts = phi_dsts_in_block.get(bid);
|
let phi_dsts = phi_dsts_in_block.get(bid);
|
||||||
|
let doms_of_block = dominators.get(bid).unwrap();
|
||||||
// check instructions including terminator
|
// check instructions including terminator
|
||||||
for inst in block.all_instructions() {
|
for inst in block.all_instructions() {
|
||||||
for used in inst.used_values() {
|
for used in inst.used_values() {
|
||||||
if let Some(&db) = def_block.get(&used) {
|
if let Some(&db) = def_block.get(&used) {
|
||||||
if pred_list.contains(&db) {
|
// If def doesn't dominate merge block, it must be routed via phi
|
||||||
// used value defined in a predecessor; must be routed via phi (i.e., used should be phi dst)
|
if !doms_of_block.contains(&db) {
|
||||||
let is_phi_dst = phi_dsts.map(|s| s.contains(&used)).unwrap_or(false);
|
let is_phi_dst = phi_dsts.map(|s| s.contains(&used)).unwrap_or(false);
|
||||||
if !is_phi_dst {
|
if !is_phi_dst {
|
||||||
errors.push(VerificationError::MergeUsesPredecessorValue {
|
errors.push(VerificationError::MergeUsesPredecessorValue {
|
||||||
@ -334,6 +318,70 @@ impl MirVerifier {
|
|||||||
pub fn clear_errors(&mut self) {
|
pub fn clear_errors(&mut self) {
|
||||||
self.errors.clear();
|
self.errors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build predecessor map for all blocks
|
||||||
|
fn compute_predecessors(&self, function: &MirFunction) -> HashMap<BasicBlockId, Vec<BasicBlockId>> {
|
||||||
|
let mut preds: HashMap<BasicBlockId, Vec<BasicBlockId>> = 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<ValueId, BasicBlockId> {
|
||||||
|
let mut def_block: HashMap<ValueId, BasicBlockId> = 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<BasicBlockId, HashSet<BasicBlockId>> {
|
||||||
|
let all_blocks: HashSet<BasicBlockId> = function.blocks.keys().copied().collect();
|
||||||
|
let preds = self.compute_predecessors(function);
|
||||||
|
let mut dom: HashMap<BasicBlockId, HashSet<BasicBlockId>> = 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<BasicBlockId> = 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 {
|
impl Default for MirVerifier {
|
||||||
@ -446,4 +494,57 @@ mod tests {
|
|||||||
let mir_text = printer.print_module(&module);
|
let mir_text = printer.print_module(&module);
|
||||||
assert!(mir_text.contains("phi"), "Printed MIR should contain a phi in merge block\n{}", mir_text);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
vm_stats_404.json
Normal file
86
vm_stats_404.json
Normal file
@ -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 } }
|
||||||
3
vm_stats_err.json
Normal file
3
vm_stats_err.json
Normal file
@ -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 🚀
|
||||||
39
vm_stats_filebox.json
Normal file
39
vm_stats_filebox.json
Normal file
@ -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 } }
|
||||||
3
vm_stats_ok.json
Normal file
3
vm_stats_ok.json
Normal file
@ -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 🚀
|
||||||
50
vm_stats_simple.json
Normal file
50
vm_stats_simple.json
Normal file
@ -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 } }
|
||||||
Reference in New Issue
Block a user