refactor(llvm-py): Box-First refactoring of ret.py (Phase 1-2)

Extract two responsibility Boxes following Box-First principle:

1. UnreachableReturnHandlerBox (Phase 1)
   - Handle unreachable block returns with Fail-Fast principle
   - Implements Phase 284-P2 unreachable semantics
   - Moved os/sys imports to file header

2. ReturnTypeAdjusterBox (Phase 2)
   - Handle ptr↔int conversion and int width adjustment
   - Isolate type coercion logic for LLVM compatibility
   - Support truncate/extend for integer width mismatches

Changes:
- File size: 205→249 lines (+21% for better organization)
- Reduced lower_return() complexity
- Preserved all Phase 284-P2 comments and semantics
- No breaking changes

Tests:
- phase286_pattern5_return_min.hako: PASS (exit 7)
- phase284_p2_return_in_loop_llvm.sh: PASS
- Quick profile: 154/154 PASS

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 17:04:42 +09:00
parent 225600b5f9
commit 32aa0ddf69

View File

@ -3,6 +3,8 @@ Return instruction lowering
Handles void and value returns
"""
import os
import sys
import llvmlite.ir as ir
from typing import Dict, Optional, Any
try:
@ -11,6 +13,74 @@ try:
except Exception:
_phi_at_block_head = None
class UnreachableReturnHandlerBox:
"""
Box-First principle: Single Responsibility - Handle unreachable block returns
Phase 284-P2: Implements Fail-Fast principle for unreachable blocks
"""
@staticmethod
def handle_null_return(builder: ir.IRBuilder, return_type: ir.Type) -> None:
"""
Handle unreachable blocks with Fail-Fast principle
MIR may generate unreachable blocks with null return values (e.g., loop(true) exit).
LLVM type-checks all blocks, so we must match the function signature.
Use 'unreachable' to satisfy type system AND crash if executed (Fail-Fast).
Args:
builder: Current LLVM IR builder
return_type: Expected return type
"""
if isinstance(return_type, ir.VoidType):
# True void function: keep original behavior
builder.ret_void()
else:
# Non-void function with null return: emit unreachable
# This satisfies LLVM's type checker (no return needed after unreachable)
# AND crashes immediately if the "unreachable" block is ever reached
builder.unreachable()
class ReturnTypeAdjusterBox:
"""
Box-First principle: Single Responsibility - Adjust return value types to match function signature
Handles ptr↔int conversion and int width adjustment
"""
@staticmethod
def adjust_type(builder: ir.IRBuilder, ret_val: ir.Value, return_type: ir.Type) -> ir.Value:
"""
Adjust return value type if needed
Args:
builder: Current LLVM IR builder
ret_val: Return value to adjust
return_type: Expected return type
Returns:
Adjusted return value
"""
# Type adjustment if needed
if hasattr(ret_val, 'type') and ret_val.type != return_type:
if isinstance(return_type, ir.IntType) and ret_val.type.is_pointer:
# ptr to int
ret_val = builder.ptrtoint(ret_val, return_type, name="ret_p2i")
elif isinstance(return_type, ir.PointerType) and isinstance(ret_val.type, ir.IntType):
# int to ptr
ret_val = builder.inttoptr(ret_val, return_type, name="ret_i2p")
elif isinstance(return_type, ir.IntType) and isinstance(ret_val.type, ir.IntType):
# int to int conversion
if return_type.width < ret_val.type.width:
# Truncate
ret_val = builder.trunc(ret_val, return_type)
elif return_type.width > ret_val.type.width:
# Zero extend
ret_val = builder.zext(ret_val, return_type)
return ret_val
def lower_return(
builder: ir.IRBuilder,
value_id: Optional[int],
@ -45,18 +115,8 @@ def lower_return(
except Exception:
pass
if value_id is None:
# Phase 284-P2: Handle unreachable blocks with Fail-Fast principle
# MIR may generate unreachable blocks with null return values (e.g., loop(true) exit).
# LLVM type-checks all blocks, so we must match the function signature.
# Use 'unreachable' to satisfy type system AND crash if executed (Fail-Fast).
if isinstance(return_type, ir.VoidType):
# True void function: keep original behavior
builder.ret_void()
else:
# Non-void function with null return: emit unreachable
# This satisfies LLVM's type checker (no return needed after unreachable)
# AND crashes immediately if the "unreachable" block is ever reached
builder.unreachable()
# Delegate to UnreachableReturnHandlerBox (Box-First principle)
UnreachableReturnHandlerBox.handle_null_return(builder, return_type)
else:
# Get return value (prefer resolver)
ret_val = None
@ -64,7 +124,6 @@ def lower_return(
if isinstance(value_id, int):
tmp0 = vmap.get(value_id)
# Phase 132 Debug: trace vmap lookup
import os, sys
if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1':
found = "FOUND" if tmp0 is not None else "MISSING"
print(f"[vmap/ret] value_id={value_id} {found} in vmap, keys={sorted(list(vmap.keys())[:20])}", file=sys.stderr)
@ -182,23 +241,9 @@ def lower_return(
ret_val = phi
except Exception:
pass
# Type adjustment if needed
if hasattr(ret_val, 'type') and ret_val.type != return_type:
if isinstance(return_type, ir.IntType) and ret_val.type.is_pointer:
# ptr to int
ret_val = builder.ptrtoint(ret_val, return_type, name="ret_p2i")
elif isinstance(return_type, ir.PointerType) and isinstance(ret_val.type, ir.IntType):
# int to ptr
ret_val = builder.inttoptr(ret_val, return_type, name="ret_i2p")
elif isinstance(return_type, ir.IntType) and isinstance(ret_val.type, ir.IntType):
# int to int conversion
if return_type.width < ret_val.type.width:
# Truncate
ret_val = builder.trunc(ret_val, return_type)
elif return_type.width > ret_val.type.width:
# Zero extend
ret_val = builder.zext(ret_val, return_type)
# Delegate type adjustment to ReturnTypeAdjusterBox (Box-First principle)
ret_val = ReturnTypeAdjusterBox.adjust_type(builder, ret_val, return_type)
# Emit return; no further instructions should be emitted in this block
builder.ret(ret_val)