feat(phase276-p0): Quick Win improvements - type helper SSOT + debug cleanup

Phase 276 P0 post-Phase 275 robustness improvements:

1. Debug cleanup (wiring.py L100-103):
   - Remove traceback.format_stack() output
   - Cleaner debug logs after Phase 275 completion

2. box_from_f64 usage investigation:
   - Confirmed ZERO usage (Rust/Python/MIR)
   - Ready for deletion (Phase 275 unboxed double SSOT)
   - crates/nyash_kernel/src/lib.rs L244-252, L971-982

3. Type resolution SSOT ( most important):
   - New file: src/llvm_py/phi_wiring/type_helper.py (72 lines)
   - Unified 3 duplicate logic sites:
     * tagging.py: inst.get("dst_type") → type_helper.get_phi_dst_type()
     * llvm_builder.py: 9 lines → 2 lines (-7)
     * wiring.py: 18 lines → 5 lines (-13)
   - Priority: MIR JSON dst_type > resolver.value_types
   - Benefits: SSOT principle, bug prevention, easy extension

4. Type mismatch warning enhancement (wiring.py L63-79):
   - CRITICAL warning for predeclared PHI type mismatch
   - PhiManager.invalidate_phi() notification
   - Early bug detection with ⚠️  marker

Effects:
- Type resolution logic: 3 sites → 1 SSOT (type_helper.py)
- Code reduction: -20 lines duplicate logic
- Robustness: SSOT principle, CRITICAL warnings, Fail-Fast
- Debuggability: cleaner logs, prominent type mismatch alerts

Testing:
- LLVM harness: exit=3 verified (/tmp/test_p275_debug2.hako)
- NYASH_PHI_TYPE_DEBUG=1: correct type resolution confirmed

Docs:
- Phase 276 P0 completion: docs/.../phase-276/P0-COMPLETION.md
- 10-Now.md: Phase 276 P0 , Phase 275 P0 completed section

Next steps (Phase 277 P0 recommended):
- Delete box_from_f64 (nyash.box.from_f64, nyash.float.box_from_f64)
- Adopt dst_type_to_llvm_type() helper

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-22 13:39:55 +09:00
parent 60cffb1948
commit 9a76a199ee
6 changed files with 465 additions and 45 deletions

View File

@ -652,15 +652,9 @@ class NyashLLVMBuilder:
trace_phi_json({"phi": "finalize_dst", "block": int(block_id), "dst": int(dst_vid), "incoming": [(int(v), int(b)) for (b, v) in [(b, v) for (v, b) in (incoming or [])]]})
except Exception:
pass
# Phase 275 P0: Get dst_type from resolver's value_types
dst_type = None
try:
if hasattr(self, 'resolver') and hasattr(self.resolver, 'value_types'):
vt = self.resolver.value_types.get(int(dst_vid))
if vt == 'f64' or (isinstance(vt, dict) and vt.get('type') == 'f64'):
dst_type = 'f64'
except Exception:
pass
# Phase 275 P0: Get dst_type from resolver's value_types (SSOT)
from phi_wiring.type_helper import get_phi_dst_type
dst_type = get_phi_dst_type(self, dst_vid)
# Ensure placeholder exists at block head with common helper
phi = _ensure_phi(self, int(block_id), int(dst_vid), bb, dst_type=dst_type)
self.vmap[int(dst_vid)] = phi

View File

@ -76,8 +76,9 @@ def setup_phi_placeholders(builder, blocks: List[Dict[str, Any]]):
# mid-block users (e.g., compare/branch) dominate correctly
# and refer to the same SSA node that finalize_phis() will wire.
try:
# Phase 275 P0: Get dst_type from instruction JSON
dst_type = inst.get("dst_type")
# Phase 275 P0: Get dst_type from instruction JSON (SSOT)
from .type_helper import get_phi_dst_type
dst_type = get_phi_dst_type(builder, dst0, inst=inst)
ph = ensure_phi(builder, bid0, dst0, bb0, dst_type=dst_type)
# Keep a strong reference as a predeclared placeholder so
# later ensure_phi calls during finalize re-use the same SSA node.

View File

