feat(llvm): Phase 131-11-H/12 - ループキャリアPHI型修正 & vmap snapshot SSOT

## Phase 131-11-H: ループキャリアPHI型修正
- PHI生成時に初期値(entry block)の型のみ使用
- backedge の値を型推論に使わない(循環依存回避)
- NYASH_CARRIER_PHI_DEBUG=1 でトレース

## Phase 131-12-P0: def_blocks 登録 & STRICT エラー化
- safe_vmap_write() で PHI 上書き保護
- resolver miss を STRICT でエラー化(フォールバック 0 禁止)
- def_blocks 自動登録

## Phase 131-12-P1: vmap_cur スナップショット実装
- DeferredTerminator 構造体(block, term_ops, vmap_snapshot)
- Pass A で vmap_cur をスナップショット
- Pass C でスナップショット復元(try-finally)
- STRICT モード assert

## 結果
-  MIR PHI型: Integer(正しい)
-  VM: Result: 3
-  vmap snapshot 機構: 動作確認
- ⚠️ LLVM: Result: 0(別のバグ、次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-14 21:28:41 +09:00
parent 413504d6de
commit 7dfd6ff1d9
21 changed files with 1391 additions and 133 deletions

View File

@ -1,9 +1,20 @@
from typing import Dict, Any, List, Tuple
from typing import Dict, Any, List, Tuple, NamedTuple
from llvmlite import ir
from trace import debug as trace_debug
from trace import phi_json as trace_phi_json
class DeferredTerminator(NamedTuple):
"""Phase 131-12-P1: Deferred terminator with vmap snapshot.
This structure captures the terminator operations along with the vmap state
at the end of Pass A, ensuring Pass C uses the correct SSA context.
"""
bb: ir.Block
term_ops: List[Dict[str, Any]]
vmap_snapshot: Dict[int, ir.Value]
def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, Any]], order: List[int], loop_plan: Dict[str, Any] | None):
"""Lower blocks in multi-pass to ensure PHIs are always before terminators.
@ -34,6 +45,8 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
try:
builder.resolver.builder = ib
builder.resolver.module = builder.module
# P0-1: Set current block_id for def_blocks tracking
builder.resolver.current_block_id = bid
except Exception:
pass
builder.loop_count += 1
@ -116,6 +129,8 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
try:
builder.resolver.builder = ib
builder.resolver.module = builder.module
# P0-1: Set current block_id for def_blocks tracking
builder.resolver.current_block_id = bid
except Exception:
pass
block_data = block_by_id.get(bid, {})
@ -162,6 +177,10 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
except Exception:
vmap_cur = dict(builder.vmap)
builder._current_vmap = vmap_cur
# Phase 131-12-P1: Object identity trace for vmap_cur investigation
import os, sys
if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1':
print(f"[vmap/id] bb{bid} vmap_cur id={id(vmap_cur)} keys={sorted(vmap_cur.keys())[:10]}", file=sys.stderr)
created_ids: List[int] = []
defined_here_all: set = set()
for _inst in body_ops:
@ -229,11 +248,17 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
except Exception:
pass
# Phase 131-4 Pass A: DEFER terminators until after PHI finalization
# Store terminators for Pass C (will be lowered in lower_terminators)
# Phase 131-12-P1 P0-2: Store terminators WITH vmap_cur snapshot for Pass C
if not hasattr(builder, '_deferred_terminators'):
builder._deferred_terminators = {}
if term_ops:
builder._deferred_terminators[bid] = (bb, term_ops)
# CRITICAL: dict(vmap_cur) creates a snapshot copy to prevent mutation issues
vmap_snapshot = dict(vmap_cur)
builder._deferred_terminators[bid] = DeferredTerminator(bb, term_ops, vmap_snapshot)
# Phase 131-12-P1: Trace snapshot creation
import os, sys
if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1':
print(f"[vmap/id] Pass A bb{bid} snapshot id={id(vmap_snapshot)} keys={sorted(vmap_snapshot.keys())[:10]}", file=sys.stderr)
# Phase 131-7: Sync ALL created values to global vmap (not just PHIs)
# This ensures Pass C (deferred terminators) can access values from Pass A
try:
@ -265,9 +290,11 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
def lower_terminators(builder, func: ir.Function):
"""Phase 131-4 Pass C: Lower deferred terminators after PHI finalization.
Phase 131-12-P1 P0-3: Restore vmap_cur snapshot for each block's terminator lowering.
This ensures PHI nodes are always at block heads before terminators are added,
maintaining LLVM IR's invariant: PHIs first, then other instructions, then terminators.
The vmap snapshot ensures terminators see the SSA context from Pass A, not later mutations.
"""
if not hasattr(builder, '_deferred_terminators'):
return
@ -275,31 +302,65 @@ def lower_terminators(builder, func: ir.Function):
deferred = builder._deferred_terminators
trace_debug(f"[llvm-py/pass-c] Lowering {len(deferred)} blocks with deferred terminators")
for bid, (bb, term_ops) in deferred.items():
ib = ir.IRBuilder(bb)
try:
builder.resolver.builder = ib
builder.resolver.module = builder.module
# Phase 131-4: Disable PHI synthesis during terminator lowering
# Terminators should only use values that already exist (from Pass A/B)
builder.resolver._disable_phi_synthesis = True
except Exception:
pass
import os, sys
strict_mode = os.environ.get('NYASH_LLVM_STRICT') == '1'
for inst in term_ops:
for bid, deferred_term in deferred.items():
# Phase 131-12-P1: Unpack DeferredTerminator with vmap snapshot
bb = deferred_term.bb
term_ops = deferred_term.term_ops
vmap_snapshot = deferred_term.vmap_snapshot
# Phase 131-12-P1 P0-4: STRICT mode assertion
if strict_mode:
assert vmap_snapshot is not None, f"STRICT: vmap_snapshot must exist for bb{bid}"
trace_debug(f"[llvm-py/pass-c/strict] bb{bid} vmap_snapshot id={id(vmap_snapshot)}")
# Phase 131-12-P1 P0-3: Save and restore _current_vmap
old_current_vmap = getattr(builder, '_current_vmap', None)
builder._current_vmap = vmap_snapshot
# Trace snapshot restoration
if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1':
print(f"[vmap/id] Pass C bb{bid} restored snapshot id={id(vmap_snapshot)} keys={sorted(vmap_snapshot.keys())[:10]}", file=sys.stderr)
# Phase 131-12-P1 P0-4: STRICT mode verification
if strict_mode:
assert hasattr(builder, '_current_vmap'), f"STRICT: _current_vmap must be set for bb{bid} terminator lowering"
assert id(builder._current_vmap) == id(vmap_snapshot), f"STRICT: _current_vmap must match snapshot for bb{bid}"
try:
ib = ir.IRBuilder(bb)
try:
trace_debug(f"[llvm-py/pass-c] term op: {inst.get('op')} dst={inst.get('dst')} in bb{bid}")
builder.resolver.builder = ib
builder.resolver.module = builder.module
# Phase 131-4: Disable PHI synthesis during terminator lowering
# Terminators should only use values that already exist (from Pass A/B)
builder.resolver._disable_phi_synthesis = True
except Exception:
pass
try:
if bb.terminator is not None:
# Terminator already exists (e.g., from loop lowering), skip
trace_debug(f"[llvm-py/pass-c] bb{bid} already has terminator, skipping")
break
except Exception:
pass
ib.position_at_end(bb)
builder.lower_instruction(ib, inst, func)
for inst in term_ops:
try:
trace_debug(f"[llvm-py/pass-c] term op: {inst.get('op')} dst={inst.get('dst')} in bb{bid}")
except Exception:
pass
try:
if bb.terminator is not None:
# Terminator already exists (e.g., from loop lowering), skip
trace_debug(f"[llvm-py/pass-c] bb{bid} already has terminator, skipping")
break
except Exception:
pass
ib.position_at_end(bb)
builder.lower_instruction(ib, inst, func)
finally:
# Phase 131-12-P1 P0-3: Restore previous _current_vmap state (prevent side effects)
if old_current_vmap is None:
if hasattr(builder, '_current_vmap'):
delattr(builder, '_current_vmap')
else:
builder._current_vmap = old_current_vmap
# Clean up deferred state
try:

View File

@ -31,6 +31,10 @@ def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func:
op = inst.get("op")
# Pick current vmap context (per-block context during lowering)
vmap_ctx = getattr(owner, '_current_vmap', owner.vmap)
# Phase 131-12-P1: Object identity trace for vmap_ctx investigation
import os, sys
if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1':
print(f"[vmap/id] instruction op={op} vmap_ctx id={id(vmap_ctx)}", file=sys.stderr)
if op == "const":
dst = inst.get("dst")