Files
hakorune/docs/development/current/main/phases/phase-131/p1.5-root-cause-summary.md

229 lines
6.6 KiB
Markdown
Raw Normal View History

# 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`