Files
nyash-codex 80f8a7bc8c 🔧 Hotfix 7 (Enhanced): ValueId receiver alias tracking for nested loops
- Problem: Pinned receiver variables in loops cause undefined ValueId errors
- Enhanced fix: Update all receiver aliases (me + all __pin$N$@recv levels)
- Handles nested loops by updating previous pin levels
- Test status: Partial improvement, ValueId(50) → ValueId(40)
- Further investigation needed for complete fix

Files modified:
- src/mir/phi_core/loopform_builder.rs (emit_header_phis)
2025-11-19 00:02:41 +09:00

9.2 KiB
Raw Permalink Blame History

Phase 25.1j — LoopSSA v2 本体 & StageB harness 強化

Status: planning.hako 側 LoopSSA v2 本体Rust SSA/PHI は触らない)

ゴール

  • 25.1i で整えた LoopSSA + ControlFormBox 観測レイヤー の上に、.hako 側 LoopSSA v2 の「本体」を載せる準備をする。
    • 特に StageB minimal harnesstools/test_stageb_min.shTest 2 で出ている
      MIR compilation error: Undefined variable: trace を構造的に解消する。
    • 文字列ハードコードベースの Exit PHI 注入(_collect_phi_vars / synthetic "r{block}_{var}")は 即座には捨てず、 将来の v2 実装に移行しやすいよう責務を整理する。
  • このフェーズでは:
    • LoopSSA / BreakFinderBox / PhiInjectorBox の trace 変数の扱いとスコープを整理し、 「どこで ENV を読むか」「どこまで Box 内のローカルで閉じるか」を決める。
    • StageB Test 2 が Program(JSON v0) 出力まで進み、Rust 側 MirBuilder の SSA/PHI 検証に到達できる状態を作る。
    • StageB Test 3 の %0/SSA 問題については「LoopSSA v2 が悪化させていない」ことの確認まで(根治は次フェーズ)。

前提25.1i までで揃っているもの)

  • Rust:
    • Loop/If:
      • LoopForm v2 + Conservative PHI Box が ControlFormLoopShape/IfShape)経由で統合済み。
      • レガシー build_loop_legacy は削除済みで、LoopForm v2 が唯一のループ構築経路。
    • テスト:
      • mir_loopform_exit_phi / mir_stage1_using_resolver_* / mir_stageb_loop_break_continue は緑。
  • .hako:
    • lang/src/compiler/builder/ssa/loopssa.hako:
      • LoopSSA.stabilize_merges(stage1_json)BreakFinderBox.find_breaksPhiInjectorBox.inject_exit_phis の簡易パイプライン。
      • HAKO_LOOPSSA_EXIT_PHI で Exit PHI 注入の ON/OFF を制御している。
    • lang/src/compiler/builder/ssa/exit_phi/break_finder.hako:
      • 文字列ベースで "loop_header": / "loop_exit": を探す _find_loops
      • header_id < id < exit_id の範囲で body block を推定する _find_loop_body
      • break を「jump terminator の targetexit_id」な block として検出。
      • HAKO_COMPILER_BUILDER_TRACE=1[break-finder] …[loopssa/control] … ログを出す。
    • lang/src/shared/mir/control_form_box.hako:
      • 現行構文で ControlFormBox を定義(kind_name / entry / exits + loop/if 用フィールド)。
      • from_loop(header_id, exit_id, body_blocks) / from_if(cond, then, else, merge) で Loop/If の形を復元。
    • lang/src/compiler/entry/compiler_stageb.hako:
      • StageB entry が CompilerBuilder.apply_all(ast_json) を通した後の JSON を出力する構成。
      • 25.1i で using lang.compiler.parser.parser_box as ParserBox 等の修正により parse error は解消済み。
  • StageB minimal harness:
    • tools/test_stageb_min.sh:
      • Test1: 直接 VM 実行 → RC=0。
      • Test2: StageB 経由 → LoopSSA/Exit PHI 経路まで進むが最終的に
        MIR compilation error: Undefined variable: trace で停止LoopSSA v2 経路における trace スコープの問題)。
      • Test3: NYASH_VM_VERIFY_MIR=1%0 undefined 由来の SSA 問題が残っている(根本は LoopSSA だけとは限らない)。

方針25.1j: LoopSSA v2 本体の入口を整える)

JA: LoopSSA/BREAK/PHI 周辺の trace 変数スコープの整理

  • 目的:
    • .hako 側 LoopSSA パスの中で trace という名前が 常にローカル or Box フィールドとして定義されている 状態にする。
    • StageB Test2 における Undefined variable: trace を根治し、Program(JSON v0) 出力まで進める。
  • 方針:
    • 「ENV から読む責務」と「boolean フラグを渡して使う責務」を分離する。
    • 具体的には:
      • LoopSSA.stabilize_merges の先頭で local builder_trace = env.get("HAKO_COMPILER_BUILDER_TRACE") を読む。
      • BreakFinderBox.find_breaks / PhiInjectorBox.inject_exit_phis には 数値フラグ trace_flag を第3引数として渡す0/1
      • 各 Box 内では local trace = trace_flag(または me.trace フィールド)として閉じた名前にする。
    • 既存の local trace = env.get("HAKO_COMPILER_BUILDER_TRACE") は LoopSSA に集約し、下位 Box は「引数でもらったフラグだけを見る」構造に変える。

