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:
@ -514,6 +514,9 @@ impl super::MirBuilder {
|
|||||||
|
|
||||||
// Remap instructions (Phase 189: Convert inter-function Calls to control flow)
|
// Remap instructions (Phase 189: Convert inter-function Calls to control flow)
|
||||||
let mut found_tail_call = false;
|
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 {
|
for inst in &old_block.instructions {
|
||||||
// Phase 189: Skip Const String instructions that define function names
|
// Phase 189: Skip Const String instructions that define function names
|
||||||
if let MirInstruction::Const { dst, value } = inst {
|
if let MirInstruction::Const { dst, value } = inst {
|
||||||
@ -527,26 +530,30 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 189: Convert Call to Jump terminator
|
// Phase 189: Detect tail calls and save parameters
|
||||||
if let MirInstruction::Call { func, .. } = inst {
|
if let MirInstruction::Call { func, args, .. } = inst {
|
||||||
if let Some(func_name) = value_to_func_name.get(func) {
|
if let Some(func_name) = value_to_func_name.get(func) {
|
||||||
if let Some(&target_block) = function_entry_map.get(func_name) {
|
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 {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Call(name='{}') → Jump terminator to {:?}",
|
"[cf_loop/joinir] Detected tail call to '{}' (args={:?}), will convert to Jump",
|
||||||
func_name, target_block
|
func_name, args
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Set the terminator to Jump and stop processing this block
|
continue; // Skip the Call instruction itself
|
||||||
new_block.terminator = Some(MirInstruction::Jump {
|
|
||||||
target: target_block,
|
|
||||||
});
|
|
||||||
found_tail_call = true;
|
|
||||||
break; // Don't process further instructions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process regular instructions
|
||||||
let remapped = Self::remap_joinir_instruction(
|
let remapped = Self::remap_joinir_instruction(
|
||||||
inst,
|
inst,
|
||||||
&value_map,
|
&value_map,
|
||||||
@ -555,8 +562,51 @@ impl super::MirBuilder {
|
|||||||
&value_to_func_name,
|
&value_to_func_name,
|
||||||
debug,
|
debug,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
match inst {
|
||||||
|
MirInstruction::BoxCall { .. } => {
|
||||||
|
eprintln!("[cf_loop/joinir] Adding BoxCall to block {:?}: {:?}", new_block_id, inst);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_block.instructions.push(remapped);
|
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(¶m_val_remapped) = value_map.get(¶m_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();
|
new_block.instruction_spans = old_block.instruction_spans.clone();
|
||||||
|
|
||||||
// Remap terminator (convert Return → Jump to exit) if not already set by tail call
|
// Remap terminator (convert Return → Jump to exit) if not already set by tail call
|
||||||
|
|||||||
Reference in New Issue
Block a user