Files
hakorune/docs/development/current/main/phase33-11-exit-phi-bug-analysis.md
nyash-codex 7c55baa818 refactor(joinir): Phase 190 convert.rs modularization
- Created joinir_function_converter.rs (~133 lines): Function-level conversion
- Created joinir_block_converter.rs (~691 lines): Block-level conversion
- Reduced convert.rs from 943 → 120 lines (87% reduction)
- Total: 944 lines (original 943 lines, minimal overhead)
- Separation of concerns: Function vs Block responsibilities
- All handlers moved to block_converter for better organization
- Maintained backward compatibility with existing API
- Build successful, simple tests passing
2025-12-05 14:41:24 +09:00

142 lines
3.0 KiB
Markdown

# Phase 33-11: Exit Block PHI Node Missing Bug Analysis
## Problem Summary
The MIR generated from JoinIR lowering is missing PHI nodes in the exit block, causing "use of undefined value" errors.
## Test Case
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
}
}
```
## Error
```
[ERROR] use of undefined value ValueId(16)
```
## MIR Dump Analysis
```mir
bb8:
1: ret %16 # %16 is never defined!
```
bb8 is the exit block, but ValueId(16) is never defined anywhere.
## Root Cause
In `src/mir/builder/control_flow.rs::merge_joinir_mir_blocks()`:
### Step 1: Exit block is created empty (line 618-621)
```rust
let exit_block_id = self.block_gen.next();
// ...
let exit_block = BasicBlock::new(exit_block_id); // Empty!
func.add_block(exit_block);
```
### Step 2: Return instructions are converted to Jump (line 827-841)
```rust
MirInstruction::Return { value } => {
if let Some(ret_val) = value {
let remapped_val = value_map.get(ret_val).copied().unwrap_or(*ret_val);
if debug {
eprintln!(
"[cf_loop/joinir] Return({:?}) → Jump to exit",
remapped_val
);
}
}
MirInstruction::Jump {
target: exit_block_id,
}
}
```
**Problem**: The return value (`remapped_val`) is logged but NOT STORED anywhere!
### Step 3: Exit block stays empty
The exit block is never populated with:
- PHI nodes to collect return values
- Return instruction to return the PHI'd value
## Expected Fix
The exit block should look like:
```mir
bb8:
1: %16 = phi [%9 from bb7], [%2 from bb5], ...
1: ret %16
```
Or simpler, if all functions return the same constant:
```mir
bb8:
1: %16 = const 0
1: ret %16
```
## JoinIR Functions Structure
The Pattern 1 lowerer generates 3 functions:
1. **main()**: Calls loop_step, returns 0
2. **loop_step()**: Tail recursion OR Jump to k_exit
3. **k_exit()**: Returns 0
All Return instructions are converted to Jump to bb8 (exit block).
## Solution Strategy
### Option A: Collect Return Values + Generate PHI
1. While converting Return→Jump, collect all return values
2. After merging, generate PHI node in exit block
3. Add Return instruction that returns PHI result
### Option B: Direct Value Propagation
1. Since Pattern 1 always returns 0, directly emit const 0 in exit block
2. Simpler but less general
### Option C: Track Return Values in Hash Map
1. Create `HashMap<BasicBlockId, ValueId>` to track returns
2. Use as PHI incoming values
3. Most robust, handles all patterns
## Recommendation
Start with **Option B** (simplest fix for Pattern 1), then generalize to **Option C** for future patterns.
## Implementation Location
File: `src/mir/builder/control_flow.rs`
Function: `merge_joinir_mir_blocks()`
Lines: ~827-950
## Test Validation
```bash
NYASH_DISABLE_PLUGINS=1 ./target/release/hakorune apps/tests/loop_min_while.hako
```
Expected output:
```
0
1
2
```