feat(joinir): Phase 33-16 Loop Header PHI SSOT with TailCallKind refactoring
## Problem - joinir_min_loop.hako returned 0 instead of expected 2 - Entry block's tail call was redirected to itself (bb4 → bb4 self-loop) - JoinIR function parameters lack SSA definition when inlined ## Solution - Distinguish entry calls from back edges using TailCallKind enum - Entry block (LoopEntry) stays at target, back edges redirect to header PHI - Added explicit classification: LoopEntry, BackEdge, ExitJump ## Key Changes - instruction_rewriter.rs: TailCallKind enum + classify_tail_call() - Renamed is_entry_func_entry_block → is_loop_entry_point (clearer intent) - loop_header_phi_builder.rs: New module for header PHI generation - joinir_inline_boundary_injector.rs: Skip loop var Copy when header PHI handles it ## Verified - joinir_min_loop.hako: returns 2 ✅ - NYASH_SSA_UNDEF_DEBUG: no undefined errors ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -26,6 +26,13 @@ impl BoundaryInjector {
|
||||
///
|
||||
/// * `Ok(())` - 成功
|
||||
/// * `Err(String)` - エラー(ブロックが見つからない等)
|
||||
///
|
||||
/// # Phase 33-16: Loop Variable Header PHI Support
|
||||
///
|
||||
/// When `boundary.loop_var_name` is set, the first join_input (loop variable)
|
||||
/// is handled by the header PHI instead of a Copy instruction. We skip
|
||||
/// emitting the Copy for join_inputs[0] in this case to avoid overwriting
|
||||
/// the PHI result with the initial value.
|
||||
pub fn inject_boundary_copies(
|
||||
func: &mut MirFunction,
|
||||
entry_block_id: BasicBlockId,
|
||||
@ -33,19 +40,28 @@ impl BoundaryInjector {
|
||||
value_map: &HashMap<ValueId, ValueId>,
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
// Phase 33-16: When loop_var_name is set, skip first join_input (handled by header PHI)
|
||||
let skip_first_join_input = boundary.loop_var_name.is_some();
|
||||
|
||||
// Phase 171-fix: Check both join_inputs and condition_bindings
|
||||
let total_inputs = boundary.join_inputs.len() + boundary.condition_bindings.len();
|
||||
let effective_join_inputs = if skip_first_join_input {
|
||||
boundary.join_inputs.len().saturating_sub(1)
|
||||
} else {
|
||||
boundary.join_inputs.len()
|
||||
};
|
||||
let total_inputs = effective_join_inputs + boundary.condition_bindings.len();
|
||||
if total_inputs == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[BoundaryInjector] Phase 171-fix: Injecting {} Copy instructions ({} join_inputs, {} condition_bindings) at entry block {:?}",
|
||||
"[BoundaryInjector] Phase 171-fix: Injecting {} Copy instructions ({} join_inputs, {} condition_bindings) at entry block {:?}{}",
|
||||
total_inputs,
|
||||
boundary.join_inputs.len(),
|
||||
effective_join_inputs,
|
||||
boundary.condition_bindings.len(),
|
||||
entry_block_id
|
||||
entry_block_id,
|
||||
if skip_first_join_input { " (skipping loop var - handled by header PHI)" } else { "" }
|
||||
);
|
||||
}
|
||||
|
||||
@ -58,10 +74,13 @@ impl BoundaryInjector {
|
||||
let mut copy_instructions = Vec::new();
|
||||
|
||||
// Phase 171: Inject Copy instructions for join_inputs (loop parameters)
|
||||
// Phase 33-16: Skip first join_input when loop_var_name is set (header PHI handles it)
|
||||
let skip_count = if skip_first_join_input { 1 } else { 0 };
|
||||
for (join_input, host_input) in boundary
|
||||
.join_inputs
|
||||
.iter()
|
||||
.zip(boundary.host_inputs.iter())
|
||||
.skip(skip_count)
|
||||
.zip(boundary.host_inputs.iter().skip(skip_count))
|
||||
{
|
||||
// リマップ後の ValueId を取得
|
||||
let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input);
|
||||
|
||||
Reference in New Issue
Block a user