docs(phi): Phase 25.1 - LoopForm v2 コメント整備 + ケース表完成

-  [LoopForm] タグで統一コメント追加
  - src/mir/loop_builder.rs
    - header-cond: Case A/B分岐説明
    - exit-break path / continue-backedge path
    - exit PHI for Case A/B
  - src/mir/phi_core/loop_snapshot_merge.rs
    - Case A/B分岐: header ∈ exit_preds判定ロジック
  - src/mir/phi_core/exit_phi_builder.rs
    - LoopForm Process ステップバイステップ説明

-  UsingCollectorBox Region+next_i化
  - lang/src/compiler/parser/using/using_collector_box.hako
    - 全ループをLoopForm v2形式に統一
    - next_i, next_j, next_k, next_t パターン導入
    - SSA安全化(未定義変数撲滅)

-  LoopForm v2 ケース表完成
  - docs/development/architecture/loops/loopform_ssot.md
    - Case A/B/C/D の完全な表
    - テスト対応マッピング
    - 実装ファイル対応表

🎯 成果: LoopForm v2の「形」をソース・テスト・ドキュメントで完全固定
This commit is contained in:
nyash-codex
2025-11-21 06:22:21 +09:00
parent 51359574d9
commit baf028a94f
5 changed files with 442 additions and 170 deletions

View File

@ -1,23 +1,35 @@
/*!
* MIR Loop Builder - SSA形式でのループ構築専用モジュール
*
* Sealed/Unsealed blockとPhi nodeを使った正しいループ実装
* Based on Gemini's recommendation for proper SSA loop handling
* Sealed/Unsealed blockとPhi nodeを使った正しいループ実装
*
* LoopForm v2 の「形」をここで固定している:
* - preheader: ループに入る直前のブロック(初期値の copy 発生源)
* - header : ループ条件を評価するブロック(`loop(cond)` の `cond` 部分)
* - body : ループ本体(ユーザーコードが書いたブロック)
* - latch : body の末尾から header へ戻る backedge 用ブロック
* - exit : ループ脱出先(`break` / `cond == false` が合流するブロック)
*
* 典型パターンControlForm::LoopShape:
* - Case A: header-cond + header→exit + body→exit`loop(i < n) { if (...) break }`
* - Case B: constant-true + body→exit のみ(`loop(1 == 1) { if (...) break }`
* - この場合、header→exit のエッジは存在しないので、exit PHI に header 値を入れてはいけない。
* - Case C: continue_merge を経由して header に戻る経路あり(`continue` を含むループ)。
*
* それぞれのケースは ControlForm / LoopSnapshotMergeBox / ExitPhiBuilder に伝搬され、
* exit PHI の入力選択や BodyLocalInternal 変数の扱いに反映される。
*/
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
use crate::mir::control_form::{ControlForm, IfShape, LoopShape, is_control_form_trace_on};
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
use crate::ast::ASTNode;
use crate::mir::control_form::{is_control_form_trace_on, ControlForm, IfShape, LoopShape};
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
use std::collections::HashMap;
// Phase 15 段階的根治戦略:制御フローユーティリティ
use super::utils::{
is_current_block_terminated,
capture_actual_predecessor_and_jump,
};
use super::utils::{capture_actual_predecessor_and_jump, is_current_block_terminated};
/// ループ脱出の種類(箱化・共通化のための型)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -50,11 +62,9 @@ pub struct LoopBuilder<'a> {
/// break文からの変数スナップショットexit PHI生成用
exit_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
// フェーズM: no_phi_modeフィールド削除常にPHI使用
}
impl<'a> LoopBuilder<'a> {
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
@ -83,27 +93,31 @@ impl<'a> LoopBuilder<'a> {
Ok(void_id)
}
/// 【箱化】ループ脱出の共通処理break/continue統一化
/// [LoopForm] 【箱化】ループ脱出の共通処理break/continue統一化
///
/// Phase 25.1o: break と continue の共通パターンを抽出し、
/// LoopExitKind で振る舞いを切り替える統一メソッド。
///
/// # 処理フロー
/// 1. 現在の変数マップをスナップショット
/// 2. 適切なスナップショットリストに追加Break → exit_snapshots, Continue → continue_snapshots
/// 3. ターゲットブロックへジャンプBreak → exit, Continue → header
/// 2. [LoopForm] スナップショット保存Break → exit_snapshots, Continue → continue_snapshots
/// 3. [LoopForm] ターゲットブロックへジャンプBreak → exit, Continue → header/continue_merge
/// 4. unreachable ブロックに切り替え
fn do_loop_exit(&mut self, kind: LoopExitKind) -> Result<ValueId, String> {
// 1. スナップショット取得(共通処理)
let snapshot = self.get_current_variable_map();
let cur_block = self.current_block()?;
// 2. スナップショット保存kind別処理
// 2. [LoopForm] exit-break path: スナップショット保存exit PHI入力用
// [LoopForm] continue-backedge path: スナップショット保存continue_merge → header
match kind {
LoopExitKind::Break => {
if std::env::var("NYASH_LOOPFORM_DEBUG").ok().as_deref() == Some("1") {
eprintln!("[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
cur_block, snapshot.keys().collect::<Vec<_>>());
eprintln!(
"[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
cur_block,
snapshot.keys().collect::<Vec<_>>()
);
}
self.exit_snapshots.push((cur_block, snapshot));
}
@ -116,7 +130,8 @@ impl<'a> LoopBuilder<'a> {
// 3. ターゲットブロックへジャンプkind別処理
match kind {
LoopExitKind::Break => {
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) {
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder)
{
self.jump_with_pred(exit_bb)?;
}
}
@ -156,7 +171,7 @@ impl<'a> LoopBuilder<'a> {
loop_header: None,
continue_target: None,
continue_snapshots: Vec::new(),
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
}
}
@ -179,8 +194,10 @@ impl<'a> LoopBuilder<'a> {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_loop_with_loopform] === ENTRY ===");
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
eprintln!(
"[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
}
eprintln!("[build_loop_with_loopform] condition={:?}", condition);
eprintln!("[build_loop_with_loopform] body.len()={}", body.len());
@ -194,8 +211,11 @@ impl<'a> LoopBuilder<'a> {
// DEBUG: Show variable map before guard check
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform] before_loop_id={:?}, variable_map size={}",
before_loop_id, current_vars.len());
eprintln!(
"[loopform] before_loop_id={:?}, variable_map size={}",
before_loop_id,
current_vars.len()
);
for (name, value) in &current_vars {
eprintln!(" {} -> {:?}", name, value);
}
@ -220,7 +240,11 @@ impl<'a> LoopBuilder<'a> {
let entry_block = self.current_block()?;
self.emit_jump(preheader_id)?;
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, preheader_id, entry_block)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
preheader_id,
entry_block,
)?;
// Initialize LoopFormBuilder with preheader and header blocks
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
@ -229,7 +253,10 @@ impl<'a> LoopBuilder<'a> {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}",
preheader_id, header_id, body_id, latch_id, exit_id);
eprintln!("[loopform] variable_map at loop entry (size={}):", current_vars.len());
eprintln!(
"[loopform] variable_map at loop entry (size={}):",
current_vars.len()
);
let mut loop_count = 0;
for (name, value) in &current_vars {
loop_count += 1;
@ -240,8 +267,10 @@ impl<'a> LoopBuilder<'a> {
}
eprintln!("[loopform] iterated {} times", loop_count);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
eprintln!(
"[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
} else {
eprintln!("[loopform] BEFORE prepare_structure: current_function=None");
}
@ -249,8 +278,10 @@ impl<'a> LoopBuilder<'a> {
loopform.prepare_structure(self, &current_vars)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
eprintln!(
"[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
} else {
eprintln!("[loopform] AFTER prepare_structure: current_function=None");
}
@ -285,12 +316,19 @@ impl<'a> LoopBuilder<'a> {
self.continue_snapshots.clear();
self.exit_snapshots.clear();
// Emit condition check in header
// [LoopForm] header-cond: cond true → body, false → exit (Case A/B)
// - Case A: loop(i < n) → header can branch to exit directly
// - Case B: loop(1 == 1) → header always enters body, exit only via break
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] BEFORE build_expression: current_block={:?}", self.current_block()?);
eprintln!(
"[loopform/condition] BEFORE build_expression: current_block={:?}",
self.current_block()?
);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
eprintln!(
"[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
}
}
let cond_value = self.parent_builder.build_expression(condition)?;
@ -298,28 +336,52 @@ impl<'a> LoopBuilder<'a> {
// if build_expression created new blocks)
let branch_source_block = self.current_block()?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] AFTER build_expression: branch_source_block={:?}", branch_source_block);
eprintln!(
"[loopform/condition] AFTER build_expression: branch_source_block={:?}",
branch_source_block
);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
eprintln!(
"[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
}
}
self.emit_branch(cond_value, body_id, exit_id)?;
// 📦 Hotfix 6: Add CFG predecessors for branch targets (Cytron et al. 1991 requirement)
// This ensures exit_block.predecessors is populated before Exit PHI generation
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}", exit_id, branch_source_block);
eprintln!(
"[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}",
exit_id, branch_source_block
);
}
crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, branch_source_block)?;
crate::mir::builder::loops::add_predecessor(self.parent_builder, exit_id, branch_source_block)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
body_id,
branch_source_block,
)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
exit_id,
branch_source_block,
)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?);
eprintln!("[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
body_id, exit_id, branch_source_block);
eprintln!(
"[loopform/condition] AFTER emit_branch: current_block={:?}",
self.current_block()?
);
eprintln!(
"[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
body_id, exit_id, branch_source_block
);
// Verify predecessors were added
if let Some(ref func) = self.parent_builder.current_function {
if let Some(exit_block) = func.blocks.get(&exit_id) {
eprintln!("[loopform/condition] exit_block.predecessors = {:?}", exit_block.predecessors);
eprintln!(
"[loopform/condition] exit_block.predecessors = {:?}",
exit_block.predecessors
);
}
}
}
@ -358,7 +420,10 @@ impl<'a> LoopBuilder<'a> {
// DEBUG: Log writes collection
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/writes] === WRITES COLLECTION (Step 5-1) ===");
eprintln!("[loopform/writes] {} variables modified in loop body", writes.len());
eprintln!(
"[loopform/writes] {} variables modified in loop body",
writes.len()
);
let mut sorted_writes: Vec<_> = writes.iter().collect();
sorted_writes.sort();
for name in &sorted_writes {
@ -404,8 +469,8 @@ impl<'a> LoopBuilder<'a> {
// DISABLED: Body-local header PHI generation
// This code was causing undefined value errors because it created header PHIs
// for variables that should only have exit PHIs (or no PHIs at all)
if false { // Disabled for Step 5-5-B experiment
// [Original code removed - see git history if needed]
if false { // Disabled for Step 5-5-B experiment
// [Original code removed - see git history if needed]
}
// Pass 4: Generate continue_merge PHIs first, then seal header PHIs
@ -418,15 +483,18 @@ impl<'a> LoopBuilder<'a> {
let merged_snapshot: HashMap<String, ValueId> = if !raw_continue_snaps.is_empty() {
if trace_loop_phi {
eprintln!("[loop-phi/continue-merge] Generating PHI nodes for {} continue paths",
raw_continue_snaps.len());
eprintln!(
"[loop-phi/continue-merge] Generating PHI nodes for {} continue paths",
raw_continue_snaps.len()
);
}
// すべての continue snapshot に現れる変数を収集
let mut all_vars: HashMap<String, Vec<(BasicBlockId, ValueId)>> = HashMap::new();
for (continue_bb, snapshot) in &raw_continue_snaps {
for (var_name, &value) in snapshot {
all_vars.entry(var_name.clone())
all_vars
.entry(var_name.clone())
.or_default()
.push((*continue_bb, value));
}
@ -460,8 +528,10 @@ impl<'a> LoopBuilder<'a> {
}
if trace_loop_phi {
eprintln!("[loop-phi/continue-merge] Generated PHI for '{}': {:?}",
var_name, phi_id);
eprintln!(
"[loop-phi/continue-merge] Generated PHI for '{}': {:?}",
var_name, phi_id
);
}
phi_id
};
@ -472,8 +542,11 @@ impl<'a> LoopBuilder<'a> {
// Note: 変数マップへの反映は seal_phis に委譲(干渉を避ける)
if trace_loop_phi {
eprintln!("[loop-phi/continue-merge] Merged {} variables from {} paths",
merged.len(), raw_continue_snaps.len());
eprintln!(
"[loop-phi/continue-merge] Merged {} variables from {} paths",
merged.len(),
raw_continue_snaps.len()
);
}
merged
} else {
@ -481,7 +554,11 @@ impl<'a> LoopBuilder<'a> {
};
self.emit_jump(header_id)?;
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, continue_merge_id)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
header_id,
continue_merge_id,
)?;
// Step 2: merged_snapshot を使って seal_phis を呼ぶ
// Phase 25.3: Continue merge PHI実装Task先生の発見
@ -555,7 +632,9 @@ impl<'a> LoopBuilder<'a> {
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
// Build exit PHIs for break statements using ControlForm wrapper
// [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds)
// - Case A: header+break → exit PHI includes both paths
// - Case B: break-only → exit PHI excludes header (not a predecessor)
let exit_snaps = self.exit_snapshots.clone();
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
&loopform,
@ -583,7 +662,6 @@ impl<'a> LoopBuilder<'a> {
Ok(void_dst)
}
// =============================================================
// PHI Helpers — prepare/finalize PHIs and block sealing
// =============================================================
@ -675,17 +753,25 @@ impl<'a> LoopBuilder<'a> {
let mut idx = 0;
while idx < block.instructions.len() {
match &mut block.instructions[idx] {
MirInstruction::Phi { dst: d, inputs: ins } if *d == dst => {
MirInstruction::Phi {
dst: d,
inputs: ins,
} if *d == dst => {
*ins = inputs.clone();
replaced = true;
break;
}
MirInstruction::Phi { .. } => { idx += 1; }
MirInstruction::Phi { .. } => {
idx += 1;
}
_ => break,
}
}
if !replaced {
let phi_inst = MirInstruction::Phi { dst, inputs: inputs.clone() };
let phi_inst = MirInstruction::Phi {
dst,
inputs: inputs.clone(),
};
block.instructions.insert(0, phi_inst);
}
if dbg {
@ -774,7 +860,6 @@ impl<'a> LoopBuilder<'a> {
self.parent_builder.variable_map.get(name).copied()
}
fn build_statement(&mut self, stmt: ASTNode) -> Result<ValueId, String> {
match stmt {
// Ensure nested bare blocks inside loops are lowered with loop-aware semantics
@ -794,8 +879,12 @@ impl<'a> LoopBuilder<'a> {
void_id
}))
}
ASTNode::If { condition, then_body, else_body, .. } =>
self.lower_if_in_loop(*condition, then_body, else_body),
ASTNode::If {
condition,
then_body,
else_body,
..
} => self.lower_if_in_loop(*condition, then_body, else_body),
ASTNode::Break { .. } => self.do_break(),
ASTNode::Continue { .. } => self.do_continue(),
other => self.parent_builder.build_expression(other),
@ -813,21 +902,32 @@ impl<'a> LoopBuilder<'a> {
let join_id = self.parent_builder.debug_next_join_id();
// Pre-pin comparison operands to slots so repeated uses across blocks are safe
if crate::config::env::mir_pre_pin_compare_operands() {
if let ASTNode::BinaryOp { operator, left, right, .. } = &condition {
use crate::ast::BinaryOperator as BO;
match operator {
BO::Equal | BO::NotEqual | BO::Less | BO::LessEqual | BO::Greater | BO::GreaterEqual => {
if let Ok(lhs_v) = self.parent_builder.build_expression((**left).clone()) {
let _ = self.parent_builder.pin_to_slot(lhs_v, "@loop_if_lhs");
}
if let Ok(rhs_v) = self.parent_builder.build_expression((**right).clone()) {
let _ = self.parent_builder.pin_to_slot(rhs_v, "@loop_if_rhs");
if let ASTNode::BinaryOp {
operator,
left,
right,
..
} = &condition
{
use crate::ast::BinaryOperator as BO;
match operator {
BO::Equal
| BO::NotEqual
| BO::Less
| BO::LessEqual
| BO::Greater
| BO::GreaterEqual => {
if let Ok(lhs_v) = self.parent_builder.build_expression((**left).clone()) {
let _ = self.parent_builder.pin_to_slot(lhs_v, "@loop_if_lhs");
}
if let Ok(rhs_v) = self.parent_builder.build_expression((**right).clone()) {
let _ = self.parent_builder.pin_to_slot(rhs_v, "@loop_if_rhs");
}
}
_ => {}
}
_ => {}
}
}
}
// Evaluate condition and create blocks
let cond_val = self.parent_builder.build_expression(condition)?;
let then_bb = self.new_block();
@ -871,16 +971,14 @@ impl<'a> LoopBuilder<'a> {
for s in then_body.iter().cloned() {
let _ = self.build_statement(s)?;
// フェーズS修正統一終端検出ユーティリティ使用
if is_current_block_terminated(self.parent_builder)? {
break;
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
let then_var_map_end = self.get_current_variable_map();
// フェーズS修正最強モード指摘の「実到達predecessor捕捉」を統一
let then_pred_to_merge = capture_actual_predecessor_and_jump(
self.parent_builder,
merge_bb
)?;
let then_pred_to_merge =
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
// Pop then-branch debug region
self.parent_builder.debug_pop_region();
@ -916,17 +1014,15 @@ impl<'a> LoopBuilder<'a> {
for s in es.into_iter() {
let _ = self.build_statement(s)?;
// フェーズS修正統一終端検出ユーティリティ使用
if is_current_block_terminated(self.parent_builder)? {
break;
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
else_var_map_end_opt = Some(self.get_current_variable_map());
}
// フェーズS修正else branchでも統一実到達predecessor捕捉
let else_pred_to_merge = capture_actual_predecessor_and_jump(
self.parent_builder,
merge_bb
)?;
let else_pred_to_merge =
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
// Pop else-branch debug region
self.parent_builder.debug_pop_region();
@ -937,10 +1033,16 @@ impl<'a> LoopBuilder<'a> {
.debug_push_region(format!("join#{}", join_id) + "/join");
let mut vars: std::collections::HashSet<String> = std::collections::HashSet::new();
let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
let then_prog = ASTNode::Program {
statements: then_body.clone(),
span: crate::ast::Span::unknown(),
};
crate::mir::phi_core::if_phi::collect_assigned_vars(&then_prog, &mut vars);
if let Some(es) = &else_body {
let else_prog = ASTNode::Program { statements: es.clone(), span: crate::ast::Span::unknown() };
let else_prog = ASTNode::Program {
statements: es.clone(),
span: crate::ast::Span::unknown(),
};
crate::mir::phi_core::if_phi::collect_assigned_vars(&else_prog, &mut vars);
}
@ -949,15 +1051,25 @@ impl<'a> LoopBuilder<'a> {
// Use shared helper to merge modified variables at merge block
struct Ops<'b, 'a>(&'b mut LoopBuilder<'a>);
impl<'b, 'a> crate::mir::phi_core::if_phi::PhiMergeOps for Ops<'b, 'a> {
fn new_value(&mut self) -> ValueId { self.0.new_value() }
fn new_value(&mut self) -> ValueId {
self.0.new_value()
}
fn emit_phi_at_block_start(
&mut self,
block: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> { self.0.emit_phi_at_block_start(block, dst, inputs) }
fn update_var(&mut self, name: String, value: ValueId) { self.0.parent_builder.variable_map.insert(name, value); }
fn debug_verify_phi_inputs(&mut self, merge_bb: BasicBlockId, inputs: &[(BasicBlockId, ValueId)]) {
) -> Result<(), String> {
self.0.emit_phi_at_block_start(block, dst, inputs)
}
fn update_var(&mut self, name: String, value: ValueId) {
self.0.parent_builder.variable_map.insert(name, value);
}
fn debug_verify_phi_inputs(
&mut self,
merge_bb: BasicBlockId,
inputs: &[(BasicBlockId, ValueId)],
) {
if let Some(ref func) = self.0.parent_builder.current_function {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
}
@ -1011,7 +1123,9 @@ impl<'a> LoopBuilder<'a> {
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
fn new_value(&mut self) -> ValueId { self.new_value() }
fn new_value(&mut self) -> ValueId {
self.new_value()
}
fn emit_phi_at_block_start(
&mut self,
block: BasicBlockId,
@ -1020,12 +1134,18 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
) -> Result<(), String> {
self.emit_phi_at_block_start(block, dst, inputs)
}
fn update_var(&mut self, name: String, value: ValueId) { self.update_variable(name, value) }
fn update_var(&mut self, name: String, value: ValueId) {
self.update_variable(name, value)
}
fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option<ValueId> {
// Call the inherent method (immutable borrow) to avoid recursion
LoopBuilder::get_variable_at_block(self, name, block)
}
fn debug_verify_phi_inputs(&mut self, merge_bb: BasicBlockId, inputs: &[(BasicBlockId, ValueId)]) {
fn debug_verify_phi_inputs(
&mut self,
merge_bb: BasicBlockId,
inputs: &[(BasicBlockId, ValueId)],
) {
if let Some(ref func) = self.parent_builder.current_function {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
}
@ -1047,7 +1167,10 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(preheader_id) {
if dbg {
eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
eprintln!(
"[DEBUG] Adding Copy instruction to block {}",
preheader_id
);
}
block.add_instruction(MirInstruction::Copy { dst, src });
Ok(())
@ -1083,15 +1206,20 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
let before = func.next_value_id;
let id = func.next_value_id();
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[LoopFormOps::new_value] fn='{}' counter: {} -> {}, allocated: {:?}",
func.signature.name, before, func.next_value_id, id);
eprintln!(
"[LoopFormOps::new_value] fn='{}' counter: {} -> {}, allocated: {:?}",
func.signature.name, before, func.next_value_id, id
);
}
id
} else {
// Fallback (should never happen in practice)
let id = self.parent_builder.value_gen.next();
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[LoopFormOps::new_value] FALLBACK value_gen, allocated: {:?}", id);
eprintln!(
"[LoopFormOps::new_value] FALLBACK value_gen, allocated: {:?}",
id
);
}
id
};
@ -1126,13 +1254,16 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
}
}
fn get_block_predecessors(&self, block: BasicBlockId) -> std::collections::HashSet<BasicBlockId> {
fn get_block_predecessors(
&self,
block: BasicBlockId,
) -> std::collections::HashSet<BasicBlockId> {
// 📦 Hotfix 6: Get actual CFG predecessors for PHI validation
if let Some(ref func) = self.parent_builder.current_function {
if let Some(bb) = func.blocks.get(&block) {
bb.predecessors.clone()
} else {
std::collections::HashSet::new() // Non-existent blocks have no predecessors
std::collections::HashSet::new() // Non-existent blocks have no predecessors
}
} else {
std::collections::HashSet::new()
@ -1154,8 +1285,12 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
let is_param = self.parent_builder.is_value_parameter(value_id);
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[is_parameter] ValueId({}) -> {} (kind = {:?})",
value_id.0, is_param, self.parent_builder.get_value_kind(value_id));
eprintln!(
"[is_parameter] ValueId({}) -> {} (kind = {:?})",
value_id.0,
is_param,
self.parent_builder.get_value_kind(value_id)
);
}
is_param
@ -1166,7 +1301,8 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
}
fn emit_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), String> {
self.parent_builder.emit_instruction(MirInstruction::Copy { dst, src })
self.parent_builder
.emit_instruction(MirInstruction::Copy { dst, src })
}
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
@ -1178,11 +1314,7 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
self.emit_phi_at_block_start(
self.current_block()?,
dst,
inputs
)
self.emit_phi_at_block_start(self.current_block()?, dst, inputs)
}
fn update_phi_inputs(
@ -1191,7 +1323,8 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
phi_id: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
self.parent_builder.update_phi_instruction(block, phi_id, inputs)
self.parent_builder
.update_phi_instruction(block, phi_id, inputs)
}
fn update_var(&mut self, name: String, value: ValueId) {