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:
nyash-codex
2025-12-15 05:43:12 +09:00
parent 3f58f34592
commit 11e794d0ff

View File

@ -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: