Files
hakorune/docs/archive/phases/phase-33/phase33-10-joinir-merge-phi-loss-analysis.md
nyash-codex a7dbc15878 feat(joinir): Phase 240-EX - Pattern2 header condition ExprLowerer integration
Implementation:
- Add make_pattern2_scope_manager() helper for DRY
- Header conditions use ExprLowerer for supported patterns
- Legacy fallback for unsupported patterns
- Fail-Fast on supported patterns that fail

Tests:
- 4 new tests (all pass)
- test_expr_lowerer_supports_simple_header_condition_i_less_literal
- test_expr_lowerer_supports_header_condition_var_less_var
- test_expr_lowerer_header_condition_generates_expected_instructions
- test_pattern2_header_condition_via_exprlowerer

Also: Archive old phase documentation (34k lines removed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 00:33:04 +09:00

5.7 KiB

JoinIR→MIR Merge Processing PHI Loss Analysis

Executive Summary

Problem: Pattern 4 (loop_continue_pattern4.hako) loses PHI instructions during JoinIR→MIR merge, while Pattern 3 (loop_if_phi.hako) preserves them correctly.

Critical Finding: Both patterns successfully create PHI instructions during JoinIR→MIR conversion, but Pattern 4's PHI disappears during the merge phase.

Test Cases

Pattern 3: loop_if_phi.hako PHI Preserved

loop(i <= 5) {
  if (i % 2 == 1) { sum = sum + i } else { sum = sum + 0 }
  i = i + 1
}

Lowerer: loop_with_if_phi_minimal.rs JoinIR: Uses Select instruction for if-else PHI in loop body

Pattern 4: loop_continue_pattern4.hako PHI Lost

loop(i < 10) {
  i = i + 1
  if (i % 2 == 0) { continue }
  sum = sum + i
}

Lowerer: loop_with_continue_minimal.rs JoinIR: Uses Select instruction for continue vs. normal path (line 304)

PHI Creation Phase (JoinIR→MIR Conversion)

Pattern 3 - Select→PHI Conversion

[joinir_block/handle_select] Created merge_block BasicBlockId(5) with 1 instructions
[joinir_block/handle_select] After insert: merge_block BasicBlockId(5) has 1 instructions
[joinir_block/finalize_block] Preserving 1 PHI instructions in block BasicBlockId(5)
[joinir/meta]   Block BasicBlockId(5): 5 instructions (1 PHI)

Result: %23 = phi [%20, bb8], [%22, bb9] appears in final MIR bb10

Pattern 4 - Select→PHI Conversion

[joinir_block/handle_select] Created merge_block BasicBlockId(5) with 1 instructions
[joinir_block/handle_select] After insert: merge_block BasicBlockId(5) has 1 instructions
[joinir_block/finalize_block] Preserving 1 PHI instructions in block BasicBlockId(5)
[joinir/meta]   Block BasicBlockId(5): 3 instructions (1 PHI)

Result: PHI is created but does NOT appear in final MIR bb10!

Key Difference: Instruction Count

  • Pattern 3: BasicBlockId(5) has 5 instructions (1 PHI + 4 others)
  • Pattern 4: BasicBlockId(5) has 3 instructions (1 PHI + 2 others)

Final MIR Comparison

Pattern 3 - bb10 (from BasicBlockId(5))

bb10:
    1: %23 = phi [%20, bb8], [%22, bb9]  ← PHI PRESENT ✅
    1: %24 = const 1
    1: %25 = %11 Add %24
    1: %11 = copy %25
    1: %12 = copy %23
    1: br label bb5

Pattern 4 - bb10 (from BasicBlockId(5))

bb10:
    1: %8 = copy %14                     ← NO PHI! ❌
    1: br label bb5

Hypothesis: Merge Processing Difference

The merge processing in instruction_rewriter.rs (lines 117-213) should handle PHI instructions correctly:

for inst in &old_block.instructions {
    // ... skip conditions ...

    let remapped = remapper.remap_instruction(inst);

    let remapped_with_blocks = match remapped {
        MirInstruction::Phi { dst, inputs, type_hint: None } => {
            // PHI block remapping (lines 183-196)
            MirInstruction::Phi {
                dst,
                inputs: inputs.iter()
                    .map(|(bb, val)| (local_block_map.get(bb).copied().unwrap_or(*bb), *val))
                    .collect(),
                type_hint: None,
            }
        }
        other => other,
    };

    new_block.instructions.push(remapped_with_blocks);  // Line 212
}

Possible Causes

Theory 1: Block Replacement

  • Pattern 4's bb10 might be getting completely replaced instead of merged
  • Check if finalize_block() in joinir_block_converter.rs (lines 691-727) is being called differently

Theory 2: Tail Call Handling

  • Pattern 4 has a tail call that might trigger different merge logic
  • The tail call detection (lines 146-167) might affect how blocks are merged
  • Pattern 3 may not have a tail call in the same position

Theory 3: Return Conversion

  • BasicBlockId(5) has terminator Return { value: Some(ValueId(99991)) }
  • This gets converted to Jump to exit block (lines 302-320)
  • Something in this conversion might be dropping instructions

Theory 4: Block Overwrite

  • Pattern 4's shorter instruction count (3 vs 5) suggests instructions are being lost
  • Check if the merge process overwrites the block instead of preserving PHI

Investigation Path

  1. Enable debug output for instruction_rewriter.rs merge processing
  2. Check finalize_block calls - are they different between patterns?
  3. Trace BasicBlockId(5) - what happens to it during merge?
  4. Compare Return terminator handling - does the conversion lose instructions?
  5. Check local_block_map - is BasicBlockId(5) being mapped correctly?

Next Steps

  1. Add detailed logging to instruction_rewriter.rs around line 122-213
  2. Trace the exact path Pattern 4's BasicBlockId(5) takes during merge
  3. Compare with Pattern 3's path to find the divergence point
  4. Check if the problem is in the initial block creation or later overwriting

Code Locations

  • JoinIR Block Converter: src/mir/join_ir_vm_bridge/joinir_block_converter.rs
    • handle_select(): lines 407-484 (creates PHI)
    • finalize_block(): lines 691-727 (preserves existing PHI)
  • Merge Processing: src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs
    • merge_and_rewrite(): lines 18-405
    • PHI remapping: lines 183-196
  • Pattern Lowerers:
    • Pattern 3: src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs
    • Pattern 4: src/mir/join_ir/lowering/loop_with_continue_minimal.rs (Select at line 304)

Expected Outcome

Pattern 4's bb10 should contain:

bb10:
    1: %XX = phi [%YY, bb8], [%ZZ, bb9]  ← Missing PHI!
    1: %8 = copy %14
    1: br label bb5

The PHI instruction for sum_merged = Select(continue_cond, sum_param, sum_next) (line 304 in loop_with_continue_minimal.rs) is being lost during merge. Status: Historical