wip(mir/loop): partial fix for ValueId use-before-def in loop PHIs
**Problem**: Loop body PHIs reference ValueIds that don't exist at header exit. Example: bb6 uses %14 from bb3, but %14 is only defined in bb6. **Partial Fix**: 1. Create header_exit_snapshot from PHI values + new pinned variables 2. Use snapshot for loop body PHI creation instead of current variable_map 3. Use snapshot for exit PHI generation **Progress**: Error moved from BasicBlockId(4) to BasicBlockId(3) **Remaining**: Circular dependency - PHIs reference other PHIs in same block. Need to ensure snapshot contains only values defined before header branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -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<String> = 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<String> = 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,
|
||||
|
||||
Reference in New Issue
Block a user