fix(mir/exit_phi): Pass branch_source_block to build_exit_phis()
## Problem Exit PHI generation was using header_id as predecessor, but when build_expression(condition) creates new blocks, the actual branch instruction is emitted from a different block, causing: "phi pred mismatch: no input for predecessor BasicBlockId(X)" ## Solution - Modified build_exit_phis() to accept branch_source_block parameter - Capture actual block after condition evaluation in loop_builder.rs - Use branch_source_block instead of header_id for PHI inputs ## Progress - Error changed from ValueId(5941)/BasicBlockId(4674) to ValueId(5927)/BasicBlockId(4672), showing partial fix - Added comprehensive test suite in mir_loopform_exit_phi.rs - Added debug logging to trace condition block creation ## Status Partial fix - unit tests pass, but Test 2 (Stage-B compilation) still has errors. Needs further investigation of complex nested compilation scenarios. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -105,6 +105,8 @@ impl<'a> LoopBuilder<'a> {
|
||||
// Snapshot variables at break point for exit PHI generation
|
||||
let snapshot = self.get_current_variable_map();
|
||||
let cur_block = self.current_block()?;
|
||||
eprintln!("[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
|
||||
cur_block, snapshot.keys().collect::<Vec<_>>());
|
||||
self.exit_snapshots.push((cur_block, snapshot));
|
||||
|
||||
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) {
|
||||
@ -237,8 +239,20 @@ impl<'a> LoopBuilder<'a> {
|
||||
self.exit_snapshots.clear();
|
||||
|
||||
// Emit condition check in header
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/condition] BEFORE build_expression: current_block={:?}", self.current_block()?);
|
||||
}
|
||||
let cond_value = self.parent_builder.build_expression(condition)?;
|
||||
// Capture the ACTUAL block that emits the branch (might differ from header_id
|
||||
// if build_expression created new blocks)
|
||||
let branch_source_block = self.current_block()?;
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/condition] AFTER build_expression: branch_source_block={:?}", branch_source_block);
|
||||
}
|
||||
self.emit_branch(cond_value, body_id, exit_id)?;
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?);
|
||||
}
|
||||
|
||||
// Lower loop body
|
||||
self.set_current_block(body_id)?;
|
||||
@ -280,7 +294,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
|
||||
// Build exit PHIs for break statements
|
||||
let exit_snaps = self.exit_snapshots.clone();
|
||||
loopform.build_exit_phis(self, exit_id, &exit_snaps)?;
|
||||
loopform.build_exit_phis(self, exit_id, branch_source_block, &exit_snaps)?;
|
||||
|
||||
// Pop loop context
|
||||
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
||||
@ -341,7 +355,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
// 4. ループ変数のPhi nodeを準備
|
||||
// ここでは、ループ内で変更される可能性のある変数を事前に検出するか、
|
||||
// または変数アクセス時に遅延生成する(再束縛は条件式構築後に行う)
|
||||
let incs = self.prepare_loop_variables(header_id, preheader_id, &pre_vars_snapshot)?;
|
||||
let incs = self.prepare_loop_variables(header_id, preheader_id, &pre_vars_snapshot, &assigned_vars)?;
|
||||
|
||||
// 5. 条件評価(Phi nodeの結果を使用)
|
||||
// Heuristic pre-pin: if condition is a comparison, evaluate its operands and pin them
|
||||
@ -566,18 +580,36 @@ impl<'a> LoopBuilder<'a> {
|
||||
// PHI Helpers — prepare/finalize PHIs and block sealing
|
||||
// =============================================================
|
||||
/// ループ変数の準備(事前検出または遅延生成)
|
||||
///
|
||||
/// ポリシー:
|
||||
/// - ループキャリア(ループ本体で再代入される変数)と pinned 変数のみを PHI 対象とする。
|
||||
/// - ループ不変のローカル(text_len / pattern_len など)は preheader 値をそのまま使い、
|
||||
/// 不要な PHI を張らないことで SSA 破綻(同一 ValueId の二重定義)を防ぐ。
|
||||
fn prepare_loop_variables(
|
||||
&mut self,
|
||||
header_id: BasicBlockId,
|
||||
preheader_id: BasicBlockId,
|
||||
pre_vars_snapshot: &std::collections::HashMap<String, ValueId>,
|
||||
assigned_vars: &[String],
|
||||
) -> Result<Vec<crate::mir::phi_core::loop_phi::IncompletePhi>, String> {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Use the variable map captured at preheader (before switching to header)
|
||||
let current_vars = pre_vars_snapshot.clone();
|
||||
// Use the variable map captured at preheader (before switching to header),
|
||||
// but filter to:
|
||||
// - ループキャリア(assigned_vars に含まれる変数)
|
||||
// - pinned 変数(__pin$*): 受信箱など、ループをまたいで値を運ぶ必要があるもの
|
||||
let mut current_vars = std::collections::HashMap::new();
|
||||
for (name, &val) in pre_vars_snapshot.iter() {
|
||||
if name.starts_with("__pin$") {
|
||||
current_vars.insert(name.clone(), val);
|
||||
continue;
|
||||
}
|
||||
if assigned_vars.iter().any(|v| v == name) {
|
||||
current_vars.insert(name.clone(), val);
|
||||
}
|
||||
}
|
||||
// Debug: print current_vars before prepare (guarded by env)
|
||||
let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
|
||||
if dbg {
|
||||
|
||||
Reference in New Issue
Block a user