diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 8a8be63b..da0fa5d7 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -185,8 +185,63 @@ impl<'a> LoopBuilder<'a> { } let condition_value = self.build_expression_with_phis(condition)?; + // Fix for ValueId(17) bug: Add PHI nodes for any pinned variables created during condition evaluation + // When method calls occur in loop conditions (e.g., i < args.length()), pin_to_slot creates + // pinned receiver variables like __pin$*@recv. These must have PHI nodes in the loop header. + let post_cond_vars = self.get_current_variable_map(); + let mut new_pinned_vars: Vec<(String, ValueId)> = Vec::new(); + for (name, &value) in post_cond_vars.iter() { + if !name.starts_with("__pin$") { continue; } + // Check if this pinned variable existed before condition compilation + let was_in_incs = incs.iter().any(|inc| inc.var_name == *name); + if !was_in_incs { + // This is a new pinned variable created during condition evaluation + new_pinned_vars.push((name.clone(), value)); + } + } + + // Add PHI nodes for new pinned variables in header block + for (name, value) in new_pinned_vars { + let phi_id = self.new_value(); + // Get the value from preheader (same as the current value since it was just created) + let preheader_value = value; + self.emit_phi_at_block_start(header_id, phi_id, vec![(preheader_id, preheader_value)])?; + // Update variable map to use PHI value + self.update_variable(name.clone(), phi_id); + // Add to incomplete PHIs for later sealing with latch edges + if let Some(ref mut header_incs) = self.incomplete_phis.get_mut(&header_id) { + header_incs.push(crate::mir::phi_core::loop_phi::IncompletePhi { + phi_id, + var_name: name, + known_inputs: vec![(preheader_id, preheader_value)], + }); + } + } + // 6. 条件分岐 let pre_branch_bb = self.current_block()?; + + // Fix for ValueId UseBeforeDef bug: Build header snapshot from PHI values + // The exit PHI must reference values that are defined AT THE HEADER BLOCK'S EXIT. + // We can't use the current variable_map directly because it might contain values + // that are only partially defined. Instead, use the PHI values from incomplete_phis + // and any new pinned variables created during condition evaluation. + let mut header_exit_snapshot = std::collections::HashMap::new(); + + // First, collect all PHI values (these are defined at the header entry) + for inc in &incs { + header_exit_snapshot.insert(inc.var_name.clone(), inc.phi_id); + } + + // Then, add any new pinned variables created during condition evaluation + // (these were added as PHIs in lines 204-219) + let post_cond_vars = self.get_current_variable_map(); + for (name, &value) in post_cond_vars.iter() { + if !header_exit_snapshot.contains_key(name) { + header_exit_snapshot.insert(name.clone(), value); + } + } + self.emit_branch(condition_value, body_id, after_loop_id)?; let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, header_id); let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, after_loop_id, header_id); @@ -197,6 +252,13 @@ impl<'a> LoopBuilder<'a> { ); } + // Save the header snapshot for exit PHI generation + crate::mir::phi_core::loop_phi::save_block_snapshot( + &mut self.block_var_maps, + header_id, + &header_exit_snapshot, + ); + // Rebind loop-carried variables to their PHI IDs now that condition is emitted for inc in &incs { self.update_variable(inc.var_name.clone(), inc.phi_id); @@ -208,10 +270,12 @@ impl<'a> LoopBuilder<'a> { self.parent_builder .debug_replace_region(format!("loop#{}", loop_id) + "/body"); // Materialize pinned slots at entry via single-pred Phi - let names: Vec = self.parent_builder.variable_map.keys().cloned().collect(); + // IMPORTANT: Use header_exit_snapshot, not current variable_map, to avoid + // referencing values that are defined after the header's branch instruction. + let names: Vec = header_exit_snapshot.keys().cloned().collect(); for name in names { if !name.starts_with("__pin$") { continue; } - if let Some(&pre_v) = self.parent_builder.variable_map.get(&name) { + if let Some(&pre_v) = header_exit_snapshot.get(&name) { let phi_val = self.new_value(); self.emit_phi_at_block_start(body_id, phi_val, vec![(pre_branch_bb, pre_v)])?; self.update_variable(name, phi_val); @@ -366,7 +430,13 @@ impl<'a> LoopBuilder<'a> { /// Exitブロックで変数のPHIを生成(breakポイントでの値を統一) fn create_exit_phis(&mut self, header_id: BasicBlockId, exit_id: BasicBlockId) -> Result<(), String> { - let header_vars = self.get_current_variable_map(); + // Use the saved header block snapshot instead of current variable map + // The current block at this point is the exit block, not the header, + // so we must retrieve the header's snapshot from block_var_maps. + let header_vars = self.block_var_maps + .get(&header_id) + .cloned() + .unwrap_or_else(|| self.get_current_variable_map()); let exit_snaps = self.exit_snapshots.clone(); crate::mir::phi_core::loop_phi::build_exit_phis_with( self,