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>
This commit is contained in:
@ -137,8 +137,13 @@ def nearest_pred_on_path(
|
||||
return None
|
||||
|
||||
|
||||
def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[int, int]]):
|
||||
"""Wire PHI incoming edges for (block_id, dst_vid) using declared (decl_b, v_src) pairs."""
|
||||
def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[int, int]], func_name: str = "unknown"):
|
||||
"""Wire PHI incoming edges for (block_id, dst_vid) using declared (decl_b, v_src) pairs.
|
||||
Phase 132-P0: Accept func_name for tuple-key (func_name, block_id) resolution.
|
||||
|
||||
Args:
|
||||
func_name: Function name for tuple-key (func_name, block_id) in block_end_values
|
||||
"""
|
||||
bb = builder.bb_map.get(block_id)
|
||||
if bb is None:
|
||||
return
|
||||
@ -146,7 +151,8 @@ def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[in
|
||||
phi = None
|
||||
try:
|
||||
snap = getattr(builder, 'block_end_values', {}) or {}
|
||||
cur = (snap.get(int(block_id), {}) or {}).get(int(dst_vid))
|
||||
# Phase 132-P0: Use tuple-key (func_name, block_id)
|
||||
cur = (snap.get((func_name, int(block_id)), {}) or {}).get(int(dst_vid))
|
||||
if cur is not None and hasattr(cur, 'add_incoming'):
|
||||
# Ensure it belongs to the same block
|
||||
cur_bb_name = getattr(getattr(cur, 'basic_block', None), 'name', None)
|
||||
@ -210,7 +216,8 @@ def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[in
|
||||
trace({"phi": "wire_replaced_src", "original": original_vs, "replaced": vs})
|
||||
try:
|
||||
# P0-4: Use resolve_incoming for PHI incoming values
|
||||
val = builder.resolver.resolve_incoming(pred_match, vs)
|
||||
# Phase 132-P0: Pass func_name for tuple-key resolution
|
||||
val = builder.resolver.resolve_incoming(pred_match, vs, func_name=func_name)
|
||||
trace({"phi": "wire_resolved", "vs": vs, "pred": pred_match, "val_type": type(val).__name__})
|
||||
except Exception as e:
|
||||
trace({"phi": "wire_resolve_fail", "vs": vs, "pred": pred_match, "error": str(e)})
|
||||
@ -239,7 +246,13 @@ def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[in
|
||||
return wired
|
||||
|
||||
|
||||
def finalize_phis(builder):
|
||||
def finalize_phis(builder, func_name: str = "unknown"):
|
||||
"""Finalize PHI nodes by wiring their incoming edges.
|
||||
Phase 132-P0: Pass func_name for tuple-key (func_name, block_id) resolution.
|
||||
|
||||
Args:
|
||||
func_name: Function name for tuple-key (func_name, block_id) in block_end_values
|
||||
"""
|
||||
total_blocks = 0
|
||||
total_dsts = 0
|
||||
total_wired = 0
|
||||
@ -247,7 +260,7 @@ def finalize_phis(builder):
|
||||
total_blocks += 1
|
||||
for dst_vid, incoming in (dst_map or {}).items():
|
||||
total_dsts += 1
|
||||
wired = wire_incomings(builder, int(block_id), int(dst_vid), incoming)
|
||||
wired = wire_incomings(builder, int(block_id), int(dst_vid), incoming, func_name=func_name)
|
||||
total_wired += int(wired or 0)
|
||||
trace({"phi": "finalize", "block": int(block_id), "dst": int(dst_vid), "wired": int(wired or 0)})
|
||||
trace({"phi": "finalize_summary", "blocks": int(total_blocks), "dsts": int(total_dsts), "incoming_wired": int(total_wired)})
|
||||
|
||||
Reference in New Issue
Block a user