diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 51f48883..bd03ea80 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -383,6 +383,10 @@ impl<'a> LoopBuilder<'a> { // Phase 25.1c/k: body-local 変数の PHI 生成 // BreakFinderBox / FuncScannerBox 等で、loop body 内で新規宣言された local 変数が // loop header に戻った時に undefined になる問題を修正 + // + // Step 5-5-A: PHI pred mismatch 根本修正 + // - Preheader入力(poison)とLatch入力の両方を即座に追加 + // - 空PHI生成→後で埋める方式は不完全だった(seal_phisが処理しない) let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1"); if trace_loop_phi { eprintln!("[loop-phi/body-local] Checking for body-local variables"); @@ -401,15 +405,36 @@ impl<'a> LoopBuilder<'a> { eprintln!("[loop-phi/body-local] Found {} body-local variables", body_local_vars.len()); } - // Add PHI nodes for body-local variables at header - for (var_name, _latch_value) in &body_local_vars { + // Step 5-5-A: Add PHI nodes with BOTH inputs (preheader + latch) + for (var_name, _body_value) in &body_local_vars { if trace_loop_phi { eprintln!("[loop-phi/body-local] Adding PHI for body-local var: {}", var_name); } let phi_id = self.new_value(); - // Insert empty PHI at header (will be sealed by seal_phis or later) + // Step 5-5-A: Preheader input is poison (variable doesn't exist yet) + let preheader_poison = self.new_value(); + + // Emit Const::Poison instruction in preheader block + if let Some(ref mut func) = self.parent_builder.current_function { + if let Some(preheader_block) = func.blocks.get_mut(&preheader_id) { + preheader_block.add_instruction(MirInstruction::Const { + dst: preheader_poison, + value: crate::mir::ConstValue::Null, // Poison semantics + }); + } + } + + // Step 5-5-A FIX: Get latch value from current variable map + // The variable map was updated at line 374-377 with body_end_vars, + // so looking it up now gives us the correct ValueId for the latch block. + let latch_value = self.get_current_variable_map() + .get(var_name) + .copied() + .unwrap_or(phi_id); // Fallback to phi_id if not found + + // Step 5-5-A: Insert PHI with BOTH inputs (preheader poison + latch value) if let Some(ref mut func) = self.parent_builder.current_function { if let Some(header_block) = func.blocks.get_mut(&header_id) { // Find position after existing PHIs @@ -418,7 +443,10 @@ impl<'a> LoopBuilder<'a> { phi_count, MirInstruction::Phi { dst: phi_id, - inputs: vec![], // Empty PHI, will be filled by seal_phis + inputs: vec![ + (preheader_id, preheader_poison), // ← Preheader input (poison) + (latch_id, latch_value), // ← Latch input (from variable map) + ], }, ); } @@ -426,10 +454,17 @@ impl<'a> LoopBuilder<'a> { // Rebind variable to PHI self.update_variable(var_name.clone(), phi_id); + + if trace_loop_phi { + eprintln!( + "[loop-phi/body-local] Created PHI {}: preheader={} (poison), latch={} ({})", + phi_id.0, preheader_poison.0, latch_id.0, latch_value.0 + ); + } } if trace_loop_phi { - eprintln!("[loop-phi/body-local] Added {} body-local PHIs", body_local_vars.len()); + eprintln!("[loop-phi/body-local] Added {} body-local PHIs with complete inputs", body_local_vars.len()); } } @@ -515,57 +550,15 @@ impl<'a> LoopBuilder<'a> { loopform.seal_phis(self, actual_latch_id, &continue_snaps, &writes)?; // Step 3: seal body-local PHIs (complete the inputs) - if !body_local_vars.is_empty() { - if trace_loop_phi { - eprintln!("[loop-phi/body-local] Sealing {} body-local PHIs", body_local_vars.len()); - } - - for (var_name, _) in &body_local_vars { - // Get the PHI we created earlier from current variable map - let current_map = self.get_current_variable_map(); - let phi_id = current_map.get(var_name).copied() - .ok_or_else(|| format!("Body-local variable '{}' not found in variable map", var_name))?; - - // Build inputs: no preheader input (variable doesn't exist there), - // add latch input and continue inputs - let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![]; - - // Add latch input - let latch_value = self.get_variable_at_block(var_name, actual_latch_id) - .unwrap_or(phi_id); // Fallback to phi_id if not found - inputs.push((actual_latch_id, latch_value)); - - // Add continue inputs - for (cid, snapshot) in &continue_snaps { - if let Some(&value) = snapshot.get(var_name) { - inputs.push((*cid, value)); - } - } - - // Update PHI inputs - if let Some(ref mut func) = self.parent_builder.current_function { - if let Some(header_block) = func.blocks.get_mut(&header_id) { - // Find the PHI instruction for this variable - for instr in &mut header_block.instructions { - if let MirInstruction::Phi { dst, inputs: phi_inputs } = instr { - if *dst == phi_id { - *phi_inputs = inputs.clone(); - if trace_loop_phi { - eprintln!("[loop-phi/body-local] Sealed '{}' phi={:?} inputs={:?}", - var_name, phi_id, inputs); - } - break; - } - } - } - } - } - } - - if trace_loop_phi { - eprintln!("[loop-phi/body-local] Sealed {} body-local PHIs", body_local_vars.len()); - } - } + // Step 5-5-A: REMOVED - PHIs now created complete with both inputs upfront + // Old sealing code was overwriting our preheader+latch inputs with latch-only, + // causing "phi pred mismatch" errors. + // + // Body-local PHIs are now created at line 408-456 with BOTH inputs: + // - preheader: poison value (variable doesn't exist yet) + // - latch: actual value from loop body + // + // No further sealing is needed! // Exit block self.set_current_block(exit_id)?;