# Phase 131 P1.5: Root Cause Analysis Summary Status: Analysis Complete Date: 2025-12-18 Scope: Phase 131 P1.5 variable propagation failure diagnosis ## TL;DR **Problem**: loop(true) { x=1; break }; return x → returns 0 instead of 1 **Root Cause**: Using PHI-based merge pipeline for Normalized IR's continuation-based control flow **Solution**: Option B - Direct env→host wiring via ExitReconnectorBox (PHI-free, matches Normalized design) ## Investigation Timeline ### Step 1: Verify ExitMeta Creation ✅ **Location**: `src/mir/control_tree/normalized_shadow/loop_true_break_once.rs:317-327` **Code**: ```rust let mut exit_values = Vec::new(); for (i, var_name) in env_layout.writes.iter().enumerate() { let k_exit_param_vid = k_exit_func.params[i]; exit_values.push((var_name.clone(), k_exit_param_vid)); } let exit_meta = ExitMeta { exit_values }; ``` **Trace Evidence**: ``` [trace:exit-line] collector: Collecting 1 exit values [trace:exit-line] collector: checking carrier 'x' in variable_ctx.variable_map [trace:exit-line] collector: collected 'x' JoinIR ValueId(6) → HOST ValueId(2), role=LoopState ``` **Conclusion**: ✅ ExitMeta is created correctly. `x` → `ValueId(6)` (k_exit param) is captured. ### Step 2: Verify ExitMetaCollector ✅ **Location**: `src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs:77-261` **Trace Evidence**: ``` [trace:exit-line] collector: collected 1 bindings: [LoopExitBinding { carrier_name: "x", join_exit_value: ValueId(6), host_slot: ValueId(2), role: LoopState }] ``` **Conclusion**: ✅ ExitMetaCollector works correctly. Boundary is created with proper mapping. ### Step 3: Verify variable_map Update ✅ **Trace Evidence**: ``` [joinir/exit-line] variable_ctx.variable_map['x'] ValueId(2) → ValueId(10) ``` **Conclusion**: ✅ variable_map is updated with exit PHI result. ### Step 4: Identify PHI Mismatch ❌ **Trace Evidence**: ``` [DEBUG-177] Exit block PHI (carrier 'x'): ValueId(10) = phi [(BasicBlockId(39), ValueId(4))] [ERROR] ❌ [rust-vm] VM error: Invalid instruction: phi pred mismatch at ValueId(10): no input for predecessor BasicBlockId(42) ``` **Conclusion**: ❌ **PHI node has wrong predecessors** - Has input from: BasicBlockId(39) - Expected input from: BasicBlockId(42) - This is a **CFG structure mismatch** ## Root Cause: CFG Structure Incompatibility ### Standard Loop CFG (Pattern 1-4) ``` entry ↓ header ←─────┐ ├─→ body │ │ ↓ │ │ latch ──┘ (loop back edge) ↓ exit ↓ PHI = merge(break_edge, fallthrough) ``` **PHI needs inputs from**: - Break edge from loop body - Fallthrough from header (condition false) ### Normalized IR CFG (Continuation-based) ``` main(env) ↓ TailCall loop_step(env) ├─→ (true) TailCall → loop_body(env) │ ↓ TailCall ↓ k_exit(env) ──→ return env.x └─→ (false) TailCall → k_exit(env) ──→ return env.x ``` **No loop back edge!** All connections are tail-calls. **k_exit receives env as parameter** - this IS the final value (SSOT), no merging needed! ## Why PHI Generation Fails The merge pipeline (`JoinIRConversionPipeline`) assumes: 1. Loop has a header with PHI nodes (for loop variables) 2. Exit block needs PHI to merge values from multiple loop exits 3. CFG has loop back edges and break edges **But Normalized IR**: 1. No header PHI (env parameters are passed via tail-calls) 2. k_exit env parameter IS the final value (no merge needed) 3. No loop back edges (only forward tail-calls) **The PHI generation logic doesn't match Normalized's control flow structure.** ## Why Option B is Correct ### Option B: Direct env→host Wiring (PHI-free) **Principle**: k_exit env parameters are SSOT for final values **Implementation**: ```rust // For Normalized IR only for (var_name, k_exit_param_vid) in exit_meta.exit_values { // k_exit_param_vid is already the final value! variable_map.insert(var_name, k_exit_param_vid); } // No PHI generation needed ``` **Why it works**: 1. **Matches Normalized design**: PHI-free continuations 2. **SSOT**: k_exit parameters are the canonical final values 3. **Simple**: No CFG analysis, no predecessor tracking 4. **Maintainable**: Works regardless of Normalized IR structure ### Option A: Fix PHI Generation (Rejected) **Would require**: 1. Analyze Normalized IR's tail-call CFG structure 2. Map k_exit parameters to PHI inputs 3. Handle edge cases (conditional jumps, multiple exits) 4. Keep in sync with Normalized IR structure changes **Problems**: 1. **Violates PHI-free design**: Normalized IR shouldn't generate PHI 2. **Complex**: CFG analysis for continuation-based control flow 3. **Fragile**: Breaks when Normalized IR structure changes 4. **Wrong abstraction**: k_exit params ARE the truth, why create PHI? ## Solution: ExitReconnectorBox ### Responsibility Generate direct Copy instructions from k_exit env params to host variable_map (Normalized IR only) ### Contract **Input**: - `env_layout.writes`: Variables written in loop - `k_exit_params`: k_exit function parameters (SSOT for final values) - `exit_values`: Mapping from ExitMeta **Output**: - Direct variable_map updates (no PHI) **Invariant**: - Only used for Normalized shadow path - Standard loops (Pattern 1-4) continue using existing merge pipeline ### Location `src/mir/control_tree/normalized_shadow/exit_reconnector.rs` **Rationale**: Normalized-specific logic stays in normalized_shadow/ module ## Verification Strategy ### Unit Tests 1. ExitReconnectorBox::reconnect() basic cases 2. Strict mode catches missing variables 3. Empty writes → empty bindings ### Integration Tests 1. phase131_loop_true_break_once_vm.sh → RC=1 (expected) 2. phase131_loop_true_break_once_llvm_exe.sh → RC=1 3. Regression: phase130, phase97 (既定挙動不変) ## Key Insights 1. **ExitMeta creation was NOT the problem** - it was already working correctly 2. **Boundary creation was NOT the problem** - ExitMetaCollector worked correctly 3. **The problem was using PHI-based merge** for a continuation-based control flow 4. **k_exit env parameters are SSOT** - no merging/PHI needed 5. **Option B respects Normalized IR's PHI-free design** - the right abstraction ## Next Steps See `p1.5-option-b-analysis.md` for: - Detailed implementation plan - Testing strategy - Commit sequence - Fail-Fast verifier design ## Related Documents - Phase 131 README: `docs/development/current/main/phases/phase-131/README.md` - Option B Analysis: `docs/development/current/main/phases/phase-131/p1.5-option-b-analysis.md` - Now: `docs/development/current/main/10-Now.md`