@ -0,0 +1,72 @@
"""Phase 275 P0: PHI型取得のSSOT
PHI dst_type 取得ロジックを統一管理。
- 優先度1: MIR JSON instruction の dst_type (最新)
- 優先度2: resolver.value_types (型推論後)
"""
def get_phi_dst_type(builder, dst_vid, inst=None):
"""PHI destination type を取得
Args:
builder: LLVM builder instance
dst_vid: Destination ValueId (int)
inst: MIR JSON instruction dict (optional, priority source)
Returns:
str | None: 'f64', 'i64', 'void', etc. (MIR type string)
Example:
>>> dst_type = get_phi_dst_type(builder, 36, inst)
>>> if dst_type == 'f64':
>>> phi_type = ir.DoubleType()
"""
# Priority 1: instruction JSON (最新、直接指定)
if inst is not None:
dst_type = inst.get("dst_type")
if dst_type is not None:
return dst_type
# Priority 2: resolver.value_types (型推論結果)
try:
if hasattr(builder, 'resolver') and hasattr(builder.resolver, 'value_types'):
vt = builder.resolver.value_types.get(int(dst_vid))
# f64 の場合
if vt == 'f64' or (isinstance(vt, dict) and vt.get('type') == 'f64'):
return 'f64'
# i64 の場合default
if vt == 'i64' or vt == 'Integer':
return 'i64'
# void の場合
if vt == 'void' or vt == 'Void':
return 'void'
# Box型の場合
if isinstance(vt, dict) and vt.get('kind') == 'handle':
return 'handle'
except Exception:
pass
return None
def dst_type_to_llvm_type(dst_type, builder):
"""MIR dst_type を LLVM IR type に変換
Args:
dst_type: str | None (from get_phi_dst_type)
builder: LLVM builder (for builder.i64 access)
Returns:
ir.Type: LLVM IR type (DoubleType, IntType, etc.)
Example:
>>> dst_type = get_phi_dst_type(builder, 36, inst)
>>> llvm_type = dst_type_to_llvm_type(dst_type, builder)
>>> phi = b.phi(llvm_type, name=f"phi_{dst_vid}")
"""
import llvmlite.ir as ir
if dst_type == 'f64' or dst_type == 'double':
return ir.DoubleType()
# デフォルトは i64
return builder.i64

View File

@ -61,8 +61,22 @@ def ensure_phi(builder, block_id: int, dst_vid: int, bb: ir.Block, dst_type=None
print(f"[phi_wiring/reuse] v{dst_vid} predeclared PHI type matches: {phi.type}", file=sys.stderr)
return phi
else:
# Phase 275 P0: 型不一致の古いPHIを発見 → CRITICAL警告
import sys
print(f"⚠️ [phi_wiring/CRITICAL] PHI type mismatch! "
f"v{dst_vid}: predeclared={phi.type} expected={expected_type}",
file=sys.stderr)
# PhiManager に古いPHI無効化を通知あれば
try:
if hasattr(builder, 'phi_manager'):
builder.phi_manager.invalidate_phi(int(block_id), int(dst_vid))
except Exception:
pass
# 詳細デバッグ
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
print(f"[phi_wiring/type_mismatch] v{dst_vid} predeclared PHI type {phi.type} != expected {expected_type}, creating new", file=sys.stderr)
print(f"[phi_wiring/type_mismatch] Creating new PHI with correct type", file=sys.stderr)
# Phase 132 Critical Fix: Check if block already has a terminator
# If so, we're trying to add PHI too late - this is a bug
@ -106,10 +120,7 @@ def ensure_phi(builder, block_id: int, dst_vid: int, bb: ir.Block, dst_type=None
import os, sys
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
import traceback
stack = ''.join(traceback.format_stack()[-4:-1]) # Last 3 frames before ensure_phi
print(f"[phi_wiring/create] v{dst_vid} dst_type={dst_type} -> phi_type={phi_type}", file=sys.stderr)
print(f"[phi_wiring/stack]\n{stack}", file=sys.stderr)
ph = b.phi(phi_type, name=f"phi_{dst_vid}")
# Phase 132 Debug: Check if basic_block is set correctly
@ -224,22 +235,12 @@ def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[in
except Exception:
phi = None
if phi is None:
# Phase 275 P0: Get dst_type from resolver's value_types
dst_type = None
try:
if hasattr(builder, 'resolver') and hasattr(builder.resolver, 'value_types'):
vt = builder.resolver.value_types.get(int(dst_vid))
import os, sys
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
print(f"[phi_wiring] v{dst_vid} value_types: {vt}", file=sys.stderr)
if vt == 'f64' or (isinstance(vt, dict) and vt.get('type') == 'f64'):
dst_type = 'f64'
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
print(f"[phi_wiring] v{dst_vid} -> dst_type='f64'", file=sys.stderr)
except Exception as e:
import os, sys
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
print(f"[phi_wiring] v{dst_vid} exception: {e}", file=sys.stderr)
# Phase 275 P0: Get dst_type from resolver's value_types (SSOT)
from .type_helper import get_phi_dst_type
dst_type = get_phi_dst_type(builder, dst_vid)
import os, sys
if os.environ.get('NYASH_PHI_TYPE_DEBUG') == '1':
print(f"[phi_wiring] v{dst_vid} -> dst_type='{dst_type}'", file=sys.stderr)
phi = ensure_phi(builder, block_id, dst_vid, bb, dst_type=dst_type)
preds_raw = [p for p in builder.preds.get(block_id, []) if p != block_id]
seen = set()