Files
hakorune/docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md

224 lines
5.8 KiB
Markdown
Raw Normal View History

# Phase 131-6: MIR SSA Dominance Diagnosis
## Update (Phase 131-10)
Case B (`apps/tests/loop_min_while.hako`) は **LLVM AOT でも `0,1,2` を出して終了**するところまで復旧済み。
この文書は Phase 131-6 時点の「切り分けログ(歴史)」として残し、最終的な到達点と修正の全体像は次を SSOT とする:
- `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`
## Executive Summary
**Status (Phase 131-6 時点)**: ❌ LLVM Backend Bug Confirmed
**Severity**: P0 - Breaks basic loop functionality
**Root Cause (Phase 131-6 時点の仮説)**: PHI node incoming values not properly wired in LLVM IR generation
## Evidence Chain
### 1. Test Case SSOT
**File**: `/tmp/simple_add.hako`
```nyash
static box Main {
main() {
local i
i = 0
i = i + 1
print(i)
return 0
}
}
```
**Expected**: Prints `1`
**Actual (VM)**: ✅ Prints `1`
**Actual (LLVM)**: ✅ Prints `1`
**File**: `apps/tests/loop_min_while.hako`
```nyash
static box Main {
main() {
local i = 0
loop(i < 3) {
print(i)
i = i + 1
}
return 0
}
}
```
**Expected**: Prints `0\n1\n2`
**Actual (VM)**: ✅ Prints `0\n1\n2`
**Actual (LLVM)**: ❌ Prints `0` infinitely (infinite loop)
### 2. MIR Verification
**Command**: `./target/release/hakorune --dump-mir apps/tests/loop_min_while.hako`
**MIR Output** (relevant blocks):
```mir
define i64 @main() {
bb0:
%1 = const 0
%2 = copy %1
br label bb4
bb3:
%17 = const 0
ret %17
bb4:
%3 = phi [%2, bb0], [%12, bb7] // ← PHI node for loop variable
br label bb5
bb5:
%8 = const 3
%9 = icmp Lt %3, %8
%10 = Not %9
br %10, label bb6, label bb7
bb6:
br label bb3
bb7:
extern_call env.console.log(%3) // ← Prints %3 (should increment each iteration)
%11 = const 1
%12 = %3 Add %11 // ← %12 = %3 + 1 (updated value)
br label bb4 // ← Jumps back with %12
}
```
**Analysis**:
- ✅ SSA form is correct
- ✅ All values defined before use within each block
- ✅ PHI node properly declares incoming values: `[%2, bb0]` (initial) and `[%12, bb7]` (loop update)
- ✅ No use-before-def violations
### 3. VM Execution Verification
**Command**: `timeout 2 ./target/release/hakorune apps/tests/loop_min_while.hako`
**Output**:
```
0
1
2
RC: 0
```
**Conclusion**: ✅ MIR is correct, VM interprets it correctly
### 4. LLVM Execution Failure
**Build Command**: `bash tools/build_llvm.sh apps/tests/loop_min_while.hako -o /tmp/loop_test`
**Build Result**: ✅ Success (no errors)
**Run Command**: `/tmp/loop_test`
**Output** (truncated):
```
0
0
0
0
... (repeats infinitely)
```
**Conclusion**: ❌ LLVM backend bug - PHI node not working
## Root Cause Analysis
### Affected Component
**File**: `/home/tomoaki/git/hakorune-selfhost/src/llvm_py/llvm_builder.py`
**Function**: `finalize_phis()` (lines 601-735+)
### Bug Mechanism
The PHI node `%3 = phi [%2, bb0], [%12, bb7]` should:
1. On first iteration: Use %2 (value 0 from bb0)
2. On subsequent iterations: Use %12 (updated value from bb7)
**What's happening**:
- %3 always resolves to 0 (initial value from %2)
- The incoming value from bb7 (%12) is not being properly connected
- Loop variable never increments → infinite loop
### Suspected Code Location
In `finalize_phis()` around lines 670-688:
```python
chosen: Dict[int, ir.Value] = {}
for (b_decl, v_src) in incoming:
try:
bd = int(b_decl); vs = int(v_src)
except Exception:
continue
pred_match = nearest_pred_on_path(bd)
if pred_match is None:
continue
# If self-carry is specified (vs == dst_vid), map to init_src_vid when available
if vs == int(dst_vid) and init_src_vid is not None:
vs = int(init_src_vid) # ← SUSPICIOUS: May cause %12 to be ignored
try:
val = self.resolver._value_at_end_i64(vs, pred_match, self.preds, self.block_end_values, self.vmap, self.bb_map)
except Exception:
val = None
if val is None:
val = ir.Constant(self.i64, 0) # ← Falls back to 0
chosen[pred_match] = val
```
### Hypothesis
The self-carry logic (lines 679-681) or value resolution (line 683) may be incorrectly mapping or failing to retrieve %12 from bb7, causing the PHI to always use the fallback value of 0.
## Next Steps
### Immediate Action Required
1. **Add Trace Logging**:
- Enable `NYASH_CLI_VERBOSE=1` or similar PHI-specific tracing
- Log what values are being wired to each PHI incoming edge
2. **Minimal Fix Verification**:
- Verify `_value_at_end_i64(12, 7, ...)` returns the correct LLVM value
- Check if `nearest_pred_on_path()` correctly identifies bb7 as predecessor of bb4
3. **Test Matrix**:
- Simple Add: ✅ (already passing)
- Loop Min While: ❌ (currently failing)
- Case A/B2 from previous phases: (regression check needed)
### Long-term Solution
Implement structural dominance verification:
- MIR verifier pass to check SSA properties
- LLVM IR verification before object emission
- Automated test for PHI node correctness
## Acceptance Criteria
### Must Pass
1.`tools/build_llvm.sh apps/tests/loop_min_while.hako -o /tmp/loop_test && /tmp/loop_test` outputs `0\n1\n2` and exits
2. ✅ Simple Add still works: `/tmp/simple_add` outputs `1`
3. ✅ No regression in existing LLVM smoke tests
### Documentation
1. ✅ This diagnosis added to `docs/development/current/main/`
2. ✅ Fix explanation added to phase131-3-llvm-lowering-inventory.md
3. ✅ Test case added to prevent regression
## Files Modified (To Be Updated)
- `src/llvm_py/llvm_builder.py` - PHI wiring logic
- `docs/development/current/main/phase131-3-llvm-lowering-inventory.md` - Add Phase 131-6 section
- (potential) Test case addition
## Timeline
- **Diagnosis**: 2025-12-14 (Complete)
- **Fix**: TBD
- **Verification**: TBD