Files
hakorune/CURRENT_TASK.md
Moe Charm 2c795fa554 クリーンアップ: プロジェクト整理とGitHub公開準備
## 🧹 ルートディレクトリ整理
- batファイル(21個) → tools/windows/llvm-build-attempts/へ移動
- ログファイル → logs/archive/へ移動
- LLVM実行ファイル(計104MB) → .gitignoreに追加して削除

## 📝 .gitignore更新
- app_*_llvm, app_link, string_len_app を除外
- 大容量バイナリファイルのアップロード防止

## 🔧 追加のリファクタリング
- src/jit/lower/builder/tls.rs: TLS関連を独立モジュール化

## 📊 整理結果
- ルートディレクトリ: 0個の一時ファイル(完全クリーン)
- リポジトリサイズ: 189MB(適切)
- 最大追跡ファイル: 5.2MB(許容範囲内)

GitHub公開準備完了!🚀

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 12:31:33 +09:00

1104 lines
72 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# CURRENT TASK (Phase 11.7 kick-off: JIT Complete / Semantics Layer)
> Quick Resume (Phase 12 bridge)
- Where to look next:
- Phase 12 overview: docs/development/roadmap/phases/phase-12/README.md
- Task board: docs/development/roadmap/phases/phase-12/TASKS.md
- ABI digest: docs/development/roadmap/phases/phase-12/NYASH-ABI-DESIGN.md
- Refactoring plan: docs/development/roadmap/phases/phase-12/REFACTORING_PLAN.md
- Build/run (VM/JIT):
- VM: `cargo build --release --features cranelift-jit && ./target/release/nyash --backend vm apps/tests/ny-echo-lite/main.nyash`
- JIT: `./target/release/nyash --backend jit apps/tests/ny-echo-lite/main.nyash`
- MapBox extensions (VM/JIT): remove/clear/getOr/keys/values/JSON are available; keys/values currently return newline-joined String (shim).
- First refactor candidate: split `src/runner.rs` into `runner/mod.rs` + `runner/modes/*` (see REFACTORING_PLAN.md).
Phase 11.7 へ仕切り直し(会議合意)
- 単一意味論層: MIR→Semantics→{VM/Cranelift/LLVM/WASM} の設計に切替。VMは参照実装、実行/生成はCodegen側で一本化。
- フォールバック廃止: VM→JITの実行時フォールバックは行わない。JITは“コンパイル専用/AOT補助”に限定。
- 共通ABI: handle/i64/ptr 変換、to_bool/compare、タグ分類、invoke固定/可変、NyRTシム呼び出しを共通化。
- Cranelift: 軽量JIT/AOTパスを追加し、最小実装からMIR-15を段階的に緑化。LLVM AOT は併存。
Docs: docs/development/roadmap/phases/phase-11.7_jit_complete/{README.md, PLAN.md, CURRENT_TASK.md, MEETING_NOTES.md}
以降は下記の旧計画LLVM準備をアーカイブ参照。スモークやツールは必要箇所を段階で引継ぎ。
開発哲学Box-First
- 「箱を作って下に積む」原則で進める。境界を先に置き、no-op足場→小さく通す→観測→厳密化の順で段階導入。
- 詳細: docs/development/philosophy/box-first-manifesto.md
次の候補(再開時)
- spawn を本当の非同期化TLV化Scheduler投入
- JIT/EXE用 nyash.future.spawn_method_h C-ABI 追加
Async Task System (Structured Concurrency)
- Spec/Plan: docs/development/roadmap/phases/phase-11.7_jit_complete/async_task_system/{SPEC.md, PLAN.md}
- 目的: Nyash→MIR→VM→JIT/EXE まで一貫した構造化並行を実現TaskGroup/Future を Box化
- 進捗P1: FutureBox を Mutex+Condvar 化、await は safepoint+timeoutNYASH_AWAIT_MAX_MSで無限待ち防止済み。
Update (2025-09-01 late / Async P2 skeleton + P3 await.Result unify)
- P2雛形
- CancellationToken 型cancel/is_cancelledを追加: src/runtime/scheduler.rs
- Scheduler::spawn_with_token / global_hooks::spawn_task_with_token現状はspawnへ委譲
- TaskGroupBox 雛形: src/boxes/task_group_box.rsAPI: new/cancel_all/is_cancelled
- P3awaitのResult化・第一弾
- VM Await: Futureの値を NyashResultBox::Ok で返すsrc/backend/vm_instructions.rs
- env.future.awaitVM/Unified: Ok(value)Err("Timeout") を返すtimeoutは NYASH_AWAIT_MAX_MS 既定5秒
- JIT Await: await_h は常にハンドル返却→ nyash.result.ok_h で Ok(handle) にラップ
- 追加: src/jit/extern/result.rsSYM_RESULT_OK_Hlowerで await 後に ok_h を差し込み
- Smokes/安全ガード
- tools/smoke_async_spawn.shtimeout 10s + NYASH_AWAIT_MAX_MS=5000。apps/tests/async-spawn-instance は nowait/awaitの安全版
- ハングした場合もOS timeoutで残存プロセスを避ける
次の実装順(合意があれば即着手)
1) Phase 2: VM 経路への最小配線(暗黙 TaskGroup
- nowait の着地点を TaskGroup.current().spawn(...) に切替(内部は現行 spawn_instance を踏む)
- scope 終了時の cancelAll→joinAllLIFOは雛形から段階導入まずは no-op フック)
2) Phase 3: JIT 側の Err 統一
- nyash.result.err_h(handle_or_msg) をNyRT/JITに追加し、timeout/キャンセル時に Err 化await ラッパで分岐)
- 0/None フォールバックを撤去VM/Externは実装済みJITを揃える
3) Verifier: await 前後の checkpoint 検証
- Lowerer/パスで ExternCall(env.runtime.checkpoint) の前後挿入・検証ルールを追加
4) CI/Smokes 最終化
- async-await-min, async-spawn-instanceVM/JIT、timeout系await_timeoutの3本を最小温存
- {vm,jit} × {default,strict} で timeout(10s) ガード
Update (2025-09-01 PM / Semantics unify + Async Phase-2 prep)
- Semantics layer in place and adopted by Interpreter/VM/JIT
- New: `src/runtime/semantics.rs` with `coerce_to_string` / `coerce_to_i64` and unified `+` ordering
- Interpreter/VM now delegate to semantics (string concat matches VM path; plugin String toUtf8→toString→fallback)
- JIT: added hostcall `nyash.semantics.add_hh` (handle,handle) for dynamic add; wired in builder/core
- Cross-backend smokes (Script/VM/JIT via VM+jit-exec)
- `apps/tests/mir-branch-ret` → 1 (all three)
- `apps/tests/semantics-unified` → 0 (concat/numeric semantics aligned across backends)
- Tool: `tools/apps_tri_backend_smoke.sh` (summarizes Result lines for 3 modes)
- Async Phase2 (front): minimal spawn API and await path
- Exposed `env.future.spawn_instance(recv, method_name, args...) -> FutureBox`
- For now resolves synchronously (safe baseline). Hooked to `PluginHost::invoke_instance_method`
- `env.runtime.checkpoint` now calls `global_hooks::safepoint_and_poll()` for future scheduler integration
- `env.future.await(value)` passthrough unless value is FutureBox (then wait_and_get)
- `apps/tests/async-await-min`: uses `nowait fut = 42; await fut;` → VM/JIT return 42; Interpreter runs (CLI does not print standardized Result line)
Delta to code
- Added: `src/runtime/semantics.rs`
- Updated: `src/interpreter/expressions/operators.rs`, `src/backend/vm_values.rs` → use semantics layer
- JIT: `src/jit/lower/{builder.rs, core_ops.rs, extern_thunks.rs}`, `src/jit/extern/collections.rs`
- NyRT CABI: `crates/nyrt/src/lib.rs` exported `nyash.semantics.add_hh`
- Runtime externs: `src/runtime/plugin_loader_v2.rs` added `env.future.spawn_instance`, improved `env.runtime.checkpoint`
- Hooks/Scheduler: `src/runtime/global_hooks.rs` (safepoint+poll, spawn_task stub), `src/runtime/scheduler.rs` (singlethread scheduler API scaffold present)
- Smokes/tools: `apps/tests/semantics-unified`, `apps/tests/gc-sync-stress`, `tools/apps_tri_backend_smoke.sh`
Open items / Next steps (proposed)
1) True async spawn (Phase2 complete)
- Change `spawn_instance` to queue a task (Scheduler) instead of inline: capture `recv(type_id/instance_id)`, `method_name`, and TLVencoded args
- On run: decode TLV and `invoke_instance_method`, set Future result; ensure safepoint via `checkpoint`
- Add NyRT CABI `nyash.future.spawn_method_h` for JIT/EXE and wire lowering
2) Interpreter CLI result normalization (optional)
- Align “Result:” line for static box Main return to make Script mode consistent with VM/JIT in smokes
3) Keep smokes minimal (avoid bloat); current trio is sufficient for gating
Update (2025-09-01 AM / JIT handoff follow-up)
- Cranelift 最小JITの下地は進捗良好LowerCore→CraneliftBuilder 経路)
- Compare/Branch/Jump、最小Phiblock params、StackSlotベースの Load/Store は実装済みbuilder.rs/core.rs
- ExternCall(env.console.log/println) の最小橋渡しConsoleBox.birth_h→by-id invokeを追加済み。
- 追加スモークjit-direct 用I/Oなし
- apps/tests/mir-store-load: x=1; y=2; x=x+y; return x → 3
- apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x 10
- apps/tests/mir-branch-multi: 入れ子条件分岐 1
- 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash`
- jit-direct 分岐/PHI 根治進行中
- 現象: select/compare の実行時観測では cond=1/then=1/else=0 と正しいが最終結果が 0 に落ちるケースあり
- 統合JIT`--backend cranelift`は期待どおりLowerCore の意味論は正しくjit-direct のCFG/合流が疑わしい
- 主因仮説確度高: 関数共有の value_stack をブロック間で使い回し分岐/合流で返値取り違え
変更点犯人切り分けと根治のための構造改革ログ
- CraneliftBuilderjit-direct 経路
- 単一出口明示合流SSAへ切替途中段階完了段階へ移行中
- ret 合流ブロックを導入しBlockParam(i64) で戻り値を受け渡し
- すべての `emit_return` i64 正規化 `jump(ret, [val])`に統一return 命令は合流側だけ)。
- end_function 側で ret 合流ブロックに切替しBlockParam f64 変換必要時のうえ return
- Compare/Branch/Select まわり
- Compare 結果をローカルスロットへ保存Branch/Select 時に確実にロードスタック取りこぼし排除)。
- `br_if` 直前に cond b1 正規化i64b1)。
- デバッグ用 extern を登録必要時のみ
- `nyash.jit.dbg_i64(tag: i64, val: i64) -> i64`値観測用
- `nyash.jit.block_enter(idx: i64) -> void`ブロック入場ログ
- LowerCorereturn 値の堅牢化
- Return 値が known/param/slot 経路に乗らない場合同一ブロックの Const 定義をスキャンして materialize
- Fast-path読みやすさ単純化: then/else が定数 return の場合`select(cond, K_then, K_else)``emit_return` に縮約`NYASH_JIT_FASTPATH_SELECT=1` で強制)。
診断ログ必要時のみ ON
- `NYASH_JIT_TRACE_BLOCKS=1` ブロック入場ログ`[JIT-BLOCK] enter=<idx>`
- `NYASH_JIT_TRACE_BR=1` …… br_if cond 有無`[JIT-CLIF] br_if cond_present=...`
- `NYASH_JIT_TRACE_SEL=1` select cond/then/else tag=100/101/102
- `NYASH_JIT_TRACE_RET=1` return の値: emit_return 直前tag=201、ret 合流tag=200
再現実行コマンドjit-direct
- 分岐の最小: `apps/tests/mir-branch-ret/main.nyash`
- `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 診断ON: `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_RET=1 NYASH_JIT_TRACE_BLOCKS=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- PHI最小: `apps/tests/mir-phi-min/main.nyash`
- 複合分岐: `apps/tests/mir-branch-multi/main.nyash`
期待と現状2025-09-01 PM 時点
- 統合JIT`--backend cranelift`: OK`mir-branch-ret`1)。
- jit-direct: 単一出口BlockParam 合流の配線で安定化を確認cond/then/else も正常)。tag=201/200 は一致
検証結果jit-direct / 2025-09-01 実行
- `apps/tests/mir-branch-ret`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-phi-min`: Result=10, [JIT-DBG] 201=10 / 200=10
- `apps/tests/mir-branch-multi`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-nested-branch`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-phi-two`: Result=50
次のタスク仕上げ
1) LowerCore fastpath/select のガード整理`NYASH_JIT_FASTPATH_SELECT` の簡素化)。
2) b1 返り値 ABI を有効化する場合の経路確認feature `jit-b1-abi`)。
3) ドキュメント整備CraneliftBuilder 単一出口方針と TRACE 変数の最終化)。
Update (2025-09-02 / JIT sealPHI安定化 + builder分割 進捗)
- 完了JIT / jit-direct 最小3本グリーン
- seal 管理の一本化途中seal撤廃end_functionで最終seal)。
- PHI(min) 合流: 事前スキャンensure_block_paramsbr/jump時のargs/append秩序確立
- Return の安定化known/param/slot優先フォールバック const materialize)。
- 診断ログ整備NYASH_JIT_DUMP/TRACE_*)。
- スモークdebug/release: mir-branch-ret=1, mir-phi-min=10, mir-branch-multi=1。
- リファクタリングbuilder 1,000行目安に向けて段階実施
- 分離済み:
- `src/jit/lower/builder/noop.rs`NoopBuilder
- `src/jit/lower/builder/object.rs`AOT .o ObjectBuilderfinish対応
- `src/jit/lower/builder/rt_shims.rs`nyash_jit_dbg_i64 等の小シム群
- `src/jit/lower/builder/tls.rs`clif_tls TLS 呼び出しヘルパ
- 動作維持: pub use で既存パス互換jit-direct スモーク通過
- 残タスク次手
- [ ] CraneliftBuilder 本体を `builder/cranelift.rs` に分離大枠)。
- [ ] `builder.rs` を薄い ハブ trait/enum/API公開 + pub use)。
- [ ] 分離ごとに jit-direct 3本debug/releaseスモーク再確認
- [ ] LowerCore の段階分割`analysis.rs` / `cfg.rs` / `ops.rs`検討別PRでも可)。
- 実行メモJIT
- Build: `cargo build --release --features cranelift-jit`
- jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 診断: `NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_BLOCKS=1 NYASH_JIT_TRACE_RET=1` 併用可
Update (2025-09-01 PM2 / Interpreter parity blockers)
- 目的: Semantics 層での VM/JIT/Interpreter パリティ検証に向けInterpreter 側の既知不具合を記録引き継ぎ
- 再現ファイルユーザー提供
- examples/semantics_test_branch.nyash
- 期待: 100
- 実際(Interpreter): IntegerBox(4)変数IDが返る挙動
- 実際(VM): 100
- examples/simple_test.nyash
- 事象: PluginBoxV2 同士の加算でエラーIntegerBoxダウンキャストのみを試行して失敗
- /tmp/test_string_concat.nyash
- 事象: 文字列連結でエラーPluginBox String の連結が不可
- 現状の暫定対応実装済み
- Await(JIT): I::Await nyash.future.await_h に降下同期get
- Safepoint(JIT): I::Safepoint nyash.rt.checkpoint に降下global_hooks 経由で GC/scheduler 連携
- Barrier(JIT): Array.set/Map.set nyash.gc.barrier_write emit
- Interpreter: FutureBox を登録new FutureBox(42)
- Interpreter: 加算まわりのフォールバック強化
- 文字列likeの優先連結toUtf8/Result.Ok含む
- 数値文字列整数にパースできる場合は加算
- 未解決最優先
1) 返り値が変数IDになるexamples/semantics_test_branch.nyash
- 調査方針: execute_statement(Return)→execute_function_call の伝播経路Variable 解決/共有の箇所を追跡しBox 値ではなく内部ID/インデックスを返している箇所を特定修正
- 対応: Return 直前と関数エピローグでの実値/型ログ限定ログを差し込み最小修正
2) PluginBox 同士の演算の包括対応
- 暫定は toString数値/文字列へ正規化で回避恒久対応は Semantics/VM と同じ規約handle-first + 文字列like/数値likeに寄せる
3) 文字列連結の広範囲対応
- toString()/toUtf8/Result.Ok の内包を最優先で文字列正規化現状の強化で多くは通る追加ケースが出れば順次取り込み)。
- 次アクションInterpreter fix plan
- [ ] semantics_test_branch 再現Return 値の実体化ルート修正
- [ ] simple_test の演算パスを toString 正規化で網羅必要なら算術 toNumber
- [ ] test_string_concat の失敗パターン収集 try_box_to_string の対象拡張
- [ ] SemanticsVM/ClifAdapter パリティ小スモーク追加分岐/配列/extern/await
Update (2025-09-02 late / jit-direct TLS単一FBリファクタ step-2 部分反映)
- Runner 小リファクタ計画どおりの分割
- `src/runner.rs` `src/runner/mod.rs` に移動
- `NyashRunner::run()` `run_task` 重複分岐を削除無害な重複除去)。
- jit-directCraneliftBuilder: 単一FunctionBuilderTLS化の前進
- ローカルstore/loadをTLS単一FB経由に統一
- `switch_to_block`: 未終端のまま別ブロックへ切替時に `jump` 注入`cur_needs_term=false` に更新
- `br_if_with_args`: TLS単一FBで発行し分岐を明示的に終端扱い`cur_needs_term=false`)。
- エントリを `seal_block(entry)` 済みに変更入口でのPHI完了を前提)。
- ブロックパラメータPHI: その場のappendはやめて必要数を記録」→`switch_to_block` 前に不足分をappendする方式に変更命令追加後のparam禁止アサート回避)。
- `end_function`: 未sealedブロックを最終sealするフェーズを追加内部追跡セットで二重seal回避)。
- 現状の挙動
- `cargo build --features cranelift-jit` は通過警告あり)。
- `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 以前の未終端での切替アサートは抑止済み
- なお finalize 時に未sealed blockNが残るケースを観測例: block4)。seal の最終整合に取りこぼしがある
- 追加で必要な作業次の小ステップ
1) LowerCore 側の `seal_block` 呼び出し順序の確認補強分岐/合流直後に seal されるよう統一)。
2) 生成ブロックが `self.blocks` に全て含まれることの確認ret_block を含む)。不足時は `begin_function` `pending_blocks` を尊重して作成
3) 最小スモークの緑化: `mir-branch-ret` 1, 続いて `mir-phi-min` 10, `mir-branch-multi` 1
4) 上記が通ったら終端注入最終sealの実装を整理不要な箇所の削減ログスイッチ固定)。
- 実行/検証メモ再掲
- ビルド: `cargo build --features cranelift-jit`
- 実行: `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 追跡: `NYASH_JIT_TRACE_BLOCKS=1`ブロック切替/`NYASH_JIT_TRACE_RET=1`返り値
開発メモ / 注意点
- 分岐の取り違えはブロックまたぎの共有スタックが原因になりがち根治策として BlockParam 経由の合流単一出口 return を徹底
- デバッグログは 必要時のみ ON の方針で仕込む収束後に不要箇所は落とす本番は静かに)。
チェックリスト収束条件
- [ ] jit-direct: `mir-branch-ret` 1tag=201/200 とも 1
- [ ] jit-direct: `mir-phi-min` 10合流経路で BlockParam 値が正しい
- [ ] jit-direct: `mir-branch-multi` 1ネスト分岐でも合流が正しい
- [ ] 統合JIT--backend craneliftと一致
- [ ] ログ一時コードの整理必要なものだけ残す
- 既知の注意jit-direct 経路のみ
- 条件分岐系Branch/Jumpで戻り値が 0 になる事象を確認`--backend cranelift`統合経路では期待値どおり例: mir-branch-ret 1)。
- 影響範囲: jit-direct 実験フラグのみLowerCore/CraneliftBuilder IR 自体は生成されており統合経路では正しく実行される
---
最終確認apps フォルダ tribackend 実行計画 / 実行ログ用
対象: C:\git\nyash-project\nyash\apps 下の各アプリインタープリターVMJIT(exe)
前提
- ビルド: `cargo build --release --features cranelift-jit`
- 実行パスLinux/Mac: `./target/release/nyash`
- 実行パスWindows PowerShell: `target\release\nyash.exe`
- 3モードの呼び分け:
- Script(インタープリター): `nyash apps/APP/main.nyash`
- VM: `nyash --backend vm apps/APP/main.nyash`
- JIT(exe): `nyash --backend vm --jit-exec --jit-hostcall apps/APP/main.nyash`
補足/既知
- 一部アプリは CLI 入力/標準入力/環境定数に依存例: ny-echo の標準入力NYASH_VERSION の参照)。必要に応じて簡易入力をパイプで与えるか定数をスクリプト先頭に仮定義して実行確認する
- PluginOnly 強制は apps では無効化するtoString 経路が PluginInvoke 固定になると出力整形に影響)。
進め方手順テンプレート
1) 共通ビルド
- [ ] `cargo build --release --features cranelift-jit`
2) アプリごとに 3 モード実行下記テンプレートをコピーして使用
テンプレート各アプリ用
- アプリ名: <app-name>
- Script: `nyash apps/<app-name>/main.nyash`
- [ ] 実行OK / 出力: <貼付>
- VM: `nyash --backend vm apps/<app-name>/main.nyash`
- [ ] 実行OK / 出力: <貼付>
- JIT(exe): `nyash --backend vm --jit-exec --jit-hostcall apps/<app-name>/main.nyash`
- [ ] 実行OK / 出力: <貼付>
- 備考: 例)標準入力が必要 → `echo "Hello" | ...`、定数 `NYASH_VERSION` を仮定義 等
対象アプリ(初期リスト)
- ny-echo
- 入力例Script: `echo "Hello" | nyash apps/ny-echo/main.nyash`
- 入力例VM: `echo "Hello" | nyash --backend vm apps/ny-echo/main.nyash`
- 入力例JIT: `echo "Hello" | nyash --backend vm --jit-exec --jit-hostcall apps/ny-echo/main.nyash`
- 既知: `NYASH_VERSION` を参照するため、未定義時はエラー。必要ならスクリプト先頭で仮定義(例: `version = "dev"`)して確認。
- ny-array-bench
- Script/VM/JIT で 3 モード実行し、処理完了を確認(所要時間・出力サマリを記録)。
- ny-mem-bench
- Script/VM/JIT で 3 モード実行し、処理完了を確認(所要時間・出力サマリを記録)。
クロスチェック(簡易スクリプト)
- 補助: `tools/apps_tri_backend_smoke.sh apps/ny-echo/main.nyash apps/ny-array-bench/main.nyash apps/ny-mem-bench/main.nyash`
- 3 モードの Result ライン要約を出力(インタラクティブ入出力が必要なものは手動実行を推奨)。
ゴール/合格基準
- 各アプリで Script/VM/JIT(exe) の 3 モードがクラッシュ無しで完走し、期待する出力/挙動が観測できること。
- 不一致/未定義エラーが出た場合は「備考」に記録し、必要に応じて最小限の仮定義(標準入力や定数)での再実行結果も併記する。
- 次回対応: brif 直後のブロック制御/シール順の見直しentry/sealing、条件値スタック消費タイミングの再点検。
Update (2025-09-01 night / JIT-direct branch/PHI fix)
- Summary
- Fixed jit-direct returning 0 for branches by unifying returns to a single-exit ret block and improving MIR return type inference (including PHI-based inference).
- Stabilized PHI joins by materializing PHI results into locals and preferring local loads for Return.
- Corrected stack order for `br_if_with_args` (pop else args → then args → cond), matching LowerCore push order; added minimal fallback when predecessor mapping is unavailable.
- Added debug symbols and logs (gated by env) for ret path and CFG wiring.
- Code touched
- MIR: `src/mir/builder.rs` (return type inference incl. PHI inputs)
- Lowering core: `src/jit/lower/core.rs` (PHI materialize to local; arg wiring fallback; small traces)
- Builder (Cranelift): `src/jit/lower/builder.rs` (single-exit return; br_if/jump args order; debug symbol registrations)
- New smokes
- `apps/tests/mir-phi-two` … merge two locals (x,y) then add
- `apps/tests/mir-nested-branch` … nested branches returning constants
- Status (jit-direct)
- mir-branch-ret → 1
- mir-phi-min → 10
- mir-phi-two → OK合流加算
- mir-nested-branch → 1
- LLVM AOT snips (prebuilt binaries) still OK for VInvoke samples.
- How to run
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-min/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-two/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-nested-branch/main.nyash`
- Optional logs: `NYASH_JIT_DUMP=1` and/or `NYASH_JIT_TRACE_RET=1`
- Next (small boxes; avoid saturation)
1) Fast-path(select) 1-hop extension only:
- If then/else each jump to a block that immediately returns a Const Integer, lower to `select(cond, K_then, K_else) → return`.
- Keep scope narrow to avoid regressions.
2) Multi-PHI (limited):
- Add one smoke with two PHI slots; confirm arg counts/ordering under `phi_min`.
3) Logging remains env-gated; no default noise. No broad refactors until the above are green.
Update (2025-09-02 / jit-direct FB lifecycle refactor)
いま動くもの
- Interpreter/VM/MIR の基本スモーク: OK
- await の Result.Ok/Err 統一: Interpreter/VM/JIT で整合
- Cranelift 実行(`--backend cranelift`: OK例: `mir-branch-ret` → 1
いま詰まっている点(要修正)
- jit-direct で Cranelift FunctionBuilder が「block0 not sealed」でパニック
- begin/end のたびに短命の FunctionBuilder を作って finalize している設計が、最新の Cranelift の前提(全ブロック seal 済みで finalizeと合っていない
- 単一出口ret_block方針は Cranelift 側に途中まで入っているが、ObjectBuilder と二重実装があり、Cranelift 側の finalize 前にブロックを seal しきれていない箇所が残っている
直近の変更(対策の第一歩)
- CraneliftBuilder
- return は ret_block へ jumpエピローグで最終 returnに変更単一出口に合わせて安全化
- entry block の seal を begin_function で実施
- end_function 最後に blocks/entry/ret の seal を実施
- ObjectBuilder
- emit_return は従来通りダイレクト returnret_block を持たないため)
現状の評価
- 上記を入れても FunctionBuilder finalize のアサーションは残存。
- jit-direct の builder ライフサイクル(複数回 finalize する設計)そのものを見直す必要あり。
次の実装(推奨順)
1) CraneliftBuilder のビルドモデルを単一 FunctionBuilder 方式へ
- 関数スコープで1つの FunctionBuilder を保持し、lower 中は finalize しない
- switch/jump/phi/hostcall も同一 FB で emit現状の都度 new/finalize を撤廃)
- seal は then/else/target を LowerCore 側からタイミング良く呼ぶend_function で最終チェック
2) jit-direct での AOT emit パスObjectBuilderは現状通りだが、strict 判定を整理
- `mir-branch-ret` のような最小ケースは unsupported=0 を確実に維持
- まずはこの1本で .o 生成→リンク→EXE 実行を通す
3) ツールチェイン側(`tools/build_aot.sh`)の strict モードヒントを活かしつつ、上記の最小成功ケースを CI スモークに追加
全側で続けてこのリファクタに着手。まずは FunctionBuilder のライフサイクル一本化から進め、`mir-branch-ret` の AOTEXE生成・実行まで通し切る。
# (以下、旧タスク: Phase 10.8 記録)
Contributor note: 開発手順・レイアウト・PR要件はリポジトリルートの `AGENTS.md`Repository Guidelines参照。ビルド/テストやCIスモークの環境変数も簡潔にまとまっています。
## Handoff (Phase 11 next): 実行確認プランtests/ は対象外)
目的: apps 配下の「簡単なアプリ」から順に、VM → AOT(LLVM) の順で確実に動作させる。tests/ フォルダは除外。
前提/共通
- LLVM: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix)` を付与してビルド
- AOTリンク: `tools/build_llvm.sh <app.nyash> -o app` を使用(`target/aot_objects/*.o` 固定)
- Verbose: トラブル時は `NYASH_CLI_VERBOSE=1`
- プラグインテスター: `tools/plugin-tester` を利用可(`cargo run --release -- check --config ../../nyash.toml --library <lib>`
推奨テスト順(簡単→段階的)
1) apps/ny-array-llvm-ret/main.nyashArray push/get 戻り値)
- 期待: `Result: 3`
- VM: `./target/release/nyash --backend vm apps/ny-array-llvm-ret/main.nyash`
- AOT: `tools/build_llvm.sh apps/ny-array-llvm-ret/main.nyash -o app && ./app`
2) apps/ny-vinvoke-llvm-ret-size/main.nyashby-id size
- 期待: `Result: 1`
- VM/AOT 同上
3) apps/ny-vinvoke-llvm-ret/main.nyashby-id get 可変長経路)
- 期待: `Result: 42`
- VM/AOT 同上
4) apps/ny-echo-lite/main.nyashreadLine → print 最小エコー)
- 期待: 出力に `Result:` 行が含まれるllvm_smoke の基準)
- 実行例: `echo hello | ./target/release/nyash --backend vm apps/ny-echo-lite/main.nyash`
- AOT: `tools/build_llvm.sh apps/ny-echo-lite/main.nyash -o app_echo && echo hello | ./app_echo`
5) apps/ny-llvm-smoke/main.nyashArray get/set/print
- 期待: `Result: 3`
- 備考: 文字列連結は NyRT concat シムへフォールバック済み。emit 検知が不安定な場合は再試行。問題が残る場合はこの項を後回し可。
6) apps/ny-echo/main.nyashオプション付きエコー
- 期待: VM/JIT/AOT の出力一致upper/lower/そのまま)
7) apps/ny-map-llvm-smoke/main.nyashMap by-id 経路)
- 期待: 行に `Map: v=42``size=1`
- 備考: 連結シム適用済み。必要なら `NYASH_LLVM_ALLOW_BY_NAME=1` で一時回避。
トラブルシュート要点
- AOT emit: `NYASH_LLVM_OBJ_OUT=$PWD/target/aot_objects/<name>.o ./target/release/nyash --backend llvm ...`
- リンク: `NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT=... tools/build_llvm.sh ...`
- プラグイン: `nyash.toml` のパス解決拡張子はOSで異なる。tester は拡張子補完に対応。
Update (2025-08-31 AM / Phase 11.1 quick pass)
Update (2025-08-31 PM / Phase 11.2 partial)
- 方式ALLVM専用 NyRT 静的ライブラリで前進。by-id を本線、by-name はデバッグ用ラッパ方針。
- Lowering 更新
- NewBox引数あり 12個`nyash.box.birth_i64(type_id, argc, a1, a2)`int/handle ptr の最小対応。0引数は `birth_h`
- BoxCallby-id, method_idあり`nyash_plugin_invoke3_i64(type_id, method_id, argc, a0, a1, a2)` 接続a0=receiver handle
- 戻り: dstが整数/真偽ならi64のまま、Box/String/Array等は i64(handle)→i8*(ptr)
- ArrayBox.get/set は既存の `nyash_array_get_h/set_h` 安全パスを存続
- 生成関数名: `ny_main` に変更NyRTの起動ルーチンから呼び出し
- NyRT(libnyrt.a) 追加シンボル
- `nyash_string_new(i8*, i32)->i8*`Const String用
- `nyash_array_get_h(i64,i64)->i64`, `nyash_array_set_h(i64,i64,i64)->i64`
- 既存の `nyash.box.birth_h/i64`, `nyash.rt.checkpoint`, `nyash.gc.barrier_write` などは維持
- ツール
- `tools/build_llvm.sh` 追加(.o → libnyrt.a リンク → EXE
- `tools/llvm_smoke.sh`.o生成のスモーク
- スモーク
- `examples/llvm11_core_smoke.nyash` で EXE 実行し `Result: 3` を確認
Update (2025-08-31 PM2 / Phase 11.2 lightweight LLVM)
- 方針(拡張性優先 / コア最小化: Tier0
- ExternCall: 環境/I/Oのみenv.console/debug/runtime など。print は ExternCall(env.console.log) 本線。
- BoxCall: データ構造/Box 操作Array/Map/String/Instance 等。AOT/LLVM は最小の安全シムのみ直結。
- コアに残す安全シムNyRT: Array(get/set/push/length), Instance(getField/setField)。Map はコアに足さない(後述)。
- Map/JSON/Math 等は当面コア外(必要時はプラグイン byid + 汎用シムでAOTを通す
- 実装・反映
- MIR パス: `passes/method_id_inject` 追加NewBox/Copy 由来の型から BoxCall に method_id 注入。PluginInvoke は可能なら BoxCall(byid)へ書換)。
- LLVM Lowering:
- ExternCall: `env.console.log``nyash.console.log`NyRT, `env.debug.trace``nyash.debug.trace`
- ExternCall: `env.console.readLine` 追加 → `nyash.console.readline`stdin 1行, CR/LF 除去, C文字列返却
- ArrayBox: `get/set/push/length` を NyRT 安全シム(`nyash_array_get_h/set_h/push_h/length_h`)に直結。
- Instance: `getField/setField` を NyRT 安全シム(`nyash.instance.get_field_h/set_field_h`)に直結(既存)。
- プラグイン byid: f64 戻りの選択(`nyash_plugin_invoke3_f64`/ i64 戻り(`..._i64`。先頭2引数はタグ付けint/float/handle対応`..._tagged_i64`)。
- byname 薄フォールバック(デバッグ用): `NYASH_LLVM_ALLOW_BY_NAME=1` 下で `nyash.plugin.invoke_by_name_i64` を使用。
- NyRT(libnyrt.a): 上記 API を追加console.log/debug.trace/readline, array push/length, instance get/set_field, byname, tagged_i64
- オブジェクト出力: `NYASH_LLVM_OBJ_OUT=<path>` で .o を明示出力(`runner/modes/llvm.rs` 経由)。
- ツール更新: `tools/build_llvm.sh` / `tools/llvm_smoke.sh``NYASH_LLVM_OBJ_OUT` を使用して .o を取得後、NyRT とリンク。
- サンプル: `apps/ny-llvm-smoke/main.nyash`Array get/set/print`apps/ny-echo-lite/main.nyash`readLine→print
- しないこと / 後回し
- Map のコア安全シム追加(`nyash_map_*_h`)は見送り。必要ならプラグイン byid + 汎用シムでAOT実行。
- ConsoleBox の高度機能は ExternCall 側で段階導入(出力は既存 log、入力は readline のみ)。
- 汎用可変長引数(>2は後段タグ付けの拡張で対応予定
- 次にやること(短期)
- ny-echo を縮小AOT対応console.readLine + print のみで OK 版)。
- Mapプラグイン最小版で string-key の get/set/size を byid 汎用シム経由でAOT実行コアは増やさない
- CI/スモーク: `.o→EXE→実行``apps/ny-llvm-smoke` / `apps/ny-echo-lite` で追加。
- ガードレール: コア安全シムを Tier0 以外に増やさない簡易チェックgrep ベース)導入検討。
- How to Build / RunAOT/LLVM
- ビルド: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
- .o→EXE: `tools/build_llvm.sh <file.nyash> -o app_llvm``./app_llvm`
- .o のみ: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm <file.nyash>`
- 推奨 @envmain.nyash 冒頭コメント):
- `// @env NYASH_CLI_VERBOSE=1`(詳細ログ)
- デバッグ時のみ `// @env NYASH_LLVM_ALLOW_BY_NAME=1`
Update (2025-08-31 PM3 / LLVM VInvoke triage)
- 直近のテスト結果(要約)
- VM backend正常
- MapBox.size(): Result: 1 ✅
- MapBox.get(1): Result: 42 ✅
- 可変長VInvoke: get(1,9,8,7,6): 期待どおりにTLV I64(tag=3)でエンコードされ、出力確認済 ✅
- LLVM backend要修正
- MapBox birth は成功instance_id=1
- set()/get() 呼び出し経路で戻り値が Result: 0期待は 42
- LLVM 実行時に VM 相当の PluginLoaderV2 Invoke 詳細ログが出ず、by-id/可変長の値流しに不整合がある可能性
- 実装・修正状況
- ランタイムNyRT
- by-id 固定長/可変長の argc を「レシーバ除外の実引数個数」に統一済み
- invoke 時の type_id を「レシーバ実体PluginBoxV2から取得した実 type_id」に変更呼び出し先揺れを排除
- LLVM Lowering
- <=4 引数: nyash_plugin_invoke3_tagged_i64f64→i64ビット化タグ付与
- >=5 引数: nyash.plugin.invoke_tagged_v_i64vals/tags の vector 経路)
- スモーク
- apps/ny-vinvoke-llvm-ret: 戻り値で 42 を検証する LLVM 用スモークを追加print/concatに依存しない
- tools/llvm_smoke.sh に VInvoke戻り値スモークをオプション追加NYASH_LLVM_VINVOKE_RET_SMOKE=1
- いま見えている課題LLVM
- by-id vector 経路vals/tags/argcのどこかで齟齬 → get が 0 を返す(キーが TLV 化されていない/タグずれなど)
- 固定長(<=4経路は未切り分け → まず size()/get(1) を LLVM 戻り値で確定size→1, get(1)→42
- .o 出力の一部環境不安定は Runner/Lowering にフォールバックを追加済write_to_memory_buffer
- 次アクション
1) LLVM 戻り値テストを段階確認
- MapBox.size() → Result: 1
- MapBox.get(1) → Result: 42
- これで固定長 by-id の健全性を確定後、可変長(>=5vector 経路へ絞り込み
2) NyRT デバッグNYASH_CLI_VERBOSE=1 時のみ)を最小追加
- nyash.plugin.invoke_tagged_v_i64: argc/method_id、vals[0..2], tags[0..2] を stderr に出力
- 実際のTLV化の前に観測し、ズレ箇所を確定
3) LLVM Loweringvector 経路)の配列構築を点検
- alloca([N x i64]) → inbounds GEP → store → i64* へ pointer_cast → 呼び出し
- GEP index[0,i]/型一致/メモリ幅を再確認
4) 必要なら一時的に「<=4 でも vector 経路を選択」する実験分岐を作り、経路差異を切り分け
5) tools/llvm_smoke.sh: target/aot_objects 固定・事前 emit → link のフローをデフォルト強化CI安定化
- ゴール(本フェーズ収束条件)
- LLVM backend で MapBox: size()/get(1)/get(1,9,8,7,6) が戻り値ベースで一致
- VInvoke可変長経路が by-id で安定
- print/concat のLoweringは後続必要最小に回す
- タスク実行MVP
- `nyash.toml``[env]` / `[tasks]` を記述し、CLIから `--run-task <name>` で実行可能。
- 例:
- `[tasks] build_llvm = "LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm"`
- 実行: `./target/release/nyash --run-task build_llvm`
- 備考: `{root}` 変数展開対応。OS別/依存/並列は未対応(将来拡張)。
残作業(合意順)
1) method_id 埋め込みと by-id 本線化
- ロード時に 名前→id を確定・キャッシュPluginLoaderV2し、MIR へ `method_id` 注入(実行時は常に by-id
2) BoxCall 汎用拡張
- 引数3個以上/戻り型の拡張i64/handle/f64 等。Field 系getField/setFieldを BoxCall として安全パス接続
3) by-name ラッパ(デバッグ/テスト用)
- Lowering フォールバックとして薄く導入env/flag 下でのみ使用)、本番は by-id 固定
4) ExternCall 網羅
- `env.console/debug/runtime/future` 等を puts 暫定から RT関数に置換、署名整備
5) スモーク/CI 拡張
- by-id の代表例CounterBox等、console/array/field/extern を .o→EXE→起動まで
- LLVM Lowering: Phi/Load/Store の最小実装を追加inkwell 0.5 / LLVM 18
- Phi: 事前に各BB先頭でPhiード生成→Branch/Jump時にincomingを配線
- Load/Store: entryでのalloca管理型は注釈/値から推定。i1/i64の簡易変換、ポインタはpointer_cast対応
- 型なしポインタopaque対応のため、`alloca`の要素型を別マップで追跡
- ビルド検証: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --features llvm` 成功
- 既存のConst/Unary/BinOp/Compare/Branch/Jump/Return と併せ、Phase 11.1の目標範囲は到達
---
一時メモMIRインタプリタ 80/20 方針)
- ConsoleBox.readLine ローカルフォールバック標準入力1行読みを一時実装`--backend mir` の echo-lite 用)。
- 後で必ず削除し、プラグイン側メソッド/Extern 経由に置換すること。
- 追跡タスク: 「ConsoleBox メソッド群の正式実装PluginInvoke/ExternCallとローカルフォールバックの撤去」。
- 影響箇所: `src/backend/mir_interpreter.rs`BoxCall: ConsoleBox.readLine 分岐)、`apps/tests/ny-echo-lite` スモーク。
次アクション(この項に紐づく)
- ClifSem 最小LoweringConst/Return/Addを実装し、JITスケルトンの0終了スモークを追加。
- ConsoleBox フォールバックは温存のままecho-liteのみのため。Console 正式化のタイミングで除去。
---
# Handoff (Phase 11.7) — MIR Interpreter + Cranelift Minimal JIT
目的: LLVMが重くなったため仕切り直し。新しいMIR解釈層と軽量Cranelift JITの最小機能を整備し、段階拡張しやすい骨格を確立。
実装済み(要点)
- 共通ABI/ユーティリティ:
- `src/backend/abi_util.rs`to_bool/eq/tag/handle
- MIRインタプリタ--backend mir:
- 対応: Const/Unary/BinOp(String結合)/Compare/Load/Store/Copy/Branch/Jump/Return/Print/Debug/Barrier/Safepoint(no-op)
- NewBox/PluginInvoke/BoxCall/ExternCallの最小対応PluginBoxV2はプラグインホスト経由
- ConsoleBox.readLine: 一時フォールバックで標準入力1行読みCURRENT_TASKに削除タスク記載済
- Cranelift最小JIT--backend cranelift:
- 実JITjit.rs: Const(i64/f64/bool->0/void->0)/Add/Sub/Mul/Div/Mod、Compare(Eq/Ne/Lt/Le/Gt/Ge)、Load/StoreStackSlot、Copy、Return/Jump/Branch
- 箱化: `src/backend/cranelift/context.rs` に ClifContext/BlockMap/ValueEnv を用意JIT構築をカプセル化
- LowerCore→ClifBuilderIRBuilder実体: 録画→実IR生成Const/Add/Returnを暫定実装
- 起動切替: `NYASH_JIT_LOWERCORE=1` で LowerCore→ClifBuilder 実IR経路を実行
- スモーク:
- `apps/tests/mir-const-add/main.nyash`0終了
- `apps/tests/mir-branch-ret/main.nyash`条件分岐で1
- どちらも --backend cranelift / --backend mir で確認済
使い方(コマンド)
- Cranelift有効ビルド: `cargo build --features cranelift-jit`
- MIRインタプリタ: `./target/debug/nyash --backend mir apps/tests/mir-const-add/main.nyash`
- Cranelift最小JIT: `./target/debug/nyash --backend cranelift apps/tests/mir-branch-ret/main.nyash`
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1 ./target/debug/nyash --backend cranelift apps/tests/mir-const-add/main.nyash`
次のタスク(推奨順)
1) ClifBuilder 実IR生成の拡張LowerCore連携
- Compare/Branch/Jump の実IR
- 最小PhiBlock Params
- StackSlotベースのLoad/StoreValueEnvから完全移行
2) ExternCallenv.console.log最小対応JIT経路でもprintln相当へ
3) スモーク追加: Load/Store、Phi最小ケース、Compare/Branch複合ケース
4) Console/Extern 正式化完了後に ConsoleBox.readLine フォールバック削除(本ファイルにタスク済)
5) 警告/CFG整理: 使っていないfeature cfgやunusedを段階的に整理
既知の注意/制限80/20の割り切り
- BoolはI64の0/1として扱っており、B1専用ABIは未導入将来拡張
---
Update (2025-09-02 night / jit-direct TLS単一FBリファクタ 進捗・引き継ぎ)
- 目的: jit-direct の Cranelift FunctionBuilder ライフサイクルを「関数ごとに1つ」に統一し、finalizeは end_function の一度のみとするCraneliftの前提に整合
- 実装済み(最小スコープ)
- TLSに Context/FBC/FunctionBuilder を保持begin_functionで生成→end_functionでfinalize
- per-op finalize の撤去。主要経路const/binop/compare/select/branch/jump/return/hostcall 等)を TLS 単一FB に切替中。
- 単一出口ret_block + i64 block param維持。emit_return は ret_block へ jump、end_function で epilogue return を生成。
- prepare_blocks は begin_function 前はTLSに触れず pending_blocks に貯め、begin_function で create_block。
- host/import 呼び出しは tls_call_import_ret/tls_call_import_with_iconsts ヘルパへ分離module.declare_func_in_func + call を安全化)。
- 未終端ブロック切替の安全弁: IRBuilder::switch_to_block に「未終端なら jump 注入」cur_needs_termを導入。
- 現状ステータス
- cargo build --features cranelift-jit: OK
- jit-direct 実行: まだ1箇所「you have to fill your block before switching」未終端での block 切替)アサートが残存。
- 再現: `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 多くの switch_to_block は closure から排除済みだが、特定条件下で未終端のまま切替が残っている模様。
- 次の小ステップ(箱を下に積む順)
1) IRBuilder::switch_to_block の重複抑止(同一 index への再切替は no-op
2) cur_needs_term の更新確認emit_return/br_if/jump 後は必ず false。主要箇所は反映済みだが再点検。
3) emit_* 内の残存 switch_to_block を整理(挿入点は LowerCore 側の switch_to_block に一本化)。
4) トレースで最終合流ret_block直前の切替を観測
- 環境: `NYASH_JIT_TRACE_BLOCKS=1 NYASH_JIT_TRACE_BR=1`
5) スモークjit-directを順に通す:
- `mir-branch-ret` → 1
- `mir-phi-min` → 10
- `mir-branch-multi` → 1
6) hostcall_typed / plugin_by_name の TLS 呼び出し統一(未対応部分があれば最小限で補完)。
- 実行/検証メモ
- ビルド: `cargo build --features cranelift-jit`
- 実行: `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 追跡ログ: `NYASH_JIT_TRACE_BR=1`brif出力`NYASH_JIT_TRACE_BLOCKS=1`block切替通知
- 影響範囲
- jit-directCraneliftBuilder限定。ObjectBuilderAOT .o生成は従来通り。
- docs/development/roadmap/phases/phase-11.7_jit_complete 配下のフェーズ文書・計画は維持(削除・変更なし)。
備考
- まずは TLS 方式で単一FBモデルを安定化動かすことを最優先。その後、余力があれば IRBuilder/LowerCore に FB を明示渡しするクリーン版へ段階移行を検討。
- String/Null/Void のConstは暫定的に0へ丸め必要箇所から段階的に正規化
- `jit-b1-abi` 等のunexpected cfg警告は今後整理対象。
関連ファイル
- 追加: `src/backend/abi_util.rs`, `src/backend/mir_interpreter.rs`, `src/runner/modes/mir_interpreter.rs`
- 追加: `apps/tests/mir-const-add/main.nyash`, `apps/tests/mir-branch-ret/main.nyash`
- Cranelift: `src/backend/cranelift/jit.rs`, `src/backend/cranelift/context.rs`, `src/backend/cranelift/builder.rs`
- Semantics: `src/jit/semantics/{mod.rs,clif.rs}`(スケルトン)
メモ/トグル
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1`
- カバレッジログ: `NYASH_JIT_DUMP=1`
Handoff Snapshot (2025-08-31 / Phase 11 kick-off)
- Core-15 凍結(第三案 / Box-SSA
- セット: { Const, UnaryOp, BinOp, Compare, TypeOp, Load, Store, Jump, Branch, Return, Phi, Call, NewBox, BoxCall, ExternCall }
- Optimizer: ArrayGet/ArraySet/RefGet/RefSet/PluginInvoke → BoxCall に正規化get/set/getField/setField
- Verifier: 上記レガシー命令を UnsupportedLegacyInstruction としてエラー化(環境で一時解除可: NYASH_VERIFY_ALLOW_LEGACY=1
- VM: BoxCall("getField"/"setField") を InstanceBox に配線fieldsへ委譲。Arrayの get/set は既存BoxCall経路で動作
- 命令数固定テスト: Core15第三案へ切替済tests/mir_instruction_set_sync.rs
- LLVM 導入Phase 11 開始)
- 依存: LLVM 18 + inkwell 0.5.0features=["llvm18-0"]。feature `llvm` で有効化
- ビルド要件: LLVM_SYS_180_PREFIX例: /usr/lib/llvm-18, 追加依存: polly, zstdlibzstd-dev 等)
- 現状のLowering11.1の最小スケルトン → 11.2 反映):
- 対応: Const(Integer/Float/Bool/String/Null), Unary(Neg/Not/BitNot), BinOp整数/浮動の主要演算), Compare, Branch/Jump, Return
- 追加: Phi/Load/Store最小実装
- 追加: NewBox引数なし→nyash.box.birth_hへ; nyash.tomlの[box_types]からtype_id解決
- 追加: BoxCallArrayBox.get/set→nyash_array_get_h/set_h 経由の安全パス)
- 追加: ExternCallenv.console.log/env.debug.trace→libc putsで暫定出力
- 未対応(次タスク): NewBox引数あり, 一般BoxCallby-name/slot 汎用化), その他ExternCall
- エントリ: Main.main のみ対象に .o 出力backend::llvm::compile_to_object
- ドキュメント更新phase11
- README.md: 進行中に更新 / 4週スプリント計画11.1→11.4
- MIR_TO_LLVM_CONVERSION_PLAN.md: PluginInvoke→BoxCall統一、配列はBoxCallとして安全パス→型特化の二段階Lowering
- MIR_ANNOTATION_SYSTEM.md: setField/getFieldBoxCall前提に更新
- INSTRUCTION_SET.md: PluginInvokeはDeprecatedBoxCallに統一
How to Build/Run (recap)
- 通常/JIT: `cargo build --release --features cranelift-jit`
- LLVMAOTスケルトン:
- 事前: LLVM 18 / inkwell 0.5.0, polly, zstd を導入
- 例: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
- スモーク: `tools/mir15_smoke.sh release`
Next Steps (Phase 11)
1) 11.1 仕上げ(本タスク)
- Phi のLoweringBB事前作成→incoming追加
- Load/Storealloca/ローカル表現の最小規約、整数/浮動/ポインタ)
2) 11.2 安全パスBox/Extern
- [実装] NewBox(引数なし)→ `nyash.box.birth_h(type_id:i64)->i64` を呼び、i8*にinttoptrtype_idはnyash.tomlから解決
- [実装] Arrayの BoxCall("get"/"set") → `nyash_array_get_h/set_h`ハンドルi64渡し
- [実装] ExternCall: `env.console.log`/`env.debug.trace` は暫定で `puts` に接続AOTデバッグ用
- [残] BoxCall 汎用by-name/slot, Field系getField/setField, ExternCallの網羅
3) 11.3 最適化導線
- 注釈inline/purity/gc/alias→ LLVM属性/メタデータ
- 型特化: Array/Field の inline GEP + write barrier
4) 11.4 高度化
- 脱箱化、TBAA、PGO/ThinLTO
メモ
- Verifier の緩和スイッチ: `NYASH_VERIFY_ALLOW_LEGACY=1`移行用。通常はOFFで運用。
- Optimizer のRewrite はLLVM前提のBoxCall統一規約と整合済み。
最優先: MIR命令セットをCore-15に統一し、VM/JIT/AOTを整えてからLLVM(inkwell)へ移行する。
目的: MIR→VM→JIT→AOT の汎用化・単純化を一気に進める。命令の重複・メタ・実装露出を撤去/統合し、Builderが実際に発行するコア命令を最小化する。
現状観測2025-08-31
- 実装定義: 37命令src/mir/instruction.rs
- Docs: Canonical 26移行注記・Core-15ターゲット追記済
- Builder使用: 24命令自動集計
- 上位頻度: Const(19), TypeOp(13), Jump(6), ExternCall(5), Return(3), Call(3)
- 中頻度: NewBox(2), WeakRef(2), Barrier(2), BoxCall(1), Branch(1), Phi(1), PluginInvoke(1), Await(1)
- JITサポート: 約20命令ホストコール分は外部委譲で簡素
統合方針Core-15
- 重複統合
- TypeCheck, Cast → TypeOp に完全統合Builder 既に主に TypeOp を発行)
- WeakNew, WeakLoad → WeakRef に統合
- BarrierRead, BarrierWrite → Barrier に統合
- Box哲学へ移譲
- Print → env.console.log (ExternCall)Builder更新済
- Debug → DebugBox.trace()/env.debug.traceExternCall/BoxCall
- Throw, Catch → ExceptionBoxthrow/catch相当のBox APIへ移譲; 移行期はRewrite
- Safepoint → RuntimeBox.checkpointExternCall
- 未使用/メタ
- FutureSet → 一旦廃止Builder未使用
- Copy, Nop → メタ命令Optim/降格専用; Coreからは除外
最終ターゲット: Core-15 命令
- 基本演算(5): Const, UnaryOp, BinOp, Compare, TypeOp
- メモリ(2): Load, Store
- 制御(4): Branch, Jump, Return, Phi
- Box(3): NewBox, BoxCall, PluginInvoke
- 配列(2): ArrayGet, ArraySet
- 外部(1): ExternCall暫定; 将来はBox化でも可
進め方Core-15確定 → LLVM
1) Builder発行の一元化非破壊
- 既に Print→ExternCall 置換済み
- Builderが TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite を発行しないよう整理(既存箇所の差し替え)
- 既存テストbuilder/optimizerを ExternCall/TypeOp/WeakRef/Barrier に合わせて更新
2) 互換Rewriteパスの追加MIR最適化フェーズ
- 古いMIR手書き/スナップショット/ツール)が生成した命令をコア命令に機械的変換
- TypeCheck/Cast → TypeOp
- WeakNew/WeakLoad → WeakRef
- BarrierRead/Write → Barrier
- Print → ExternCall(env.console.log)
- Debug → ExternCall(env.debug.trace)
- Throw/Catch → ExternCall(env.exception.*) もしくは BoxCall(ExceptionBox)
- Safepoint → ExternCall(env.runtime.checkpoint)
3) VM/JITの段階撤去と整理
- VM/JIT: コア命令に集中(ホストコール/Box経由のI/Oや例外
4) Docs/CI
- INSTRUCTION_SET をCore-15へ更新26→15マッピング表
- 命令数固定テストを「15」に切替
タスク分解(本フェーズ)
- [x] Builderからのレガシー発行のデフォルト停止WeakNew/WeakLoad/BarrierRead/Writeを統一命令に切替、トグルで復活可
- [x] MIR Rewriteパス追加一部完了: Print/Type/Weak/Barrier、Debug/Safepointはトグル
- [ ] Optimizer/Verifier/Printerの非互換の見直しVerifierでレガシー命令をエラー化
- [ ] VM: レガシー命令のコードパスに警告ログ(将来削除フラグ)
- [ ] JIT: コア命令に合わせたテーブル整理(未使用ホストコールの棚卸し)
- [ ] Docs更新命令セット、移行ガイド、Box哲学との整合
- [ ] 回帰テスト更新builder_modularizedを含む一式
追加の即応ステップ10.8a
- [x] Rewrite: Print → ExternCall(env.console.log)
- [x] Rewrite: TypeCheck/Cast → TypeOp、WeakNew/WeakLoad → WeakRef、BarrierRead/Write → Barrier
- [x] Rewrite(トグル): Debug → ExternCall(env.debug.trace)NYASH_REWRITE_DEBUG=1
- [x] Rewrite(トグル): Safepoint → ExternCall(env.runtime.checkpoint)NYASH_REWRITE_SAFEPOINT=1
- [x] Builder: Try/Catch/Throw/Safepoint の直接発行を抑止(トグル導入・モジュール化系にも適用)
- [x] Runtime: extern_call スタブ追加env.runtime.checkpoint, env.debug.trace
- [x] Rewrite(トグル・スキャフォールド): FutureNew/FutureSet/Await → ExternCall(env.future.*) 変換NYASH_REWRITE_FUTURE=1
引き継ぎ2025-08-31 深夜)
サマリ
- JIT予約シンボル: `nyash.rt.checkpoint`, `nyash.gc.barrier_write` を確保AOT/JITの双方で登録
- Future/Await Rewrite: `NYASH_REWRITE_FUTURE=1` で ExternCall(env.future.*) に段階導入runtime最小実装あり
- Builderレガシー停止: Weak/Barrier 系の直接発行を既定で無効化(統一命令に集約、必要時トグル)
- JIT-direct安定化: entry seal/戻り値制御/シム登録/コード寿命を調整し落ち着かせ済み
- 次の着手順: MIR15のVM/JITカバレッジ拡張 → LLVM(inkwell) 移行
- 完了/修正
- JIT/AOT 予約シンボルの登録完了(`nyash.rt.checkpoint`, `nyash.gc.barrier_write`
- JITスタブ実装no-opトレースとAOT(nyrt)エクスポートを追加
- Future/Await Rewriteのスキャフォールド`NYASH_REWRITE_FUTURE=1` runtime側`env.future.*`最小実装
- Builderレガシー停止を既定化WeakNew/WeakLoad/BarrierRead/Write→統一命令。必要時はトグルで復活
- `NYASH_BUILDER_LEGACY_WEAK=1`, `NYASH_BUILDER_LEGACY_BARRIER=1`
- JIT directの安定化segfault修正
- エントリblockのseal遅延PHI/引数受け用)
- MIRシグネチャに基づく戻り値有無Void関数はret無し/void呼び出し
- `nyash.console.birth_h` のJIT内シム追加JITBuilder登録
- finalize済みコードの寿命延長JITModuleをリークして新モジュールに差し替え
- カバレッジ確認MIR15→VM/JIT
- 追加ドキュメント: docs/reference/mir/MIR15_COVERAGE_CHECKLIST.md
- スモークスクリプト: tools/mir15_smoke.sh実行例: `cargo build --release --features cranelift-jit``tools/mir15_smoke.sh release`
- 現状OKJIT-direct: BinOp, Compare(真偽戻り), Load/Store(ローカル), Branch/Jump/PHI最小, ExternCall(console.log)
- まだoptionalフォールバックで許容: 配列/Mapのhostcall系len/isEmpty/get/push 等)
実行メモ
- ビルド: `cargo build --release --features cranelift-jit`
- スモーク: `tools/mir15_smoke.sh release`
- AOT(.o)簡易出力: `NYASH_AOT_OBJECT_OUT=target/aot_objects ./target/release/nyash --jit-direct examples/aot_min_return_42.nyash`
- 生成: `target/aot_objects/main.o`
- 備考: `tools/build_aot.sh` は jit-direct で .o を生成するよう更新済(要検証)
LLVM足場VM側 先行)
- Escape AnalysisVMのみ: `NYASH_VM_ESCAPE_ANALYSIS=1`
- 非エスケープなBoxの `Barrier(Read/Write)` を保守的に `Nop`
- 実装: `src/mir/passes/escape.rs`NewBox起点のローカル追跡Return/Call/Store使用でescape検出
- 適用: VM実行前にMIRへ適用`src/runner/modes/vm.rs`
- 目的: LLVM(inkwell)への最適化ヒント連携を見据えた足固めまずVMで効果検証
- 次の着手(この順で)
1) MIR15のVM/JITカバレッジをもう一段拡張配列/Map hostcallのJIT対応 or optionalのまま明確化
2) スモークに代表サンプルを追加し、CI/ローカルでワンコマンド確認
3) LLVMフェーズinkwellへ移行Const/Return→BinOp/Compare→CF/PHIの順
次フェーズ提案
- まずMIR15のVM/JITを固めるhostcallはoptional許容 or 段階実装)
- その後、LLVMinkwellへ移行開始
MIRセット移行メモ
- 現行の参照ドキュメントは「26命令Canonical」を維持`docs/reference/mir/INSTRUCTION_SET.md`)。
- 実装はCore-15へ段階移行中TypeOp/WeakRef/Barrier 統合、Print Deprecatedで、MIR15のカバレッジは `MIR15_COVERAGE_CHECKLIST.md` で運用。
- Core-15が安定した時点で参照ドキュメント側を「15命令」に更新し、命令数固定テストも切替える。
- [x] JIT/AOT: 将来のGCバリア/セーフポイント用のシンボル予約nyash.gc.barrier_write, nyash.rt.checkpoint
環境変数(段階移行トグル)
- NYASH_BUILDER_SAFEPOINT_ENTRY=1: 関数エントリにSafepointを発行
- NYASH_BUILDER_SAFEPOINT_LOOP=1: ループ各回でSafepointを発行
- NYASH_BUILDER_LEGACY_WEAK=1: 旧WeakNew/WeakLoad発行を有効化既定: 無効、WeakRefに統一
- NYASH_BUILDER_LEGACY_BARRIER=1: 旧BarrierRead/Write発行を有効化既定: 無効、Barrierに統一
- NYASH_BUILDER_DISABLE_TRYCATCH=1: try/catch/finallyを無効化try本体のみ
- NYASH_BUILDER_DISABLE_THROW=1: throwをenv.debug.traceへフォールバック
- NYASH_REWRITE_DEBUG=1: Debug命令をExternCall(env.debug.trace)に書き換え
- NYASH_REWRITE_SAFEPOINT=1: Safepoint命令をExternCall(env.runtime.checkpoint)に書き換え
- NYASH_REWRITE_FUTURE=1: FutureNew/Set/Await を ExternCall(env.future.*) に書き換え(スキャフォールド)
- NYASH_DEBUG_TRACE=1: env.debug.traceのログをstderrに出力
- NYASH_RUNTIME_CHECKPOINT_TRACE=1: env.runtime.checkpointのログをstderrに出力
直近実装(完了)
- AOT/JIT: string-like hostcalls 実装concat_hh/eq_hh/lt_hhとLowerer経路、シンボル登録
- Print命令の非推奨化: BuilderでExternCall(console.log)へ統一、Rewriteでも変換
- Builderlegacy抑止のトグル: Safepoint/Try-Catch/Throwをトグル化、loop safepointも任意化
- Runtime extern_call: env.debug.trace / env.runtime.checkpoint を追加
次の着手(順序)
1. JIT/AOT: GCバリア/セーフポイントのシンボル予約と下準備nyash.gc.barrier_write, nyash.rt.checkpoint
2. Docs: 上記トグル/Extern API/命令マッピングの追記INSTRUCTION_SET, runtime extern, migration
3. Future/AwaitのRewriteスキャフォールドNYASH_REWRITE_FUTURE=1と最小実装方針の明文化完了
4. Builderのlegacy APIemit_weak_new/load, barrier_read/writeの非推奨化と使用箇所の削減
5. JIT directのBlock-Sealパニック修正block seal順序・entry sealの見直し
期待効果
- 命令 37→15目安で読みやすさ/実装コスト/検証コストを大幅削減
- JIT/AOT の対応面積が小さくなり、今回の string-like hostcall のような追加の導入が容易に
- 「Everything is Box」に合致I/O, 例外, ランタイム機能をBox/Externに集約
優先度/スケジュール
- 優先度: 最優先10.5c/10.7に割り込み)
- 目安: 1〜2日でBuilder/Rewrite/Docs、続いてVM/JITの掃除を段階投入
直近スナップショット2025-08-30 更新)
Current State
- Plugin-First/Handle-First/TLVはAOT/VMで安定10.5e完了状態を継続)
- 10.6計画Thread-Safety/Schedulerと10.7計画トランスパイルAll-or-Nothingを確定
- Nyash-onlyパイプラインtools/pycを開始Parser/CompilerはNyashで実装方針
- include式の最小実装を追加式でBoxを返す1ファイル=1static box
- インタプリタ: include式は実行時評価
- VM/AOT: MIRビルダーが取り込み先を同一MIRに連結MIR命令は増やさない
- nyash.tomlの[include.roots]でルート解決拡張子省略、index.nyash対応
- tools/pycをモジュール分割
- tools/pyc/pyc.nyashエントリ: includeでPyIR/PythonParserNy/PyCompilerを取り込み
- tools/pyc/PyIR.nyash, PythonParserNy.nyash, PyCompiler.nyashNyash-only実装
How To RunNyash-only
- VM: `NYASH_PY_CODE=$'def main():\n return 42' ./target/release/nyash --backend vm tools/pyc/pyc.nyash`
- 出力: Parser JSON → IRreturn 42→ 生成Nyashソース現状は骨組み
- include動作サンプル: `./target/release/nyash --backend vm examples/include_main.nyash`Math.add(1,2)=3
進捗2025-08-30 夜)
- include: 循環検出を追加(インタプリタ/VM収集器ともにロード中スタックで経路出力。examples/cycle_a/b で検証
- tools/pyc: 最小IRreturn定数→Nyash生成を通し、出力をprintまで接続
- 文字列基盤: VMにString統一ブリッジを着手内部StringBoxとプラグインStringBoxの比較互換、内部Stringメソッドのフォールバック
- 追加プラグイン(小粒・基底)
- RegexBoxcompile/isMatch/find/replaceAll/split: examples/regex_min.nyash
- EncodingBoxutf8/base64/hex: examples/encoding_min.nyash
- TOMLBoxparse/get/toJson: examples/toml_min.nyash
- PathBoxjoin/dirname/basename/extname/isAbs/normalize: examples/path_min.nyash
Next Steps優先順・更新
1. String統一ブリッジ実装済・一次完了
- VM: 比較/加算/代表メソッドのフォールバックlength/isEmpty/charCodeAt/concat/+をstring-like正規化で実装
- Interpreter: 比較/加算はstring-like正規化を適用メソッドは後続で最小追補があれば対応
- 例: encoding_min/regex_min/toml_min/path_min で回帰確認
2. AOT/JITへのブリッジ降ろしMIR→VM→JIT→exeの汎用性維持・ハードコーディング禁止
- 文字列演算のhostcall化read-only: nyash.string.concat_hh / eq_hh / lt_hh
- Lowerer: BinOp(Add) / Compare(Eq/Lt) を「string-like」判定時にhostcallへフォールバック
- 代表メソッド: length/isEmpty/charCodeAtは既存hostcall経由で維持、concat(メソッド)も追加検討
- Registry: 署名/権限ReadOnly登録、シンボル解決とJITビルダー登録
- 目標: examples/string_bridge_min.nyash をAOTでも成功
3. tools/pyc: IR→Nyashの反映強化return/If/Assignを安定化、Strictスイッチ連動
4. Strictスイッチ: tools/pycunsupported_nodes非空でErr、envでON/OFF
5. CLI隠しフラグ `--pyc`/`--pyc-native`Parser→Compiler→AOTの一本化導線
6. 最小回帰VM/AOTの差分記録とdocs追補include/exportとpyc、Regex/Encoding/TOML/PathのAPI概要
Env Keyspyc
- NYASH_PY_CODE: Pythonソース文字列Nyash-onlyパイプライン/Parser用
- NYASH_PY_IR: IR(JSON)直接注入Rust雛形Compilerの確認用・オプション
目的: Handle-First + by-name を軸に、Python統合PyRuntimeBox/PyObjectBoxを汎用・安全に実装する。最適化は後段。さらに10.7のNyash-onlyトランスパイルC2pycを最小構成で立ち上げる。
ステータス2025-08-30 更新)
- フェーズ: 10.5c 汎用Handle/TLV実装の拡張Python統合開始
- 方針: 「綺麗に作って動かす」= ハードコーディング排除・Handle/TLV統一・最適化は後回し
10.5b 完了項目(橋渡し済み)
- by-name シムgetattr/callを実装JIT/AOTし、Lowerer から a0 を `nyash.handle.of` で確実にハンドル化して呼び出し
- 引数 a1/a2 はハンドル優先/なければレガシー参照から TLV 構築String/Integer はプリミティブ化)
- 汎用 birth シムを追加
- `nyash.box.birth_h(type_id:i64)->i64`JIT/AOT
- `nyash.box.birth_i64(type_id:i64, argc:i64, a1:i64, a2:i64)->i64`JIT/AOT
- Lowerer: NewBox引数無しは birth_h に統一。引数ありは安全なケースInteger const引数が既にハンドルだけ birth_i64 に段階導入
- AOT: examples/aot_py_math_sqrt_min.nyash で Strict でも .o 生成を確認target/aot_objects/main.o
- ログ
- AOT: NYASH_CLI_VERBOSE=1 で birth_h の可視化
- JIT: events で by-name/birth の観測(必要十分の最小限)
10.5c 着手項目(進行中)
- Lowerer: PluginInvoketype_id/method_id & by-nameの Handle-First 配線を統一a0を常にnyash.handle.of
- JIT/AOT: birth_h/_i64と by-name シムでTLV生成を汎用化String/Integerはプリミティブ化、他はHandle
- Strict時のJIT実行停止コンパイル専用でVM=仕様の原則を徹底
非対応(後回し・最適化)
- StringBox 専用の known_string/再利用最適化
- 汎用的な定数プールbirth の可変長 TLV 一括最適化
次の作業10.5c 続き)
1) FFI仕様の短文化a0/a1/a2=Handle優先→TLV、レガシー抑止フラグ、戻りTLVのdecodeポリシー
2) birth引数の一般化メモ可変長TLV、例外時ハンドリング
3) Python統合の最小チェーンimport→getattr→callのAOT/VM双方での実装確認サンプル追加
4) ドキュメント更新10.5c README/INDEX、FFIガイド
合意済みルール
- まず汎用・安全に動かす(最適化は内部に隠し、後段)
- StringBox 等の個別特化は入れない。Handle/TLV で統一し、Box 追加を阻害しない
- Strict/FailFast を維持fallback で隠さない)
Update (2025-09-02 AM / Async unify + VM await fix + JIT AOT builder plan)
- Whats implemented (since last update)
- Interpreter/VM/JIT await semantics unified to Result.Ok/Err.
- Interpreter: await now returns Ok(value) or Err("Timeout") with cooperative polling and NYASH_AWAIT_MAX_MS guard.
- VM: execute_await() changed to safepoint + scheduler.poll loop with timeout → Err("Timeout") on expiry, Ok(value) on success.
- JIT: await_h produces handle (0 on timeout), then ok_h/err_h wrap into Result.Ok/Err (already wired).
- TaskGroup scaffolding connected to Interpreter
- nowait registers Future into implicit TaskGroup; function/static/parent calls push/pop task scopes to enable join on scope exit.
- TokenBox added as a first-class Box
- New Box: TokenBox (wraps CancellationToken). Externs: env.task.currentToken() → TokenBox, env.task.cancelCurrent() → cancel current scope token.
- Delay future (scheduler-backed)
- Extern: env.future.delay(ms) → FutureBox that resolves to void after ms (uses SingleThreadScheduler.spawn_after or thread fallback).
- CLI result normalization (interpreter path)
- When printing results, prefer semantics::coerce_to_i64/coerce_to_string and special-case plugin IntegerBox.get() so “IntegerBox(id)” prints as numeric value.
- New samples (smoke-friendly)
- apps/tests/mir-safe-min: minimal MIR (plugins disabled)
- Run: `NYASH_DISABLE_PLUGINS=1 ./target/debug/nyash --backend mir apps/tests/mir-safe-min/main.nyash` → Result: 3
- apps/tests/async-nowait-basic: Interpreter nowait/await using threads
- Run: `NYASH_DISABLE_PLUGINS=1 ./target/debug/nyash apps/tests/async-nowait-basic/main.nyash` → Result: 33
- apps/tests/async-scope-token: VM token + delay demo (plugins on)
- Run: `./target/debug/nyash --backend vm apps/tests/async-scope-token/main.nyash`
- Output: token: …; after delay; token after cancel: …; Result: 0
- apps/tests/async-await-timeout: VM await timeout demo
- Run: `./target/debug/nyash --backend vm apps/tests/async-await-timeout/main.nyash`
- Output: `Err(Timeout)` then Result: 0
- JIT (execute) status
- `--backend cranelift` (skeleton) runs: `apps/tests/mir-branch-ret` → Result: 1
- JIT-direct path compiles/executes for simple cases (single-exit return strategy in place, PHI materialization to locals, etc.).
- JIT (AOT/EXE) current blocker and plan
- Symptom: jit-direct path panics in Cranelift FunctionBuilder finalize: “FunctionBuilder finalized, but block block0 is not sealed”.
- Root cause: Current CraneliftBuilder repeatedly creates shortlived FunctionBuilder instances and finalizes them per emission step; sealing discipline diverges from expected pattern (single FunctionBuilder per function, seal blocks after predecessors known). Entry sealing/ret-epilogue sealing were added, but perstep finalize still violates constraints.
- Plan (box-first, clean layering)
1) Refactor CraneliftBuilder to hold a single FunctionBuilder per function lifetime.
- Maintain current block, value stack, and IR emission without recreating/finalizing FB on every op.
- Emit jump/branch/hostcall/phi consistently in the same FB.
- Seal blocks when predecessors are determined (via LowerCore callbacks), and perform a final seal sweep before define_function.
2) Keep ObjectBuilder (AOT .o path) returning directly (no ret_block), unchanged aside from any minimal alignment with singleFB pattern (it already returns directly and finishes module per function).
3) Target sample: apps/tests/mir-branch-ret for first green AOT object emission.
- Success criteria: tools/build_aot.sh invoked via `--compile-native -o app` produces an executable that prints Result: 1.
4) After branch/ret green, extend to minimal PHI case (mir-phi-min) ensuring paramized block args are declared prior to seals.
- Interim guidance
- For JIT testing use `--backend cranelift` (skeleton exec) or VM path with `NYASH_JIT_EXEC=0` unless running jit-direct readonly smokes.
- For AOT/EXE, wait for the singleFB refactor merge; current tools/build_aot.sh in strict mode forbids fallback and will fail on the sealing assertion.
- Env toggles / helpers
- Await timeout: `NYASH_AWAIT_MAX_MS` (default 5000)
- Scheduler trace/budget: `NYASH_SCHED_TRACE=1`, `NYASH_SCHED_POLL_BUDGET=N`
- JIT lower dump/trace: `NYASH_JIT_DUMP=1`, `NYASH_JIT_TRACE_RET=1`, `NYASH_JIT_TRACE_BLOCKS=1`
- JIT policy (read-only in jit-direct): `NYASH_JIT_STRICT=1` and policy.read_only enforced
- Next actions (execution order)
1) CraneliftBuilder: single FunctionBuilder per functionfinalize at end_functionのみ
- Remove perop new/finalize; switch emit_* to use the persistent FB.
- Seal entry immediately; seal successors when wiring is complete; final global seal sweep before define_function.
2) Verify jit-direct with `apps/tests/mir-branch-ret` (NYASH_JIT_THRESHOLD=1).
3) Enable AOT object emission for the sample and link via tools/build_aot.sh; run resulting EXE (expect Result: 1).
4) Extend to `mir-phi-min` (ensure ensure_block_params + sealing order correct).
5) Wire tri-backend/async/timeout smokes in tools/ (minimal, concise outputs) and add to CI.