# Phase 25.1k — LoopSSA v2 実装 & Stage‑B SSA 安定化(.hako 本体版) Status: planning(.hako 側 LoopSSA v2 本体実装/Rust 側は既存 SSA/PHI を SSOT として維持) ## ゴール - 25.1j までに固めた LoopSSA/BreakFinderBox/PhiInjectorBox の責務・設計をベースに、 `.hako` 側 LoopSSA v2 の **実装本体** に踏み込むフェーズだよ。 - 具体的には: - Stage‑B minimal harness(`tools/test_stageb_min.sh`)の Test 2/3 で出ている: - `BreakFinderBox._find_loops/2` 周辺の `use of undefined value ValueId(50)`(Rust VM 側エラー) - `%0` 由来の SSA エラー(`NYASH_VM_VERIFY_MIR=1` 時) を **LoopSSA v2 の改善によって減らす/消す** ことを狙う。 - 文字列ハードコードベースの `_collect_phi_vars` / synthetic `"r{block}_{var}"` を、 Carrier/Pinned ベースの設計に一歩近づける(完全置き換えまでは行かなくても OK)。 ## 前提(25.1j までで揃っているもの) - Rust 側: - LoopForm v2 + Conservative PHI Box + ControlForm が統合済みで、If/Loop の SSA/PHI は緑。 - Stage‑B 風ループ(Rust テスト)も LoopForm v2 / Conservative PHI で安定している。 - `.hako` 側: - LoopSSA パス: - `LoopSSA.stabilize_merges(stage1_json)` が `BreakFinderBox.find_breaks(json, trace_flag)` → `PhiInjectorBox.inject_exit_phis(json, breaks, trace_flag)` の 2 段構成で動作。 - trace/ENV 解釈は LoopSSA に一元化され、下流には 0/1 の `trace_flag` だけ渡す構造に整理済み。 - BreakFinderBox: - `_find_loops(json_str, trace)` が `"loop_header":` / `"loop_exit":` から loop を検出。 - `loop_info` に `{"header", "exit", "body", "control"}` を格納し、`control` に ControlFormBox を添付。 - PhiInjectorBox: - 現状は `common_vars = ["i","n","item","j","count","val"]` に対する簡易版 `_collect_phi_vars`。 - value_id は `"r{block_id}_{var_name}"` 形式の synthetic 値(観測用のダミー)を返している。 - ドキュメント: - 25.1j の README で LoopSSA/BreakFinderBox/PhiInjectorBox の責務境界と Carrier/Pinned/Invariants 概念を明文化済み。 ## 方針(25.1k: LoopSSA v2 の「中身」を少し前に進める) ### K‑A: Stage‑B Test2 の ValueId(50) 問題の最小再現と LoopSSA 切り分け - 目的: - `BreakFinderBox._find_loops/2` で発生している `use of undefined value ValueId(50)` を、 「LoopSSA が生成した JSON の問題なのか」「他フェーズの JSON なのか」切り分ける。 - ステップ: 1. `tools/test_stageb_min.sh` Test2 の JSON 出力を一時ファイルに保存(Stage‑B → Program(JSON v0) 直後)。 2. その JSON に対して: - Rust 側 MirBuilder に直接食わせて `NYASH_VM_VERIFY_MIR=1` を通す。 - `.hako` 側 LoopSSA を単独で呼び出す(`LoopSSA.stabilize_merges(json)`)最小ハーネスを用意。 3. どの時点で ValueId(50) が未定義になるかを特定し、 それが LoopSSA の改変前後で変わっているかを確認する。 ### K‑B: BreakFinderBox の LoopScope 精度の向上(保守的に) - 目的: - `_find_loop_body` の「header_id < id < exit_id」ヒューリスティックが過剰/過少に body を拾っていないかを確認・改善する。 - ステップ: - ControlFormBox を活用して LoopScope の妥当性をチェック: - header/exit/body に対して Rust 側 LoopForm v2 の期待と比較しやすい形でログを出す(block id 範囲など)。 - 必要であれば: - body 集合を「exit から逆到達できる block」など、より保守的な条件に修正(文字列ベースの範囲内で)。 - ゴール: - LoopSSA が「本来そのループに属さない block」を body に含めないようにする(ValueId(50) のような飛び火を防ぐ)。 ### K‑C: PhiInjectorBox の v2 への一歩(Carrier/Pinned の入口だけ作る) - 目的: - `_collect_phi_vars` の完全置き換えまでは行かずに、今後の移行先となる v2 API の入口を整える。 - ステップ: - `PhiInjectorBox` に新しい内部ヘルパーを追加(例: `_collect_phi_vars_v2(json_str, break_list, loop_info)`): - まだ中身は stub でもよいが、「Carrier/Pinned/Invariants」の 3 区分を引数/戻り値で表現できる形にする。 - 25.1k では v1 実装(`_collect_phi_vars`)を実際に置き換えず、trace=1 の時だけ v2 の診断ログを出すくらいに留める。 - `loop_info.get("control")` の ControlFormBox から header/exit/body を読み取り、 どの変数が本来の Carrier に相当しそうかをログに出す(まだ PHI には使わない)。 ### K‑D: Stage‑B Test3 (%0) の「悪化していない」ことの確認 - 目的: - LoopSSA v2 の変更が、Stage‑B Test3 の `%0` SSA 問題を悪化させていないことを確認する。 - ステップ: - `NYASH_VM_VERIFY_MIR=1` で Test3 を流したときのエラー位置(関数名/ブロック/命令)を記録。 - 25.1k の差分適用前後で比較し、LoopSSA 関連の変更による新規エラーが出ていないことを確認。 - 必要なら、Test3 から LoopSSA を一時オフ(`HAKO_LOOPSSA_EXIT_PHI=0`)にした場合のログも取っておき、 「LoopSSA が原因の部分」と「それ以外」を明確に切り分ける。 ## このフェーズで「しない」こと - PhiInjectorBox の `_collect_phi_vars` / `_get_var_value` を **完全刷新すること**: - これは 25.1k の次、25.1l 以降の「本格 v2 実装」のタスクとして分ける。 - Rust 側 LoopForm v2 / Conservative PHI の設計を変えること: - Rust 側はあくまで SSOT であり、.hako 側はそれに追従する形で徐々に近づける。