feat(llvm): Phase 132-P1 - Function-local state clearing at function boundaries
## Problem
Multiple function-scoped caches used (bid, vid) or similar keys without
function namespace, causing cross-function collisions.
## Affected State (now cleared at function start)
1. **PhiManager.predeclared** - (bid, vid) -> phi_value
2. **Resolver caches**:
- i64_cache, ptr_cache, f64_cache - (bb_name, vid) -> value
- _end_i64_cache - (bid, vid) -> value
3. **String caches** - string_ids, string_literals, string_ptrs, etc.
4. **Control flow** - _jump_only_blocks, block_phi_incomings, def_blocks
## Solution (Box-First principle)
Added `_clear_function_local_state(builder)` called at start of each
`lower_function()`. This ensures:
- Each function starts with clean state
- No cross-function ValueId/BlockId collisions
- PHI generation determinism improved
## Implementation
**File**: src/llvm_py/builders/function_lower.py
```python
def _clear_function_local_state(builder):
"""Clear all function-local state at function boundary."""
if hasattr(builder, 'phi_manager'):
builder.phi_manager.predeclared.clear()
# ... clear all caches ...
```
## Design Principles
- **Box-First**: Each function is an isolated Box
- **SSOT**: Function-local state is scoped to function
- **Fail-Fast**: Collision structure eliminated by design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -11,6 +11,74 @@ from phi_wiring import (
|
||||
)
|
||||
|
||||
|
||||
def _clear_function_local_state(builder):
|
||||
"""
|
||||
Phase 132-P1: Clear all function-local state at function boundary.
|
||||
|
||||
Box-First Principle: Prevents cross-function ValueId/BlockId collisions
|
||||
by clearing all function-scoped state when entering a new function.
|
||||
|
||||
Cleared state:
|
||||
1. PhiManager.predeclared - (bid, vid) -> phi_value
|
||||
2. Resolver caches - i64_cache, ptr_cache, f64_cache, _end_i64_cache
|
||||
3. Jump-only blocks - _jump_only_blocks
|
||||
4. PHI incomings - block_phi_incomings, def_blocks
|
||||
5. String/NewBox caches - function-local string handling
|
||||
"""
|
||||
try:
|
||||
# 1. PhiManager predeclared PHIs (bid, vid) -> phi_value
|
||||
if hasattr(builder, 'phi_manager') and hasattr(builder.phi_manager, 'predeclared'):
|
||||
builder.phi_manager.predeclared.clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# 2. Resolver caches (all keyed by block/value IDs)
|
||||
builder.resolver.i64_cache.clear()
|
||||
builder.resolver.ptr_cache.clear()
|
||||
builder.resolver.f64_cache.clear()
|
||||
if hasattr(builder.resolver, '_end_i64_cache'):
|
||||
builder.resolver._end_i64_cache.clear()
|
||||
|
||||
# 3. String-related caches (function-local)
|
||||
if hasattr(builder.resolver, 'string_ids'):
|
||||
builder.resolver.string_ids.clear()
|
||||
if hasattr(builder.resolver, 'string_literals'):
|
||||
builder.resolver.string_literals.clear()
|
||||
if hasattr(builder.resolver, 'string_ptrs'):
|
||||
builder.resolver.string_ptrs.clear()
|
||||
if hasattr(builder.resolver, 'length_cache'):
|
||||
builder.resolver.length_cache.clear()
|
||||
|
||||
# 4. NewBox→string-arg hints (function-local)
|
||||
if hasattr(builder.resolver, 'newbox_string_args') and isinstance(builder.resolver.newbox_string_args, dict):
|
||||
builder.resolver.newbox_string_args.clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# 5. Jump-only blocks (bid -> pred_bid)
|
||||
if hasattr(builder, '_jump_only_blocks'):
|
||||
builder._jump_only_blocks.clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# 6. PHI incomings and def_blocks (function-local)
|
||||
if hasattr(builder, 'block_phi_incomings'):
|
||||
builder.block_phi_incomings.clear()
|
||||
if hasattr(builder, 'def_blocks'):
|
||||
builder.def_blocks.clear()
|
||||
|
||||
# Also clear resolver's references to these
|
||||
if hasattr(builder.resolver, 'block_phi_incomings'):
|
||||
builder.resolver.block_phi_incomings = {}
|
||||
if hasattr(builder.resolver, 'def_blocks'):
|
||||
builder.resolver.def_blocks.clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def lower_function(builder, func_data: Dict[str, Any]):
|
||||
"""Lower a single MIR function to LLVM IR using the given builder context.
|
||||
This is a faithful extraction of NyashLLVMBuilder.lower_function.
|
||||
@ -49,35 +117,8 @@ def lower_function(builder, func_data: Dict[str, Any]):
|
||||
builder.bb_map.clear()
|
||||
except Exception:
|
||||
builder.bb_map = {}
|
||||
# Phase 132-P0: Clear phi_manager per-function to avoid ValueId collisions
|
||||
try:
|
||||
if hasattr(builder, 'phi_manager') and hasattr(builder.phi_manager, 'predeclared'):
|
||||
builder.phi_manager.predeclared.clear()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
# Reset resolver caches keyed by block names
|
||||
builder.resolver.i64_cache.clear()
|
||||
builder.resolver.ptr_cache.clear()
|
||||
builder.resolver.f64_cache.clear()
|
||||
if hasattr(builder.resolver, '_end_i64_cache'):
|
||||
builder.resolver._end_i64_cache.clear()
|
||||
if hasattr(builder.resolver, 'string_ids'):
|
||||
builder.resolver.string_ids.clear()
|
||||
if hasattr(builder.resolver, 'string_literals'):
|
||||
builder.resolver.string_literals.clear()
|
||||
if hasattr(builder.resolver, 'string_ptrs'):
|
||||
builder.resolver.string_ptrs.clear()
|
||||
if hasattr(builder.resolver, 'length_cache'):
|
||||
builder.resolver.length_cache.clear()
|
||||
# Also clear newbox→string-arg hints per function to avoid leakage
|
||||
try:
|
||||
if hasattr(builder.resolver, 'newbox_string_args') and isinstance(builder.resolver.newbox_string_args, dict):
|
||||
builder.resolver.newbox_string_args.clear()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
# Phase 132-P1: Clear all function-local state (prevent cross-function collision)
|
||||
_clear_function_local_state(builder)
|
||||
|
||||
# Phase 131-15-P1: Load value_types metadata from JSON into resolver
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user