fix(mir): conservative PHI box for If/Loop and Stage1 resolver SSA

This commit is contained in:
nyash-codex
2025-11-18 09:26:39 +09:00
parent fa087eeeea
commit 8b37e9711d
6 changed files with 181 additions and 19 deletions

View File

@ -154,9 +154,11 @@ impl<'a> LoopBuilder<'a> {
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
// Check feature flag for LoopForm PHI v2
// 📦 Default to true (v2 is now stable and default)
// Set NYASH_LOOPFORM_PHI_V2=0 to use legacy version if needed
let use_loopform_v2 = std::env::var("NYASH_LOOPFORM_PHI_V2")
.map(|v| v == "1" || v.to_lowercase() == "true")
.unwrap_or(false);
.unwrap_or(true);
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_loop] use_loopform_v2={}", use_loopform_v2);
@ -165,7 +167,8 @@ impl<'a> LoopBuilder<'a> {
if use_loopform_v2 {
self.build_loop_with_loopform(condition, body)
} else {
eprintln!("⚠️ WARNING: Using legacy loop builder! Set NYASH_LOOPFORM_PHI_V2=1");
eprintln!("⚠️ WARNING: Using legacy loop builder! LoopForm v2 is now default.");
eprintln!("⚠️ Legacy version may be deprecated in future releases.");
self.build_loop_legacy(condition, body)
}
}
@ -222,7 +225,10 @@ impl<'a> LoopBuilder<'a> {
let exit_id = self.new_block();
// Jump from current block to preheader
let entry_block = self.current_block()?;
self.emit_jump(preheader_id)?;
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, preheader_id, entry_block)?;
// Initialize LoopFormBuilder with preheader and header blocks
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
@ -259,6 +265,8 @@ impl<'a> LoopBuilder<'a> {
// Pass 2: Emit preheader (copies and jump to header)
loopform.emit_preheader(self)?;
// 📦 Hotfix 6: Add CFG predecessor for header from preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id)?;
// Pass 3: Emit header PHIs (incomplete, only preheader edge)
self.set_current_block(header_id)?;
@ -301,8 +309,23 @@ impl<'a> LoopBuilder<'a> {
}
}
self.emit_branch(cond_value, body_id, exit_id)?;
// 📦 Hotfix 6: Add CFG predecessors for branch targets (Cytron et al. 1991 requirement)
// This ensures exit_block.predecessors is populated before Exit PHI generation
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}", exit_id, branch_source_block);
}
crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, branch_source_block)?;
crate::mir::builder::loops::add_predecessor(self.parent_builder, exit_id, branch_source_block)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?);
eprintln!("[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
body_id, exit_id, branch_source_block);
// Verify predecessors were added
if let Some(ref func) = self.parent_builder.current_function {
if let Some(exit_block) = func.blocks.get(&exit_id) {
eprintln!("[loopform/condition] exit_block.predecessors = {:?}", exit_block.predecessors);
}
}
}
// Lower loop body
@ -336,6 +359,8 @@ impl<'a> LoopBuilder<'a> {
}
self.emit_jump(header_id)?;
// 📦 Hotfix 6: Add CFG predecessor for header from latch (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, latch_id)?;
// Pass 4: Seal PHIs with latch values
loopform.seal_phis(self, actual_latch_id)?;
@ -1256,6 +1281,19 @@ impl<'a> LoopFormOps for LoopBuilder<'a> {
}
}
fn get_block_predecessors(&self, block: BasicBlockId) -> std::collections::HashSet<BasicBlockId> {
// 📦 Hotfix 6: Get actual CFG predecessors for PHI validation
if let Some(ref func) = self.parent_builder.current_function {
if let Some(bb) = func.blocks.get(&block) {
bb.predecessors.clone()
} else {
std::collections::HashSet::new() // Non-existent blocks have no predecessors
}
} else {
std::collections::HashSet::new()
}
}
fn is_parameter(&self, name: &str) -> bool {
// A parameter is a true function parameter that doesn't change across iterations
// Pinned receivers (__pin$*$@*) are NOT parameters - they're carriers