feat(joinir): Phase 188 Pattern 1 tail call parameter binding

Problem: Tail call conversion (Call→Jump) was failing because:
1. Function parameters were not bound, causing "undefined value" errors
2. Instructions before Call (like BoxCall for print) were being skipped

Solution: Implemented two-pass tail call conversion:
- First pass: Process all instructions, detect tail calls, skip only Call itself
- Second pass: Insert Copy instructions to bind call arguments to parameters

Implementation:
- Lines 517-609: Two-pass approach with tail_call_target tracking
- Lines 588-603: Parameter binding via Copy instructions (arg → param)
- Lines 566-573: Debug logging for BoxCall verification

Example binding:
  Call(loop_step, [i_next])
  → Copy { dst: param_i, src: i_next }
  → Jump(loop_step_entry)

Status: JoinIR firing , parameter binding 
Blocker: Non-deterministic HashMap iteration (next fix)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-05 11:40:05 +09:00
parent 074fab8459
commit 2c68b04e49

View File

@ -514,6 +514,9 @@ impl super::MirBuilder {
// Remap instructions (Phase 189: Convert inter-function Calls to control flow)
let mut found_tail_call = false;
let mut tail_call_target: Option<(BasicBlockId, Vec<ValueId>)> = None;
// First pass: Process all instructions, identify tail calls
for inst in &old_block.instructions {
// Phase 189: Skip Const String instructions that define function names
if let MirInstruction::Const { dst, value } = inst {
@ -527,26 +530,30 @@ impl super::MirBuilder {
}
}
// Phase 189: Convert Call to Jump terminator
if let MirInstruction::Call { func, .. } = inst {
// Phase 189: Detect tail calls and save parameters
if let MirInstruction::Call { func, args, .. } = inst {
if let Some(func_name) = value_to_func_name.get(func) {
if let Some(&target_block) = function_entry_map.get(func_name) {
// This is a tail call - save info and skip the Call instruction itself
let remapped_args: Vec<ValueId> = args
.iter()
.map(|&v| value_map.get(&v).copied().unwrap_or(v))
.collect();
tail_call_target = Some((target_block, remapped_args));
found_tail_call = true;
if debug {
eprintln!(
"[cf_loop/joinir] Call(name='{}') → Jump terminator to {:?}",
func_name, target_block
"[cf_loop/joinir] Detected tail call to '{}' (args={:?}), will convert to Jump",
func_name, args
);
}
// Set the terminator to Jump and stop processing this block
new_block.terminator = Some(MirInstruction::Jump {
target: target_block,
});
found_tail_call = true;
break; // Don't process further instructions
continue; // Skip the Call instruction itself
}
}
}
// Process regular instructions
let remapped = Self::remap_joinir_instruction(
inst,
&value_map,
@ -555,8 +562,51 @@ impl super::MirBuilder {
&value_to_func_name,
debug,
);
if debug {
match inst {
MirInstruction::BoxCall { .. } => {
eprintln!("[cf_loop/joinir] Adding BoxCall to block {:?}: {:?}", new_block_id, inst);
}
_ => {}
}
}
new_block.instructions.push(remapped);
}
// Second pass: Insert parameter bindings for tail calls
if let Some((target_block, args)) = tail_call_target {
if debug {
eprintln!(
"[cf_loop/joinir] Inserting param bindings for tail call to {:?}",
target_block
);
}
// Insert Copy instructions for parameter binding
for (i, arg_val_remapped) in args.iter().enumerate() {
let param_val_original = ValueId(i as u32);
if let Some(&param_val_remapped) = value_map.get(&param_val_original) {
new_block.instructions.push(MirInstruction::Copy {
dst: param_val_remapped,
src: *arg_val_remapped,
});
if debug {
eprintln!(
"[cf_loop/joinir] Param binding: arg {:?} → param {:?}",
arg_val_remapped, param_val_remapped
);
}
}
}
// Set terminator to Jump
new_block.terminator = Some(MirInstruction::Jump {
target: target_block,
});
}
new_block.instruction_spans = old_block.instruction_spans.clone();
// Remap terminator (convert Return → Jump to exit) if not already set by tail call