Files
hakorune/docs/development/current/main/phase33-16-INDEX.md
nyash-codex 4e32a803a7 feat(joinir): Phase 33-22 CommonPatternInitializer & JoinIRConversionPipeline integration
Unifies initialization and conversion logic across all 4 loop patterns,
eliminating code duplication and establishing single source of truth.

## Changes

### Infrastructure (New)
- CommonPatternInitializer (117 lines): Unified loop var extraction + CarrierInfo building
- JoinIRConversionPipeline (127 lines): Unified JoinIR→MIR→Merge flow

### Pattern Refactoring
- Pattern 1: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 2: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 3: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 4: Uses CommonPatternInitializer + JoinIRConversionPipeline (-40 lines)

### Code Reduction
- Total reduction: ~115 lines across all patterns
- Zero code duplication in initialization/conversion
- Pattern files: 806 lines total (down from ~920)

### Quality Improvements
- Single source of truth for initialization
- Consistent conversion flow across all patterns
- Guaranteed boundary.loop_var_name setting (prevents SSA-undef bugs)
- Improved maintainability and testability

### Testing
- All 4 patterns tested and passing:
  - Pattern 1 (Simple While): 
  - Pattern 2 (With Break): 
  - Pattern 3 (If-Else PHI): 
  - Pattern 4 (With Continue): 

### Documentation
- Phase 33-22 inventory and results document
- Updated joinir-architecture-overview.md with new infrastructure

## Breaking Changes
None - pure refactoring with no API changes

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-07 21:02:20 +09:00

8.5 KiB

Phase 33-16: Loop Header PHI SSOT - Documentation Index

Last Updated: 2025-12-07
Status: Complete implementation design ready

Quick Navigation

For Implementation

Start here: phase33-16-visual-guide.md

  • Architecture flow diagram (all 7 phases)
  • Code change map with exact line numbers
  • 7 complete code changes (copy-paste ready)
  • Testing commands

For Understanding

Read first: phase33-16-qa.md

  • Answer to all 5 core questions
  • Exact code snippets with explanations
  • "What you DON'T need" guidance
  • Complete flow summary

For Context

Detailed planning: phase33-16-implementation-plan.md

  • Executive summary
  • Problem analysis
  • 6 concrete implementation steps
  • Testing strategy
  • Risk analysis and mitigation
  • Future enhancements

Quick Summary

Reference: PHASE_33_16_SUMMARY.md

  • TL;DR answers
  • Architecture evolution
  • Implementation scope
  • Key architectural insight
  • Testing roadmap

Original Design

Background: phase33-16-loop-header-phi-design.md

  • Original design document
  • Problem statement
  • Solution overview

Your 5 Questions - Direct Answers

Q1: Where exactly should LoopHeaderPhiBuilder::build() be called?

Location: src/mir/builder/control_flow/joinir/merge/mod.rs, line 107 When: Between Phase 3 (remap_values) and Phase 4 (instruction_rewriter) Why: Phase 3.5 allocates PHI dsts before instruction_rewriter needs them Details: phase33-16-qa.md#q1

