feat(llvm): Phase 131-13/14 - MIR JSON順序修正 & 2パスsnapshot解決

## Phase 131-13: MIR JSON 命令順序修正
- copy 遅延ロジック削除(~80行)
- MIR の def→use 順序をそのまま出力(SSOT)
- PHI 先頭集約のみ維持

## Phase 131-14: jump-only block 2パス snapshot 解決
- Pass A: jump-only block はメタ記録のみ
- Pass B: resolve_jump_only_snapshots() で CFG ベース解決
- path compression で連鎖を効率的に解決
- サイクル検出で Fail-Fast

## 結果
-  STRICT モードでエラーなし
-  bb7 が bb5 の snapshot を正しく継承
-  ループが正しく動作(1, 2 出力確認)
- ⚠️ print/concat で segfault(別問題、次Phase)

🤖 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 00:39:43 +09:00
parent eb70dfc5bb
commit 7f57a1bb05
7 changed files with 559 additions and 113 deletions

View File

@ -3,7 +3,7 @@ Resolver API (Python version)
Based on src/backend/llvm/compiler/codegen/instructions/resolver.rs
"""
from typing import Dict, Optional, Any, Tuple
from typing import Dict, Optional, Any, Tuple, Set
import os
from trace import phi as trace_phi
from trace import values as trace_values
@ -63,6 +63,8 @@ class Resolver:
self.block_phi_incomings = {}
# P0-1: SSOT for end-of-block values (snapshots)
self.block_end_values = {}
# P0-3: Circular reference detection (hang prevention)
self._visited: Set[Tuple[int, int]] = set()
def mark_string(self, value_id: int) -> None:
try:
@ -75,7 +77,81 @@ class Resolver:
return int(value_id) in self.string_ids
except Exception:
return False
def _check_cycle(self, block_id: int, value_id: int):
"""P0-3: Circular reference detection (hang prevention)"""
key = (block_id, value_id)
if key in self._visited:
raise RuntimeError(
f"[LLVM_PY] Circular reference detected: bb{block_id} v{value_id}"
)
self._visited.add(key)
def resolve_cur(self, block_id: int, value_id: int, vmap_cur: Dict[int, ir.Value]) -> ir.Value:
"""P0-1: Same-block instruction lowering (vmap_cur as primary source)
Used for lowering instructions within the same basic block where the value
is defined and used. Checks vmap_cur first, then applies fail-fast checks.
Args:
block_id: Current basic block ID
value_id: Value ID to resolve
vmap_cur: Current block's value map (def->use tracking)
Returns:
LLVM IR value (i64)
"""
# 1. Check vmap_cur first
val = vmap_cur.get(value_id)
if val is not None:
return val
# 2. Fail-Fast: def_blocks has bb but vmap_cur doesn't → lowerer bug
if value_id in self.def_blocks and block_id in self.def_blocks[value_id]:
if os.environ.get('NYASH_LLVM_STRICT') == '1':
raise RuntimeError(
f"[LLVM_PY/STRICT] resolve_cur: v{value_id} defined in bb{block_id} "
f"but not in vmap_cur. Lowerer order bug?"
)
# 3. vmap_cur miss → undefined error
if os.environ.get('NYASH_LLVM_STRICT') == '1':
raise RuntimeError(
f"[LLVM_PY/STRICT] resolve_cur: v{value_id} not found in bb{block_id} vmap_cur. "
f"Available: {sorted(vmap_cur.keys())}"
)
# Non-STRICT: fallback to 0
return ir.Constant(ir.IntType(64), 0)
def resolve_incoming(self, pred_block_id: int, value_id: int) -> ir.Value:
"""P0-2: PHI incoming resolution (snapshot-only reference)
Used for resolving PHI incoming values from predecessor blocks.
Only looks at block_end_values snapshot, never vmap_cur.
Args:
pred_block_id: Predecessor block ID
value_id: Value ID to resolve from predecessor
Returns:
LLVM IR value (i64)
"""
snapshot = self.block_end_values.get(pred_block_id, {})
val = snapshot.get(value_id)
if val is not None:
return val
# Fail-Fast: snapshot miss → structural bug
if os.environ.get('NYASH_LLVM_STRICT') == '1':
raise RuntimeError(
f"[LLVM_PY/STRICT] resolve_incoming: v{value_id} not in bb{pred_block_id} snapshot. "
f"Available: {sorted(snapshot.keys())}"
)
# Non-STRICT: fallback to 0
return ir.Constant(ir.IntType(64), 0)
def resolve_i64(
self,
value_id: int,