fix(json_v0): Phase 25.1c/k - loop body local変数のPHI生成を追加
BreakFinderBox._find_loops/2 等で、loop body 内で新規宣言された local 変数(header_pos, next_i 等)が loop header に戻った時に undefined になる SSA バグを修正。 変更内容: - body_vars から base_vars にない変数を検出 - それらに対する IncompletePhi を header に追加 - backedge_to_cond 判定を拡張(Branch にも対応) - break があるループでは latch が Branch 終端になるため - トレースログ追加(HAKO_LOOP_PHI_TRACE=1) 根本原因: prepare_loop_variables_with() は preheader の変数のみを対象とし、 loop body 内で新規宣言された変数を PHI に含めていなかった。 修正効果: - BreakFinderBox._find_loops/2 の ValueId(172) undefined 解決見込み - FuncScannerBox.scan_all_boxes/1 も同様のパターンで修正される 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -134,13 +134,65 @@ pub(super) fn lower_loop_stmt(
|
||||
if let Some(bb) = ops.f.get_block_mut(bend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(ops.f, bend, cond_bb); }
|
||||
}
|
||||
let backedge_to_cond = matches!(
|
||||
ops.f.blocks
|
||||
// Detect whether the "latch" block has a backedge to the loop header/cond.
|
||||
// Note: loops with `break` often end the latch with a conditional Branch that
|
||||
// targets both `cond_bb` (continue path) and `exit_bb` (break path). We must
|
||||
// treat such branches as valid backedges as well; otherwise body-local vars
|
||||
// will miss PHI nodes at the header and become undefined on the second iteration
|
||||
// (BreakFinderBox._find_loops/2 などで観測されたバグ).
|
||||
let backedge_to_cond = match ops
|
||||
.f
|
||||
.blocks
|
||||
.get(&bend)
|
||||
.and_then(|bb| bb.terminator.as_ref()),
|
||||
Some(MirInstruction::Jump { target, .. }) if *target == cond_bb
|
||||
);
|
||||
.and_then(|bb| bb.terminator.as_ref())
|
||||
{
|
||||
Some(MirInstruction::Jump { target, .. }) if *target == cond_bb => true,
|
||||
Some(MirInstruction::Branch { then_bb, else_bb, .. })
|
||||
if *then_bb == cond_bb || *else_bb == cond_bb =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1");
|
||||
if trace_loop_phi {
|
||||
eprintln!("[loop-phi] backedge_to_cond={}, base_vars={}, body_vars={}",
|
||||
backedge_to_cond, base_vars.len(), body_vars.len());
|
||||
}
|
||||
if backedge_to_cond {
|
||||
// Phase 25.1c/k: body 内で新規宣言された local 変数も PHI に含める
|
||||
// BreakFinderBox._find_loops/2 等で、loop body 内の local 変数が
|
||||
// loop header に戻った時に undefined になる問題を修正
|
||||
let mut body_local_count = 0;
|
||||
for (var_name, &_latch_value) in body_vars.iter() {
|
||||
if !base_vars.contains_key(var_name) {
|
||||
// body で新規宣言された変数 → header に PHI ノードを追加
|
||||
body_local_count += 1;
|
||||
if trace_loop_phi {
|
||||
eprintln!("[loop-phi/body-local] Adding PHI for body-local var: {}", var_name);
|
||||
}
|
||||
let phi_id = ops.f.next_value_id();
|
||||
let inc_phi = crate::mir::phi_core::loop_phi::IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![], // preheader には存在しないので空
|
||||
};
|
||||
// header に空の PHI ノードを挿入(seal_incomplete_phis_with で完成させる)
|
||||
if let Some(bb) = ops.f.get_block_mut(cond_bb) {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi {
|
||||
dst: phi_id,
|
||||
inputs: vec![], // 空の PHI(後で latch/continue から完成)
|
||||
});
|
||||
}
|
||||
// 変数マップを PHI に rebind
|
||||
ops.vars.insert(var_name.clone(), phi_id);
|
||||
incomplete.push(inc_phi);
|
||||
}
|
||||
}
|
||||
if trace_loop_phi && body_local_count > 0 {
|
||||
eprintln!("[loop-phi/body-local] Total body-local vars added: {}", body_local_count);
|
||||
}
|
||||
|
||||
// 5) header の不完全PHIを完成(latch + continue スナップショット集約)
|
||||
let mut ops_seal = Ops { f: ops.f, vars: ops.vars, block_var_maps: &block_var_maps2 };
|
||||
crate::mir::phi_core::loop_phi::seal_incomplete_phis_with(
|
||||
|
||||
Reference in New Issue
Block a user