Q2: How do I get the header_block_id (loop_step's entry block after remapping)?

Code: remapper.get_block(entry_func_name, entry_func.entry_block)? Key: Entry block is the loop header for Pattern 2 Details: phase33-16-qa.md#q2

Q3: How do I get the loop variable's initial value (host-side)?

Code: remapper.get_value(ValueId(0))? Key: ValueId(0) is always the loop parameter in JoinIR space Details: phase33-16-qa.md#q3

Q4: Where should instruction_rewriter record latch_incoming?

Location: src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs, ~line 300 When: In tail call section, after parameter bindings Code: loop_header_phi_info.set_latch_incoming(loop_var_name, target_block, latch_value) Details: phase33-16-qa.md#q4

Q5: Should the Phase 33-15 skip logic be removed or modified?

Answer: Modify, NOT remove Strategy: Use header PHI dst when available, fallback to parameter Details: phase33-16-qa.md#q5


Implementation at a Glance

Files to Modify

  • src/mir/builder/control_flow/joinir/merge/mod.rs (2 locations: Phase 3.5, Phase 4.5)
  • src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs (5 locations: signature, latch tracking, exit logic)

Changes Summary

Phase What Where Type
3.5 Build header PHIs merge/mod.rs New
4 Update signature instruction_rewriter.rs Signature
4 Track latch instruction_rewriter.rs New code
4 Use PHI for exit instruction_rewriter.rs Modified
4 Use PHI for carriers instruction_rewriter.rs Modified
4.5 Finalize PHIs merge/mod.rs New

Scope

  • Lines added: ~100
  • Lines modified: ~100
  • Lines removed: 0 (backward compatible)
  • New files: 0 (uses existing LoopHeaderPhiBuilder)

Architecture Diagram

Phase 3: remap_values()
  ↓ (ValueIds remapped: 0→100, 0→101, etc.)
Phase 3.5: LoopHeaderPhiBuilder::build() ⭐ NEW
  └── Allocate phi_dst(101), entry_incoming(init_value)
  └── Pass loop_header_phi_info to Phase 4
Phase 4: instruction_rewriter::merge_and_rewrite()
  ├── When tail call: set_latch_incoming()
  └── When Return: use phi_dst (not parameter)
Phase 4.5: LoopHeaderPhiBuilder::finalize() ⭐ NEW
  └── Emit PHIs into header block
Phase 5: exit_phi_builder::build_exit_phi()
  └── Return carrier_phis with header PHI dsts
Phase 6: ExitLineOrchestrator::execute()
  └── Update variable_map with carrier_phis

Testing Checklist

Before implementation:

  • Review phase33-16-qa.md (answers to your questions)
  • Review phase33-16-visual-guide.md (code locations)
  • Baseline compile: cargo build --release

During implementation:

  • Implement Phase 3.5 → compile
  • Update signature → compile
  • Add latch tracking → compile
  • Replace exit_phi_inputs → compile
  • Replace carrier_inputs → compile
  • Add Phase 4.5 → compile
  • Update docs → compile

After implementation:

  • NYASH_JOINIR_DEBUG=1 ./target/release/nyash --dump-mir test.hako | grep "Phase 33-16"
  • Verify header block has PHI instructions at start
  • Verify exit values reference header PHI dsts
  • Test: joinir_min_loop.hako produces correct MIR
  • Test: Loop variable values correct at exit

Complete Guides (in order of use)

  1. phase33-16-qa.md (11 KB)

    • Start here for understanding
    • All 5 questions answered
    • Code examples ready to copy-paste
  2. phase33-16-visual-guide.md (22 KB)

    • Use during implementation
    • Architecture diagrams
    • Complete code changes with line numbers
    • Copy-paste ready code
  3. phase33-16-implementation-plan.md (18 KB)

    • For detailed planning
    • Risk analysis
    • Testing strategy
    • Future enhancements
  4. PHASE_33_16_SUMMARY.md (8.2 KB)

    • Quick reference
    • TL;DR answers
    • Key insights
    • Next steps

Reference Documents

  1. phase33-16-loop-header-phi-design.md (9.0 KB)
    • Original design document
    • Historical context

Key Concepts

Loop Header PHI as SSOT (Single Source of Truth)

The core idea: Instead of using undefined loop parameters, use PHI nodes at the loop header to track the "current value" of loop variables.

Why it works:

  • PHI nodes ARE SSA-defined at the header block
  • PHI dsts can safely be referenced in exit values
  • Eliminates SSA-undef errors from undefined parameters

Architecture:

  • Phase 3.5: Pre-allocate PHI dsts (before instruction processing)
  • Phase 4: Set latch incoming during instruction processing
  • Phase 4.5: Finalize PHIs with all incoming edges
  • Phase 6: Use PHI dsts in exit line reconnection

Two-Phase PHI Construction

Phase 3.5: build() - Allocate PHI dsts

let phi_dst = builder.next_value_id();  // Allocate
entry_incoming = (entry_block, init_value);  // Set entry
latch_incoming = None;  // Will be set in Phase 4

Phase 4.5: finalize() - Emit PHI instructions

PHI {
  dst: phi_dst,  // Already allocated
  inputs: [
    (entry_block, init_value),  // From Phase 3.5
    (header_block, latch_value),  // From Phase 4
  ]
}

Known Limitations & Future Work

Phase 33-16 (Current)

Minimal scope: Loop variable only, no other carriers Pattern 2 primary focus (break statements) Backward compatible with Phase 33-15

Phase 33-16+ (Future)

  • Extract multiple carriers from exit_bindings
  • Handle Pattern 3+ with multiple PHIs
  • Pattern-specific header block identification
  • Optimize constant carriers

Support & Debugging

If implementation breaks:

  1. Compilation error: Check if loop_header_phi_info is properly passed as mutable reference
  2. SSA-undef error: Check if finalize() is called and all latch_incoming are set
  3. Wrong values: Check if latch_incoming is set from correct tail call args
  4. No header PHI: Enable NYASH_JOINIR_DEBUG=1 and check "Phase 33-16" output

Debug commands:

# Check debug output
NYASH_JOINIR_DEBUG=1 ./target/release/nyash --dump-mir test.hako 2>&1 | grep "Phase 33-16"

# Inspect MIR
./target/release/nyash --emit-mir-json mir.json test.hako
jq '.functions[0].blocks[0].instructions[0:3]' mir.json

Next Phase: Phase 34+

After Phase 33-16 is complete:

  1. Pattern 4 (continue statement) implementation
  2. Trim patterns with complex carriers
  3. Loop-as-expression full integration
  4. Performance optimizations

Ready to implement? Start with phase33-16-qa.md!