fix(mir): conservative PHI box for If/Loop and Stage1 resolver SSA
This commit is contained in:
@ -20,26 +20,101 @@ impl MirBuilder {
|
||||
else_map_end_opt: &Option<std::collections::HashMap<String, super::ValueId>>,
|
||||
skip_var: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
// 📦 Conservative PHI Generation (Box Theory)
|
||||
// Generate PHI for ALL variables present in ANY branch (union), not just modified ones.
|
||||
// This ensures correctness: if a variable is defined in one branch but not the other,
|
||||
// we use the predecessor value as fallback (or skip if undefined everywhere).
|
||||
//
|
||||
// Theory: Conservative ∘ Elimination = Minimal SSA
|
||||
// - Conservative (this): correctness-first, generate all PHIs
|
||||
// - Elimination (future): efficiency optimization, remove unused PHIs
|
||||
use std::collections::HashSet;
|
||||
|
||||
// Collect all variables from all sources
|
||||
let mut all_vars = HashSet::new();
|
||||
all_vars.extend(pre_if_snapshot.keys().cloned());
|
||||
all_vars.extend(then_map_end.keys().cloned());
|
||||
if let Some(ref else_map) = else_map_end_opt {
|
||||
all_vars.extend(else_map.keys().cloned());
|
||||
}
|
||||
|
||||
// Keep track of which vars were changed (for debugging/hints)
|
||||
let changed = crate::mir::phi_core::if_phi::compute_modified_names(
|
||||
pre_if_snapshot,
|
||||
then_map_end,
|
||||
else_map_end_opt,
|
||||
);
|
||||
use std::collections::HashSet;
|
||||
let changed_set: HashSet<String> = changed.iter().cloned().collect();
|
||||
for name in changed {
|
||||
|
||||
// Debug: Conservative PHI trace
|
||||
let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE").ok().as_deref() == Some("1");
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] all_vars count: {}", all_vars.len());
|
||||
eprintln!("[Conservative PHI] pre_if_snapshot: {:?}", pre_if_snapshot.keys().collect::<Vec<_>>());
|
||||
eprintln!("[Conservative PHI] then_map_end: {:?}", then_map_end.keys().collect::<Vec<_>>());
|
||||
if let Some(ref else_map) = else_map_end_opt {
|
||||
eprintln!("[Conservative PHI] else_map_end: {:?}", else_map.keys().collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
// Generate PHI for all variables (Conservative)
|
||||
for name in all_vars {
|
||||
if skip_var.map(|s| s == name).unwrap_or(false) {
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] Skipping {}: matches skip_var", name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let pre = match pre_if_snapshot.get(name.as_str()) {
|
||||
Some(v) => *v,
|
||||
None => continue, // unknown before-if; skip
|
||||
};
|
||||
let then_v = then_map_end.get(name.as_str()).copied().unwrap_or(pre);
|
||||
let else_v = else_map_end_opt
|
||||
|
||||
// 📦 Conservative PHI: Fallback to predecessor value if not defined in a branch
|
||||
// This handles variables defined in only one branch (e.g., bb16 defines %51, but bb15 doesn't)
|
||||
let pre_val_opt = pre_if_snapshot.get(name.as_str()).copied();
|
||||
|
||||
// Get values from each branch, falling back to predecessor value
|
||||
let then_v_opt = then_map_end.get(name.as_str()).copied()
|
||||
.or(pre_val_opt);
|
||||
let else_v_opt = else_map_end_opt
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(name.as_str()).copied())
|
||||
.unwrap_or(pre);
|
||||
.or(pre_val_opt);
|
||||
|
||||
// 📦 Conservative PHI: Handle variables defined in only ONE branch
|
||||
// If variable exists in one branch but not the other (and not in predecessor),
|
||||
// create a fresh "undefined" ValueId for the missing branch.
|
||||
// This ensures all control flow paths have a definition at the merge point.
|
||||
let (then_v, else_v) = match (then_v_opt, else_v_opt) {
|
||||
(Some(tv), Some(ev)) => {
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] Generating PHI for {}: then={:?} else={:?}", name, tv, ev);
|
||||
}
|
||||
(tv, ev)
|
||||
},
|
||||
(Some(tv), None) => {
|
||||
// Variable exists in then branch but not else or predecessor
|
||||
// Emit a 'const void' instruction to represent undefined value
|
||||
let undef = crate::mir::builder::emission::constant::emit_void(self);
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] One-branch variable {}: then={:?} else=void({:?})", name, tv, undef);
|
||||
}
|
||||
(tv, undef)
|
||||
},
|
||||
(None, Some(ev)) => {
|
||||
// Variable exists in else branch but not then or predecessor
|
||||
// Emit a 'const void' instruction to represent undefined value
|
||||
let undef = crate::mir::builder::emission::constant::emit_void(self);
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] One-branch variable {}: then=void({:?}) else={:?}", name, undef, ev);
|
||||
}
|
||||
(undef, ev)
|
||||
},
|
||||
(None, None) => {
|
||||
// Variable doesn't exist anywhere - skip
|
||||
if trace_conservative {
|
||||
eprintln!("[Conservative PHI] Skipping {}: undefined everywhere", name);
|
||||
}
|
||||
continue
|
||||
}
|
||||
};
|
||||
// フェーズM: 常にPHI命令を使用(no_phi_mode撤廃)
|
||||
// incoming の predecessor は "実際に merge に遷移してくる出口ブロック" を使用する
|
||||
let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user