2025-12-15 03:17:31 +09:00
|
|
|
"""
|
|
|
|
|
Phase 132-Post: PHI Management Box
|
|
|
|
|
|
|
|
|
|
Box-First principle: Encapsulate PHI lifecycle management
|
|
|
|
|
- Track PHI ownership (which block created which PHI)
|
|
|
|
|
- Protect PHIs from overwrites (SSOT principle)
|
|
|
|
|
- Filter vmap to preserve PHI values
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class PhiManager:
|
|
|
|
|
"""PHI value lifecycle manager (Box pattern)"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.predeclared = {} # (bid, vid) -> phi_value
|
|
|
|
|
|
|
|
|
|
def register_phi(self, bid: int, vid: int, phi_value):
|
|
|
|
|
"""Register a PHI as owned by specific block"""
|
|
|
|
|
self.predeclared[(bid, vid)] = phi_value
|
|
|
|
|
|
|
|
|
|
def is_phi_owned(self, bid: int, vid: int) -> bool:
|
|
|
|
|
"""Check if PHI is owned by block"""
|
|
|
|
|
return (bid, vid) in self.predeclared
|
|
|
|
|
|
|
|
|
|
def filter_vmap_preserve_phis(self, vmap: dict, target_bid: int) -> dict:
|
|
|
|
|
"""Filter vmap while preserving owned PHIs
|
|
|
|
|
|
|
|
|
|
SSOT: PHIs in vmap are the single source of truth
|
feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation
## Problem
`block_end_values` used block ID only as key, causing collisions when
multiple functions share the same block IDs (e.g., bb0 in both
condition_fn and main).
## Root Cause
- condition_fn's bb0 → block_end_values[0]
- main's bb0 → block_end_values[0] (OVERWRITES!)
- PHI resolution gets wrong snapshot → dominance error
## Solution (Box-First principle)
Change key from `int` to `Tuple[str, int]` (func_name, block_id):
```python
# Before
block_end_values: Dict[int, Dict[int, ir.Value]]
# After
block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]]
```
## Files Modified (Python - 6 files)
1. `llvm_builder.py` - Type annotation update
2. `function_lower.py` - Pass func_name to lower_blocks
3. `block_lower.py` - Use tuple keys for snapshot save/load
4. `resolver.py` - Add func_name parameter to resolve_incoming
5. `wiring.py` - Thread func_name through PHI wiring
6. `phi_manager.py` - Debug traces
## Files Modified (Rust - cleanup)
- Removed deprecated `loop_to_join.rs` (297 lines deleted)
- Updated pattern lowerers for cleaner exit handling
- Added lifecycle management improvements
## Verification
- ✅ Pattern 1: VM RC: 3, LLVM Result: 3 (no regression)
- ⚠️ Case C: Still has dominance error (separate root cause)
- Needs additional scope fixes (phi_manager, resolver caches)
## Design Principles
- **Box-First**: Each function is an isolated Box with scoped state
- **SSOT**: (func_name, block_id) uniquely identifies block snapshots
- **Fail-Fast**: No cross-function state contamination
## Known Issues (Phase 132-P1)
Other function-local state needs same treatment:
- phi_manager.predeclared
- resolver caches (i64_cache, ptr_cache, etc.)
- builder._jump_only_blocks
## Documentation
- docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md
- docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 05:36:50 +09:00
|
|
|
Phase 132-P0: Also add PHIs from predeclared registry
|
2025-12-15 03:17:31 +09:00
|
|
|
"""
|
|
|
|
|
result = {}
|
|
|
|
|
for vid, val in vmap.items():
|
2025-12-17 03:51:03 +09:00
|
|
|
# PHIs are valid SSA values across dominated blocks; keep them in the per-block view.
|
|
|
|
|
result[vid] = val
|
feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation
## Problem
`block_end_values` used block ID only as key, causing collisions when
multiple functions share the same block IDs (e.g., bb0 in both
condition_fn and main).
## Root Cause
- condition_fn's bb0 → block_end_values[0]
- main's bb0 → block_end_values[0] (OVERWRITES!)
- PHI resolution gets wrong snapshot → dominance error
## Solution (Box-First principle)
Change key from `int` to `Tuple[str, int]` (func_name, block_id):
```python
# Before
block_end_values: Dict[int, Dict[int, ir.Value]]
# After
block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]]
```
## Files Modified (Python - 6 files)
1. `llvm_builder.py` - Type annotation update
2. `function_lower.py` - Pass func_name to lower_blocks
3. `block_lower.py` - Use tuple keys for snapshot save/load
4. `resolver.py` - Add func_name parameter to resolve_incoming
5. `wiring.py` - Thread func_name through PHI wiring
6. `phi_manager.py` - Debug traces
## Files Modified (Rust - cleanup)
- Removed deprecated `loop_to_join.rs` (297 lines deleted)
- Updated pattern lowerers for cleaner exit handling
- Added lifecycle management improvements
## Verification
- ✅ Pattern 1: VM RC: 3, LLVM Result: 3 (no regression)
- ⚠️ Case C: Still has dominance error (separate root cause)
- Needs additional scope fixes (phi_manager, resolver caches)
## Design Principles
- **Box-First**: Each function is an isolated Box with scoped state
- **SSOT**: (func_name, block_id) uniquely identifies block snapshots
- **Fail-Fast**: No cross-function state contamination
## Known Issues (Phase 132-P1)
Other function-local state needs same treatment:
- phi_manager.predeclared
- resolver caches (i64_cache, ptr_cache, etc.)
- builder._jump_only_blocks
## Documentation
- docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md
- docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 05:36:50 +09:00
|
|
|
|
|
|
|
|
# Phase 132-P0: Add PHIs from predeclared that aren't in vmap yet
|
|
|
|
|
for (bid, vid), phi_val in self.predeclared.items():
|
|
|
|
|
if bid == target_bid and vid not in result:
|
|
|
|
|
result[vid] = phi_val
|
|
|
|
|
|
2025-12-15 03:17:31 +09:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def sync_protect_phis(self, target_vmap: dict, source_vmap: dict):
|
|
|
|
|
"""Sync values but protect existing PHIs (Fail-Fast)
|
|
|
|
|
|
|
|
|
|
Never overwrite PHIs - they are SSOT
|
|
|
|
|
"""
|
|
|
|
|
for vid, val in source_vmap.items():
|
|
|
|
|
existing = target_vmap.get(vid)
|
|
|
|
|
if existing and hasattr(existing, 'add_incoming'):
|
|
|
|
|
continue # SSOT: Don't overwrite PHIs
|
|
|
|
|
target_vmap[vid] = val
|