Files
hakorune/docs/development/roadmap/phases/phase-25.1g/README.md
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

8.8 KiB
Raw Blame History

Phase 25.1g — Conservative PHI ↔ ControlForm 統合Rust統合完了

Status: completedControlForm 導線統合+レガシー縮退/挙動は不変)

ゴール

  • 25.1f で整えた ControlForm 観測レイヤーLoopShape / IfShape + ControlFormを、
    Conservative PHI BoxIf/Loop 用 SSA/PHI ロジック)の入口として使えるようにする。
  • いきなりすべてを書き換えるのではなく、
    1. If 用 Conservative PHI → ControlForm 対応
    2. LoopForm v2 Exit PHI → ControlForm 対応 の順に、小さく段階的に寄せる。
  • 各ステップごとに Rust テストと tools/test_stageb_min.sh を流しつつ、
    SSA/PHI の赤ログが増えていないことを確認して進む。

前提25.1f までで揃ったもの)

  • Rust:
    • src/mir/control_form.rs:
      • LoopShape / IfShape / ControlKind / ControlForm / CfgLike / is_control_form_trace_on() が定義済み。
    • src/mir/loop_builder.rs:
      • LoopForm v2 経路(build_loop_with_loopform)の出口で LoopShapeControlForm::Loop を生成・トレース。
      • lower_if_in_loop の merge 部分で IfShapeControlForm::If を生成・トレース。
      • NYASH_CONTROL_FORM_TRACE(未設定=ON, 0/false=OFFでトレース ON/OFF 切り替え可能。
    • Conservative PHI:
      • If 用: src/mir/builder/phi.rs / src/mir/phi_core/if_phi.rsConservative PHI Box 実装)。
      • Loop 用: src/mir/phi_core/loopform_builder.rsLoopForm v2 の Exit PHI / Carrier/Pinned 対応)。
  • .hako:
    • lang/src/shared/mir/control_form_box.hako:
      • static box ControlFormBoxkind_name + loop_* / if_* + entry/exitsだけ実装済みまだ未使用

方針Option B の範囲)

  • このフェーズでは:
    • Conservative PHI の「インターフェースと呼び出し位置」に ControlForm の導線を用意する。
    • ただし既存のロジック(引数で BlockId を受け取る形は残し、ControlForm 導線は 観測+補助的な入口 として扱う。
    • 影響範囲は If / Loop の PHI 部分に限定し、他の MIR 降下には触れない。
  • 実装方針:
    • 新しい API を「足す」→ 既存コードから段階的に使い始める、という形で進める。
    • 例:
      • If 用: merge_modified_at_merge_with_control(form: &ControlForm, ...) を追加。
      • Loop 用: build_exit_phis_for_control(form: &ControlForm, ...) を追加。
    • 最初のステップでは「ControlForm から必要な BlockId を取り出して、既存の関数に委譲するだけ」の薄いラッパにする。

実施内容

G1: If 用 Conservative PHI への ControlForm 導線(完了)

  • 目的:
    • loop_builder.rs::lower_if_in_loop から ControlForm::If を PHI ロジックに渡し、Conservative PHI Box が ControlForm ベースで動けるようにする。
  • 実装:
    • src/mir/phi_core/if_phi.rs に ControlForm ベースの薄いラッパを追加:
      pub fn merge_modified_with_control<O: PhiMergeOps>(
          ops: &mut O,
          form: &crate::mir::control_form::ControlForm,
          pre_if_snapshot: &HashMap<String, ValueId>,
          then_map_end: &HashMap<String, ValueId>,
          else_map_end_opt: &Option<HashMap<String, ValueId>>,
          skip_var: Option<&str>,
          then_pred_opt: Option<crate::mir::BasicBlockId>,
          else_pred_opt: Option<crate::mir::BasicBlockId>,
      ) -> Result<(), String> {
          use crate::mir::control_form::ControlKind;
      
          let shape = match &form.kind {
              ControlKind::If(shape) => shape,
              _ => return Ok(()),
          };
      
          let merge_bb = shape.merge_block;
      
          let trace = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");
          if trace {
              eprintln!(
                  "[if-phi/control-form] Using ControlForm wrapper: merge={:?} then={:?} else={:?}",
                  merge_bb, shape.then_block, shape.else_block
              );
          }
      
          merge_modified_at_merge_with(
              ops,
              merge_bb,
              shape.then_block,
              shape.else_block.unwrap_or(shape.then_block),
              then_pred_opt,
              else_pred_opt,
              pre_if_snapshot,
              then_map_end,
              else_map_end_opt,
              skip_var,
          )
      }
      
    • src/mir/loop_builder.rs::lower_if_in_loop では、IfShape→ControlForm を構築してこのラッパを直接呼び出すように統合:
      let if_shape = IfShape {
          cond_block: pre_branch_bb,
          then_block: then_bb,
          else_block: Some(else_bb),
          merge_block: merge_bb,
      };
      let form = ControlForm::from_if(if_shape.clone());
      
      crate::mir::phi_core::if_phi::merge_modified_with_control(
          &mut ops,
          &form,
          &pre_if_var_map,
          &then_var_map_end,
          &else_var_map_end_opt,
          None,
          Some(then_pred_to_merge),
          Some(else_pred_to_merge),
      )?;
      
    • その後ろで is_control_form_trace_on() が true のときに form.debug_dump()if_shape.debug_validate() を呼び、
      If の形を ControlForm 経由で常時観測できるようにした。
  • テスト:
    • cargo test -q mir_stage1_using_resolver_min_fragment_verifies -- --nocapture
    • cargo test -q mir_stage1_using_resolver_full_collect_entries_verifies -- --nocapture
    • いずれも PASSConservative PHI の挙動変化なし)。

G2: LoopForm v2 Exit PHI への ControlForm 導線(完了)

  • 目的:
    • LoopForm v2 Exit PHI が ControlFormLoopShapeを入口に使えるようにする。
  • 実装:
    • src/mir/phi_core/loopform_builder.rs に ControlForm 用ラッパを追加:
      pub fn build_exit_phis_for_control<O: LoopFormOps>(
          loopform: &LoopFormBuilder,
          ops: &mut O,
          form: &crate::mir::control_form::ControlForm,
          exit_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
          branch_source_block: BasicBlockId,
      ) -> Result<(), String> {
          use crate::mir::control_form::ControlKind;
      
          let shape = match &form.kind {
              ControlKind::Loop(shape) => shape,
              _ => return Ok(()),
          };
      
          let exit_id = shape.exit;
          let trace = std::env::var("NYASH_LOOPFORM_DEBUG").ok().is_some();
          if trace {
              eprintln!(
                  "[loopform/exit-phi/control-form] Using ControlForm wrapper: exit={:?} branch_source={:?}",
                  exit_id, branch_source_block
              );
          }
      
          loopform.build_exit_phis(ops, exit_id, branch_source_block, exit_snapshots)
      }
      
    • src/mir/loop_builder.rs::build_loop_with_loopform では、Exit PHI 部分をこのラッパ経由に統一し、
      その直後で ControlForm::Loopdebug_dump / debug_validate を行うように整理:
      let loop_shape = LoopShape {
          preheader: preheader_id,
          header: header_id,
          body: body_id,
          latch: latch_id,
          exit: exit_id,
          continue_targets,
          break_targets,
      };
      let form = ControlForm::from_loop(loop_shape.clone());
      
      let exit_snaps = self.exit_snapshots.clone();
      crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
          &loopform,
          self,
          &form,
          &exit_snaps,
          branch_source_block,
      )?;
      
      if is_control_form_trace_on() {
          form.debug_dump();
          #[cfg(debug_assertions)]
          if let Some(ref func) = self.parent_builder.current_function {
              loop_shape.debug_validate(func);
          }
      }
      
  • テスト:
    • cargo test -q mir_loopform_exit_phi -- --nocapture
    • cargo test -q mir_stageb_loop_break_continue -- --nocapture
    • いずれも PASS。Exit PHI の SSA/PHI 挙動は従来と同じで、ControlForm ベースでも構造が崩れていないことを確認。

G3: ControlForm ↔ Conservative PHI の設計メモ更新(完了)

  • 目的:
    • 25.1d/e/f/g の成果をまとめ、「If/Loop の PHI が最終的にどのレイヤで SSOT を持つか」を文書で固定する。
  • 実施:
    • 本 README と CURRENT_TASK.md に、Conservative PHI Box と ControlForm レイヤの役割分担を整理して追記。
    • 25.1g は「Rust 側での Conservative PHI ↔ ControlForm 統合If/Loop完了」フェーズとして締める。

このフェーズで残っていること / 先送りしたこと

  • .hako 側 LoopSSA / StageB パイプラインはまだ ControlFormBox 未統合のまま。
    • Test 3StageB MIR verify%0 問題や、BreakFinderBox 周辺の undefined ValueId は次フェーズLoopSSA 実装側)で扱う。
  • LoopBuilder/If 降下そのものを ControlForm ベースに再構成する「Option C」は、Phase 25.2 以降の大きめリファクタとして残しておく。