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:
@ -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:
|
||||
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user