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:
nyash-codex
2025-12-07 12:01:54 +09:00
parent a09ce0cbff
commit 287ceca18d
8 changed files with 676 additions and 53 deletions

View File

@ -232,6 +232,12 @@ pub struct JoinInlineBoundary {
///
/// Here, `expr_result = None` because the loop doesn't return a value.
pub expr_result: Option<crate::mir::ValueId>,
/// Phase 33-16: Loop variable name (for LoopHeaderPhiBuilder)
///
/// The name of the loop control variable (e.g., "i" in `loop(i < 3)`).
/// Used to track which PHI corresponds to the loop variable.
pub loop_var_name: Option<String>,
}
impl JoinInlineBoundary {
@ -257,6 +263,7 @@ impl JoinInlineBoundary {
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14: Default to carrier-only pattern
loop_var_name: None, // Phase 33-16
}
}
@ -297,6 +304,7 @@ impl JoinInlineBoundary {
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
@ -333,6 +341,7 @@ impl JoinInlineBoundary {
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
@ -391,6 +400,7 @@ impl JoinInlineBoundary {
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
@ -435,6 +445,7 @@ impl JoinInlineBoundary {
condition_inputs,
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
@ -483,6 +494,7 @@ impl JoinInlineBoundary {
condition_inputs,
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
@ -538,6 +550,7 @@ impl JoinInlineBoundary {
condition_inputs: vec![], // Deprecated, use condition_bindings instead
condition_bindings,
expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16
}
}
}