Files
hakorune/CURRENT_TASK.md
Moe Charm b05d5dd7ca Phase 12: TypeBox革命とJIT制御フロー修正 - セルフホスティングへの道
## 🔥 TypeBox ABI革命(セルフホスティング実現)
- Nyash ABIをC実装TypeBoxで提供する画期的設計
- Gemini/Codexの深い考察を反映した詳細仕様書作成
- 16バイトアライメント、セレクターキャッシング、弱参照等の具体実装案
- Rust依存を完全排除し、セルフホスティングへの明確な道筋を確立

## 🎯 JIT制御フロー根本修正(ChatGPT5の25分長考の成果)
- ブロック挿入点の明確化: switch_to_block()で各ブロックemit開始時に切替
- シール戦略の簡潔化: br/jump直後のseal撤廃、最終シールに一本化
- PHI最小経路実装: ブロック引数の事前プリスキャン、後付けPHI問題解決
- Cranelift制約対応: 開始済みブロックへのappend禁止を正しく実装

## 📚 ドキュメント更新
- Phase 12全体のセルフホスティング対応更新
- NYASH-ABI-C-IMPLEMENTATION.md: 実装仕様書作成
- gemini-codex-deep-thoughts.md: AI専門家の考察アーカイブ
- 各種README/TASKS更新でセルフホスティング要件追加

##  テスト結果
- mir-branch-ret: OK (Result: 1)
- mir-phi-min: OK (Result: 10)
- mir-branch-multi: OK (Result: 1)

「Everything is Box」の究極形態:ABIすらBoxとして扱う革命的設計!

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 11:18:19 +09:00

70 KiB
Raw Blame History

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 正規化i64→b1
    • デバッグ用 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: OKmir-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-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.rssrc/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_functionpending_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 モード実行(下記テンプレートをコピーして使用)

テンプレート(各アプリ用)

  • アプリ名:
    • 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.mdRepository 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=42size=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.lognyash.console.logNyRT, env.debug.tracenyash.debug.trace
      • ExternCall: env.console.readLine 追加 → nyash.console.readlinestdin 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.shNYASH_LLVM_OBJ_OUT を使用して .o を取得後、NyRT とリンク。
    • サンプル: apps/ny-llvm-smoke/main.nyashArray get/set/printapps/ny-echo-lite/main.nyashreadLine→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.rsBoxCall: 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.rsto_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.nyash0終了
    • 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=1brif出力NYASH_JIT_TRACE_BLOCKS=1block切替通知
  • 影響範囲

    • 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」に切替

タスク分解(本フェーズ)

  • Builderからのレガシー発行のデフォルト停止WeakNew/WeakLoad/BarrierRead/Writeを統一命令に切替、トグルで復活可
  • MIR Rewriteパス追加一部完了: Print/Type/Weak/Barrier、Debug/Safepointはトグル
  • Optimizer/Verifier/Printerの非互換の見直しVerifierでレガシー命令をエラー化
  • VM: レガシー命令のコードパスに警告ログ(将来削除フラグ)
  • JIT: コア命令に合わせたテーブル整理(未使用ホストコールの棚卸し)
  • Docs更新命令セット、移行ガイド、Box哲学との整合
  • 回帰テスト更新builder_modularizedを含む一式

追加の即応ステップ10.8a

  • Rewrite: Print → ExternCall(env.console.log)
  • Rewrite: TypeCheck/Cast → TypeOp、WeakNew/WeakLoad → WeakRef、BarrierRead/Write → Barrier
  • Rewrite(トグル): Debug → ExternCall(env.debug.trace)NYASH_REWRITE_DEBUG=1
  • Rewrite(トグル): Safepoint → ExternCall(env.runtime.checkpoint)NYASH_REWRITE_SAFEPOINT=1
  • Builder: Try/Catch/Throw/Safepoint の直接発行を抑止(トグル導入・モジュール化系にも適用)
  • Runtime: extern_call スタブ追加env.runtime.checkpoint, env.debug.trace
  • 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-jittools/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.rsNewBox起点のローカル追跡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命令」に更新し、命令数固定テストも切替える。

  • 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.nyashMath.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-nativeParser→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)->i64JIT/AOT
    • nyash.box.birth_i64(type_id:i64, argc:i64, a1:i64, a2:i64)->i64JIT/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.