refactor(llvm): Phase 131-12-P2 - block_end_values SSOT 化(WIP)

## 実装内容
- get_end_values() API 追加
- _value_at_end_i64() を snapshot-only に変更
- def_blocks 即時更新
- PHI incoming を snapshot から取得

## 発見された問題
- 同一ブロック内の def→use が predecessor snapshot を見てしまう
- これは次フェーズで resolve_cur / resolve_incoming 分離で修正

🤖 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-14 21:55:44 +09:00
parent 7dfd6ff1d9
commit eb70dfc5bb
4 changed files with 75 additions and 67 deletions

View File

@ -61,6 +61,8 @@ class Resolver:
self.def_blocks = {}
# Optional: block -> { dst_vid -> [(pred_bid, val_vid), ...] } for PHIs from MIR JSON
self.block_phi_incomings = {}
# P0-1: SSOT for end-of-block values (snapshots)
self.block_end_values = {}
def mark_string(self, value_id: int) -> None:
try:
@ -322,28 +324,34 @@ class Resolver:
self.ptr_cache[cache_key] = result
return result
def get_end_values(self, block_id: int) -> Dict[int, ir.Value]:
"""P0-1: Get end-of-block snapshot (SSOT).
Returns the snapshot of values at the end of the specified block.
This is the single source of truth for PHI incoming resolution.
"""
return self.block_end_values.get(block_id, {})
def _value_at_end_i64(self, value_id: int, block_id: int, preds: Dict[int, list],
block_end_values: Dict[int, Dict[int, Any]], vmap: Dict[int, Any],
bb_map: Optional[Dict[int, ir.Block]] = None,
_vis: Optional[set] = None) -> ir.Value:
"""Resolve value as i64 at the end of a given block by traversing predecessors if needed."""
"""P0-2: Resolve value from block snapshot only (no global search).
This function now ONLY looks at the block_end_values snapshot for the
specified block. It does NOT recursively search predecessors. This
eliminates processing-order dependency bugs.
"""
trace_phi(f"[resolve] end_i64 enter: bb{block_id} v{value_id}")
key = (block_id, value_id)
if key in self._end_i64_cache:
return self._end_i64_cache[key]
if _vis is None:
_vis = set()
if key in _vis:
trace_phi(f"[resolve] cycle detected at end_i64(bb{block_id}, v{value_id}) → 0")
return ir.Constant(self.i64, 0)
_vis.add(key)
# Do not synthesize PHIs here. Placeholders are created in the function prepass.
# If present in snapshot, coerce there
# P0-2: ONLY use snapshot - no predecessor recursion
snap = block_end_values.get(block_id, {})
if value_id in snap and snap[value_id] is not None:
val = snap[value_id]
val = snap.get(value_id)
if val is not None:
is_phi_val = False
try:
is_phi_val = hasattr(val, 'add_incoming')
@ -361,28 +369,16 @@ class Resolver:
except Exception:
belongs_here = False
if belongs_here:
self._end_i64_cache[key] = val
return val
# Otherwise ignore and try predecessors to avoid self-carry from foreign PHI
coerced = self._coerce_in_block_to_i64(val, block_id, bb_map)
self._end_i64_cache[key] = coerced
return coerced
coerced = self._coerce_in_block_to_i64(val, block_id, bb_map)
self._end_i64_cache[key] = coerced
return coerced
# Otherwise, PHI from wrong block -> treat as miss
else:
coerced = self._coerce_in_block_to_i64(val, block_id, bb_map)
self._end_i64_cache[key] = coerced
return coerced
# Try recursively from predecessors
pred_ids = [p for p in preds.get(block_id, []) if p != block_id]
for p in pred_ids:
v = self._value_at_end_i64(value_id, p, preds, block_end_values, vmap, bb_map, _vis)
if v is not None:
self._end_i64_cache[key] = v
return v
# Do not use global vmap here; if not materialized by end of this block
# (or its preds), bail out with zero to preserve dominance.
preds_s = ','.join(str(x) for x in pred_ids)
trace_phi(f"[resolve] end_i64 miss: bb{block_id} v{value_id} preds=[{preds_s}] → 0")
# P0-2: STRICT mode - fail fast on resolution miss
# P0-2: Snapshot miss - STRICT mode error, else fallback to 0
import os
if os.environ.get('NYASH_LLVM_STRICT') == '1':
# Collect diagnostic information
@ -393,19 +389,19 @@ class Resolver:
except Exception:
pass
# Build search path for diagnostics
search_path = [block_id] + pred_ids
snapshot_keys = sorted(list(snap.keys())) if snap else []
raise RuntimeError(
f"[LLVM_PY/STRICT] PHI resolution miss:\n"
f" ValueId: {value_id}\n"
f" Target block: {block_id}\n"
f" Predecessors: [{preds_s}]\n"
f" Search path: {search_path}\n"
f"[LLVM_PY/STRICT] Value not in block snapshot:\n"
f" ValueId: v{value_id}\n"
f" Block: bb{block_id}\n"
f" Available in snapshot: {snapshot_keys}\n"
f" {def_blocks_info}\n"
f" Hint: PHI dst may not be registered in def_blocks, or dominance issue"
f" Hint: Value should be in block_end_values[{block_id}] but is missing"
)
# Non-STRICT: fallback to 0 (for diagnostics)
trace_phi(f"[resolve] end_i64 miss: bb{block_id} v{value_id} → 0")
z = ir.Constant(self.i64, 0)
self._end_i64_cache[key] = z
return z