diff --git a/src/runner/json_v0_bridge/lowering/loop_.rs b/src/runner/json_v0_bridge/lowering/loop_.rs index 020b657c..cf7c964e 100644 --- a/src/runner/json_v0_bridge/lowering/loop_.rs +++ b/src/runner/json_v0_bridge/lowering/loop_.rs @@ -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 - .get(&bend) - .and_then(|bb| bb.terminator.as_ref()), - Some(MirInstruction::Jump { target, .. }) if *target == cond_bb - ); + // 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 => 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(