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:
2025-12-25 04:01:44 +09:00
parent a78742b6d7
commit 21e855d62a

View File

@ -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