feat(plan): Phase 286C-4 Step 2 - Complete plan_rewrites() (~370 lines)
Added parameter binding insertion and terminator conversion to plan_rewrites(): **Parameter Binding (~100 lines)**: - Generate Copy instructions for tail call arguments - Handle recursive/continuation/normal tail calls - Skip bindings for loop header PHIs - Record latch incoming for loop header PHI **Terminator Conversion (~270 lines)**: - Return → Jump conversion using ReturnConverterBox - Exit value collection using ExitArgsCollectorBox - Update result.phi_inputs and result.carrier_inputs - Handle tail call Jump terminators with classification Plan stage now generates complete RewrittenBlocks ready for apply stage. Build passes with warnings only.
This commit is contained in:
@ -473,17 +473,420 @@ fn plan_rewrites(
|
||||
new_block.instructions.push(remapped_with_blocks);
|
||||
}
|
||||
|
||||
// TODO: Second pass - insert parameter bindings for tail calls
|
||||
// This requires significant logic extraction from merge_and_rewrite
|
||||
// For Phase 286C-4 Step 2, we'll handle this in a follow-up commit
|
||||
// Second pass: Insert parameter bindings for tail calls (if any)
|
||||
if let Some((target_block, args)) = tail_call_target {
|
||||
// Find the target function name from the target_block
|
||||
let mut target_func_name: Option<String> = None;
|
||||
for (fname, &entry_block) in &ctx.function_entry_map {
|
||||
if entry_block == target_block {
|
||||
target_func_name = Some(fname.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if target is continuation/recursive/loop entry
|
||||
let is_target_continuation = target_func_name
|
||||
.as_ref()
|
||||
.map(|name| continuation_candidates.contains(name))
|
||||
.unwrap_or(false);
|
||||
|
||||
let is_recursive_call = target_func_name
|
||||
.as_ref()
|
||||
.map(|name| name == func_name)
|
||||
.unwrap_or(false);
|
||||
|
||||
if let Some(ref target_func_name) = target_func_name {
|
||||
if let Some(target_params) = function_params.get(target_func_name) {
|
||||
let is_target_loop_entry = entry_func_name == Some(target_func_name.as_str());
|
||||
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Tail call param binding: from='{}' to='{}' (recursive={}, loop_entry={}, continuation={})",
|
||||
func_name, target_func_name, is_recursive_call, is_target_loop_entry, is_target_continuation
|
||||
);
|
||||
|
||||
// Skip parameter binding in specific cases:
|
||||
// 1. Loop entry point (header PHIs define carriers)
|
||||
// 2. Recursive/entry call to loop header with PHIs (latch edge)
|
||||
// 3. Continuation call (handled separately below)
|
||||
if is_loop_entry_point {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Skip param bindings in header block (PHIs define carriers)"
|
||||
);
|
||||
} else if (is_recursive_call || is_target_loop_entry) && is_loop_header_with_phi {
|
||||
// Update remapper mappings for continuation instructions
|
||||
for (i, arg_val_remapped) in args.iter().enumerate() {
|
||||
if i < target_params.len() {
|
||||
let param_val_original = target_params[i];
|
||||
remapper.set_value(param_val_original, *arg_val_remapped);
|
||||
}
|
||||
}
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Skip Copy bindings for {} call (remapper updated)",
|
||||
if is_recursive_call { "recursive" } else { "entry" }
|
||||
);
|
||||
} else if is_target_continuation {
|
||||
// Continuation call: Copy args to original params
|
||||
for (i, arg_val_remapped) in args.iter().enumerate() {
|
||||
if i < target_params.len() {
|
||||
let param_val_original = target_params[i];
|
||||
new_block.instructions.push(MirInstruction::Copy {
|
||||
dst: param_val_original,
|
||||
src: *arg_val_remapped,
|
||||
});
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Continuation param binding: {:?} = copy {:?}",
|
||||
param_val_original, arg_val_remapped
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal tail call: Insert Copy instructions
|
||||
for (i, arg_val_remapped) in args.iter().enumerate() {
|
||||
if i < target_params.len() {
|
||||
let param_val_original = target_params[i];
|
||||
let param_remap_result = remapper.get_value(param_val_original);
|
||||
let param_val_dst = param_remap_result.unwrap_or(param_val_original);
|
||||
|
||||
// Check if this would overwrite a header PHI dst
|
||||
let is_header_phi_dst = loop_header_phi_info
|
||||
.carrier_phis
|
||||
.values()
|
||||
.any(|entry| entry.phi_dst == param_val_dst);
|
||||
|
||||
if is_header_phi_dst {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Skip param binding to PHI dst {:?}",
|
||||
param_val_dst
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
new_block.instructions.push(MirInstruction::Copy {
|
||||
dst: param_val_dst,
|
||||
src: *arg_val_remapped,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record latch incoming for loop header PHI (recursive calls)
|
||||
if is_recursive_call {
|
||||
if let Some(b) = boundary {
|
||||
if let Some(loop_var_name) = &b.loop_var_name {
|
||||
if !args.is_empty() {
|
||||
let latch_value = args[0];
|
||||
loop_header_phi_info.set_latch_incoming(
|
||||
loop_var_name,
|
||||
new_block_id,
|
||||
latch_value,
|
||||
);
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Set latch incoming for '{}': block={:?}, value={:?}",
|
||||
loop_var_name, new_block_id, latch_value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Set latch incoming for other carriers
|
||||
let mut carrier_arg_idx = if b.loop_var_name.is_some() { 1 } else { 0 };
|
||||
for binding in b.exit_bindings.iter() {
|
||||
if let Some(ref loop_var) = b.loop_var_name {
|
||||
if &binding.carrier_name == loop_var {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if carrier_arg_idx < args.len() {
|
||||
let latch_value = args[carrier_arg_idx];
|
||||
loop_header_phi_info.set_latch_incoming(
|
||||
&binding.carrier_name,
|
||||
new_block_id,
|
||||
latch_value,
|
||||
);
|
||||
carrier_arg_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set latch incoming for loop invariants
|
||||
for (inv_name, _inv_host_id) in b.loop_invariants.iter() {
|
||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(inv_name) {
|
||||
loop_header_phi_info.set_latch_incoming(
|
||||
inv_name,
|
||||
new_block_id,
|
||||
phi_dst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize spans
|
||||
new_block.instruction_spans = sync_spans(&new_block.instructions, old_block);
|
||||
|
||||
// TODO: Plan terminator conversion
|
||||
// For now, copy the old terminator (will be refined in future commits)
|
||||
if let Some(ref term) = old_block.terminator {
|
||||
new_block.set_terminator(remapper.remap_instruction(term));
|
||||
// Terminator conversion
|
||||
if !found_tail_call {
|
||||
if let Some(ref term) = old_block.terminator {
|
||||
match term {
|
||||
MirInstruction::Return { value } => {
|
||||
// Check if we should keep Return or convert to Jump
|
||||
if ReturnConverterBox::should_keep_return(is_continuation_candidate, is_skippable_continuation) {
|
||||
// Non-skippable continuation: keep Return
|
||||
let remapped_value = ReturnConverterBox::remap_return_value(*value, |v| remapper.remap_value(v));
|
||||
new_block.set_terminator(MirInstruction::Return { value: remapped_value });
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Keeping Return for non-skippable continuation '{}' (value={:?})",
|
||||
func_name, remapped_value
|
||||
);
|
||||
} else {
|
||||
// Convert Return to Jump to exit block
|
||||
let mut exit_edge_args: Option<crate::mir::EdgeArgs> = None;
|
||||
if value.is_some() {
|
||||
if let Some(b) = boundary {
|
||||
// Use terminator edge-args from old block
|
||||
if let Some(edge_args) = old_block.edge_args_from_terminator() {
|
||||
if edge_args.layout != b.jump_args_layout {
|
||||
let msg = format!(
|
||||
"[plan_rewrites] exit edge-args layout mismatch: block={:?} edge={:?} boundary={:?}",
|
||||
old_block.id, edge_args.layout, b.jump_args_layout
|
||||
);
|
||||
if ctx.strict_exit {
|
||||
return Err(msg);
|
||||
} else if verbose {
|
||||
log!(true, "[DEBUG] {}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Remap jump_args to HOST value space
|
||||
let remapped_args: Vec<ValueId> = edge_args
|
||||
.values
|
||||
.iter()
|
||||
.map(|&arg| remapper.remap_value(arg))
|
||||
.collect();
|
||||
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Remapped exit jump_args: {:?}",
|
||||
remapped_args
|
||||
);
|
||||
|
||||
// Collect exit values using ExitArgsCollectorBox
|
||||
let edge_args = crate::mir::EdgeArgs {
|
||||
layout: edge_args.layout,
|
||||
values: remapped_args,
|
||||
};
|
||||
exit_edge_args = Some(edge_args.clone());
|
||||
|
||||
let collector = ExitArgsCollectorBox::new();
|
||||
let collection_result = collector.collect(
|
||||
&b.exit_bindings,
|
||||
&edge_args.values,
|
||||
new_block_id,
|
||||
ctx.strict_exit,
|
||||
edge_args.layout,
|
||||
)?;
|
||||
|
||||
// Add expr_result to exit_phi_inputs
|
||||
if let Some(expr_result_val) = collection_result.expr_result_value {
|
||||
result.phi_inputs.push((new_block_id, expr_result_val));
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] exit_phi_inputs: ({:?}, {:?})",
|
||||
new_block_id, expr_result_val
|
||||
);
|
||||
}
|
||||
|
||||
// Add carrier values to carrier_inputs
|
||||
for (carrier_name, (block_id, value_id)) in collection_result.carrier_values {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Collecting carrier '{}': from {:?} value {:?}",
|
||||
carrier_name, block_id, value_id
|
||||
);
|
||||
result.carrier_inputs
|
||||
.entry(carrier_name)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((block_id, value_id));
|
||||
}
|
||||
} else {
|
||||
// Fallback: Use header PHI dst
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Block {:?} has NO jump_args, using header PHI fallback",
|
||||
old_block.id
|
||||
);
|
||||
|
||||
if let Some(loop_var_name) = &b.loop_var_name {
|
||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
|
||||
result.phi_inputs.push((new_block_id, phi_dst));
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] Using header PHI dst {:?} for exit (loop_var='{}')",
|
||||
phi_dst, loop_var_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out ConditionOnly carriers
|
||||
for binding in &b.exit_bindings {
|
||||
if binding.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::ConditionOnly {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(&binding.carrier_name) {
|
||||
result.carrier_inputs
|
||||
.entry(binding.carrier_name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((new_block_id, phi_dst));
|
||||
} else if b.exit_reconnect_mode == crate::mir::join_ir::lowering::carrier_info::ExitReconnectMode::DirectValue {
|
||||
// DirectValue fallback: use host_slot
|
||||
result.carrier_inputs
|
||||
.entry(binding.carrier_name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((new_block_id, binding.host_slot));
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] DirectValue fallback for '{}': using host_slot {:?}",
|
||||
binding.carrier_name, binding.host_slot
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Jump terminator with edge args
|
||||
if let Some(edge_args) = exit_edge_args {
|
||||
new_block.set_jump_with_edge_args(ctx.exit_block_id, Some(edge_args));
|
||||
} else {
|
||||
new_block.set_terminator(MirInstruction::Jump {
|
||||
target: ctx.exit_block_id,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
MirInstruction::Jump { target, edge_args } => {
|
||||
let remapped_term = remap_jump(
|
||||
&remapper,
|
||||
*target,
|
||||
edge_args,
|
||||
&ctx.skipped_entry_redirects,
|
||||
&local_block_map,
|
||||
);
|
||||
new_block.set_terminator(remapped_term);
|
||||
}
|
||||
MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
} => {
|
||||
let remapped_term = remap_branch(
|
||||
&remapper,
|
||||
*condition,
|
||||
*then_bb,
|
||||
*else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
&ctx.skipped_entry_redirects,
|
||||
&local_block_map,
|
||||
);
|
||||
new_block.set_terminator(remapped_term);
|
||||
}
|
||||
_ => {
|
||||
let remapped = remapper.remap_instruction(term);
|
||||
new_block.set_terminator(remapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some((target_block, args)) = tail_call_target {
|
||||
// Tail call: Set Jump terminator
|
||||
// Classify tail call and determine actual target
|
||||
let is_target_continuation = {
|
||||
let mut target_func_name: Option<String> = None;
|
||||
for (fname, &entry_block) in &ctx.function_entry_map {
|
||||
if entry_block == target_block {
|
||||
target_func_name = Some(fname.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
target_func_name
|
||||
.as_ref()
|
||||
.map(|name| continuation_candidates.contains(name))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
let tail_call_kind = classify_tail_call(
|
||||
is_loop_entry_point,
|
||||
!loop_header_phi_info.carrier_phis.is_empty(),
|
||||
boundary.is_some(),
|
||||
is_target_continuation,
|
||||
);
|
||||
|
||||
let actual_target = match tail_call_kind {
|
||||
TailCallKind::BackEdge => {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] BackEdge: redirecting from {:?} to header {:?}",
|
||||
target_block, loop_header_phi_info.header_block
|
||||
);
|
||||
loop_header_phi_info.header_block
|
||||
}
|
||||
TailCallKind::LoopEntry => {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] LoopEntry: using direct target {:?}",
|
||||
target_block
|
||||
);
|
||||
target_block
|
||||
}
|
||||
TailCallKind::ExitJump => {
|
||||
// Check if target is skippable continuation
|
||||
let mut target_func_name: Option<String> = None;
|
||||
for (fname, &entry_block) in &ctx.function_entry_map {
|
||||
if entry_block == target_block {
|
||||
target_func_name = Some(fname.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
let is_target_skippable = target_func_name
|
||||
.as_ref()
|
||||
.map(|name| skippable_continuation_func_names.contains(name))
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_target_skippable {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] ExitJump (skippable): redirecting from {:?} to exit_block_id {:?}",
|
||||
target_block, ctx.exit_block_id
|
||||
);
|
||||
ctx.exit_block_id
|
||||
} else {
|
||||
log!(
|
||||
verbose,
|
||||
"[plan_rewrites] ExitJump (non-skippable): to target_block {:?}",
|
||||
target_block
|
||||
);
|
||||
target_block
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new_block.set_terminator(MirInstruction::Jump {
|
||||
target: actual_target,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Add block to result
|
||||
|
||||
Reference in New Issue
Block a user