JB: LoopSSA v2 の責務再定義(設計)

  • 目的:
    • LoopSSA.stabilize_merges が「何を受け取り、どこまで責任を持つか」を Rust LoopForm v2 に揃えた形で 明文化 する。
    • StageB / LoopSSA / BreakFinderBox / PhiInjectorBox の責務分割を Box 単位で固定し、将来の v2 実装時も迷わない足場を作る。
  • 方針:
    • LoopSSA パスを 3 層に分解して設計を書く:
      1. LoopSSAオーケストレータ箱
        • input: Stage1 Program(JSON v0)(単一関数 or Program 全体)。
        • output: Loop break に対する exit PHI が挿入された Program(JSON v0)。
        • 責務:
          • dev トレースフラグ(HAKO_COMPILER_BUILDER_TRACE)と機能フラグ(HAKO_LOOPSSA_EXIT_PHI)を解釈。
          • BreakFinderBox.find_breaks(stage1_json, trace_flag) を呼んで Loop + break 情報を収集。
          • PhiInjectorBox.inject_exit_phis(stage1_json, breaks, trace_flag) に処理を委譲。
          • 例外時は JSON を変更せず FailFast する(「部分的に壊れた JSON を返さない」)。
      2. BreakFinderBox解析箱 / readonly
        • input: Stage1 Program(JSON v0)、trace_flag。
        • output: break 情報の配列([{block_id, exit_id, loop_header}, …])。
        • 責務:
          • "loop_header":NNN / "loop_exit":MMM のペアを見つけ、単純な LoopScope を構成する。
          • ControlFormBox に Loop 形header/exit/bodyを写し取る観測用
          • JSON 文字列は一切書き換えない(解析専用)。
      3. PhiInjectorBox変換箱 / writeonly
        • input: Stage1 Program(JSON v0)、break 情報配列、trace_flag。
        • output: Exit PHI 相当の命令を instructions 配列の先頭に挿入した Program(JSON v0)。
        • 責務:
          • break ごとの incoming 値を集約し、phi_vars = [{name, incoming:[{block,value},…]}, …] を構成。
          • Exit block の "instructions":[ … ] の直後に PHI 相当 JSON をテキスト挿入する。
          • 既存の命令配列順を壊さないPHI は「先頭に」挿入するのみ)。
    • Carrier / Pinned / Invariant の扱いは 設計としてだけ 固める:
      • Carrier: ループ内で値が更新され、exit でも必要になる変数Rust 側 Carrier と対応)。
      • Pinned: pin 付き一時値(__pin$…)など、観測目的で特別扱いする変数。
      • Invariant: ループ外で定義され、ループ内で再定義されない値PHI 不要)。
    • 25.1j では Exit PHI のアルゴリズム自体はまだ現行の simple 版のまま としつつ、 LoopSSA / BreakFinderBox / PhiInjectorBox の責務境界だけを README とソースコードコメントで明確にする。

JC: BreakFinder v2 への足場ControlFormBox の本格利用準備)

  • 目的:
    • 既存の loop_info = {header, exit, body} ベースに加えて、ControlFormBox に Loop 形を必ず写す。
  • 方針:
    • BreakFinderBox._find_loops 内で:
      • loop_info 生成に加えて local cf = new ControlFormBox()cf.from_loop(header_id, exit_id, body_blocks) を常時呼ぶ。
      • trace ON のとき [loopssa/control] ログに加えて ControlFormBox の内容も JSON 風にまとめて出すheader/exit/body_size
    • このフェーズでは戻り値の構造はまだ変えず、find_breaks は従来どおり breaks 配列を返す。

JD: PhiInjector v2 設計(実装は次フェーズ)

  • 目的:
    • Exit PHI 注入を ControlFormBox/LoopScope ベースで書き直すための 設計を先に固める
  • 方針:
    • 25.1j ではコード本体は変えず、phi_injector.hako と README に設計のみ追記:
      • inject_exit_phis(json_str, breaks, trace_flag) の将来形ControlFormBox + break 群 + JSON v0を定義。
      • Carrier/Pinned/Invariants を Rust LoopForm v2 の概念に揃える。
      • common_vars = ["i","n","item",…] のハードコードは「v2 で撤退予定」とコメントで明示。
    • 実アルゴリズムの書き換えv2 本体)は Phase 25.1k 以降の大きなタスクとして分離する。

このフェーズで「しない」こと

  • .hako 側 LoopSSA を Rust LoopForm v2 と完全同型にすることPHI アルゴリズムの全面移植)は行わない。
  • StageB Test3 の %0/SSA エラーを「必ずゼロにする」こと:
    • 25.1j では trace 変数のスコープ修正と LoopSSA v2 の責務整理 に集中する。
    • %0 の根治は、LoopSSA v2 本体Carrier/Pinned/Exit PHIを実装する次フェーズのターゲットとする。