2025-09-12 20:40:48 +09:00
|
|
|
"""
|
|
|
|
|
Return instruction lowering
|
|
|
|
|
Handles void and value returns
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import llvmlite.ir as ir
|
2025-09-17 16:11:01 +09:00
|
|
|
from typing import Dict, Optional, Any
|
2025-09-12 20:40:48 +09:00
|
|
|
|
|
|
|
|
def lower_return(
|
|
|
|
|
builder: ir.IRBuilder,
|
|
|
|
|
value_id: Optional[int],
|
|
|
|
|
vmap: Dict[int, ir.Value],
|
2025-09-13 15:37:58 +09:00
|
|
|
return_type: ir.Type,
|
|
|
|
|
resolver=None,
|
|
|
|
|
preds=None,
|
|
|
|
|
block_end_values=None,
|
2025-09-17 16:11:01 +09:00
|
|
|
bb_map=None,
|
|
|
|
|
ctx: Optional[Any] = None,
|
2025-09-12 20:40:48 +09:00
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Lower MIR Return instruction
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
builder: Current LLVM IR builder
|
|
|
|
|
value_id: Optional return value ID
|
|
|
|
|
vmap: Value map
|
|
|
|
|
return_type: Expected return type
|
|
|
|
|
"""
|
2025-09-17 16:11:01 +09:00
|
|
|
# Prefer BuildCtx maps if provided
|
|
|
|
|
if ctx is not None:
|
|
|
|
|
try:
|
|
|
|
|
if getattr(ctx, 'resolver', None) is not None:
|
|
|
|
|
resolver = ctx.resolver
|
|
|
|
|
if getattr(ctx, 'preds', None) is not None and preds is None:
|
|
|
|
|
preds = ctx.preds
|
|
|
|
|
if getattr(ctx, 'block_end_values', None) is not None and block_end_values is None:
|
|
|
|
|
block_end_values = ctx.block_end_values
|
|
|
|
|
if getattr(ctx, 'bb_map', None) is not None and bb_map is None:
|
|
|
|
|
bb_map = ctx.bb_map
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
2025-09-12 20:40:48 +09:00
|
|
|
if value_id is None:
|
|
|
|
|
# Void return
|
|
|
|
|
builder.ret_void()
|
|
|
|
|
else:
|
2025-09-13 15:37:58 +09:00
|
|
|
# Get return value (prefer resolver)
|
2025-09-14 04:51:33 +09:00
|
|
|
ret_val = None
|
2025-09-13 15:37:58 +09:00
|
|
|
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
|
2025-09-14 04:51:33 +09:00
|
|
|
try:
|
2025-09-17 16:11:01 +09:00
|
|
|
# If this block has a declared PHI for the return value, force using the
|
|
|
|
|
# local PHI placeholder to ensure dominance and let finalize_phis wire it.
|
|
|
|
|
try:
|
|
|
|
|
block_name = builder.block.name
|
|
|
|
|
cur_bid = int(str(block_name).replace('bb',''))
|
|
|
|
|
except Exception:
|
|
|
|
|
cur_bid = -1
|
|
|
|
|
try:
|
|
|
|
|
bm = getattr(resolver, 'block_phi_incomings', {}) or {}
|
|
|
|
|
except Exception:
|
|
|
|
|
bm = {}
|
|
|
|
|
if isinstance(value_id, int) and isinstance(bm.get(cur_bid), dict) and value_id in bm.get(cur_bid):
|
|
|
|
|
# Reuse predeclared ret-phi when available
|
|
|
|
|
cur = None
|
|
|
|
|
try:
|
|
|
|
|
rp = getattr(resolver, 'ret_phi_map', {}) or {}
|
|
|
|
|
key = (int(cur_bid), int(value_id))
|
|
|
|
|
if key in rp:
|
|
|
|
|
cur = rp[key]
|
|
|
|
|
except Exception:
|
|
|
|
|
cur = None
|
|
|
|
|
if cur is None:
|
|
|
|
|
btop = ir.IRBuilder(builder.block)
|
|
|
|
|
try:
|
|
|
|
|
btop.position_at_start(builder.block)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
# Reuse existing local phi if present; otherwise create
|
|
|
|
|
cur = vmap.get(value_id)
|
|
|
|
|
need_new = True
|
|
|
|
|
try:
|
|
|
|
|
need_new = not (cur is not None and hasattr(cur, 'add_incoming') and getattr(getattr(cur, 'basic_block', None), 'name', None) == builder.block.name)
|
|
|
|
|
except Exception:
|
|
|
|
|
need_new = True
|
|
|
|
|
if need_new:
|
|
|
|
|
cur = btop.phi(ir.IntType(64), name=f"phi_ret_{value_id}")
|
|
|
|
|
# Bind to maps
|
|
|
|
|
vmap[value_id] = cur
|
|
|
|
|
try:
|
|
|
|
|
if hasattr(resolver, 'global_vmap') and isinstance(resolver.global_vmap, dict):
|
|
|
|
|
resolver.global_vmap[value_id] = cur
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
ret_val = cur
|
|
|
|
|
if ret_val is not None:
|
|
|
|
|
builder.ret(ret_val)
|
|
|
|
|
return
|
2025-09-14 04:51:33 +09:00
|
|
|
if isinstance(return_type, ir.PointerType):
|
|
|
|
|
ret_val = resolver.resolve_ptr(value_id, builder.block, preds, block_end_values, vmap)
|
|
|
|
|
else:
|
|
|
|
|
# Prefer pointer→handle reboxing for string-ish returns even if function return type is i64
|
|
|
|
|
is_stringish = False
|
|
|
|
|
if hasattr(resolver, 'is_stringish'):
|
|
|
|
|
try:
|
|
|
|
|
is_stringish = resolver.is_stringish(int(value_id))
|
|
|
|
|
except Exception:
|
|
|
|
|
is_stringish = False
|
|
|
|
|
if is_stringish and hasattr(resolver, 'string_ptrs') and int(value_id) in getattr(resolver, 'string_ptrs'):
|
|
|
|
|
# Re-box known string pointer to handle
|
|
|
|
|
p = resolver.string_ptrs[int(value_id)]
|
|
|
|
|
i8p = ir.IntType(8).as_pointer()
|
|
|
|
|
i64 = ir.IntType(64)
|
|
|
|
|
boxer = None
|
|
|
|
|
for f in builder.module.functions:
|
|
|
|
|
if f.name == 'nyash.box.from_i8_string':
|
|
|
|
|
boxer = f; break
|
|
|
|
|
if boxer is None:
|
|
|
|
|
boxer = ir.Function(builder.module, ir.FunctionType(i64, [i8p]), name='nyash.box.from_i8_string')
|
|
|
|
|
ret_val = builder.call(boxer, [p], name='ret_ptr2h')
|
|
|
|
|
else:
|
|
|
|
|
ret_val = resolver.resolve_i64(value_id, builder.block, preds, block_end_values, vmap, bb_map)
|
|
|
|
|
except Exception:
|
|
|
|
|
ret_val = None
|
|
|
|
|
if ret_val is None:
|
2025-09-13 15:37:58 +09:00
|
|
|
ret_val = vmap.get(value_id)
|
2025-09-12 20:40:48 +09:00
|
|
|
if not ret_val:
|
|
|
|
|
# Default based on return type
|
|
|
|
|
if isinstance(return_type, ir.IntType):
|
|
|
|
|
ret_val = ir.Constant(return_type, 0)
|
|
|
|
|
elif isinstance(return_type, ir.DoubleType):
|
|
|
|
|
ret_val = ir.Constant(return_type, 0.0)
|
|
|
|
|
else:
|
|
|
|
|
# Pointer type - null
|
|
|
|
|
ret_val = ir.Constant(return_type, None)
|
|
|
|
|
|
|
|
|
|
# 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
|
2025-09-13 15:37:58 +09:00
|
|
|
ret_val = builder.ptrtoint(ret_val, return_type, name="ret_p2i")
|
2025-09-12 20:40:48 +09:00
|
|
|
elif isinstance(return_type, ir.PointerType) and isinstance(ret_val.type, ir.IntType):
|
|
|
|
|
# int to ptr
|
2025-09-13 15:37:58 +09:00
|
|
|
ret_val = builder.inttoptr(ret_val, return_type, name="ret_i2p")
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
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)
|
2025-09-12 20:40:48 +09:00
|
|
|
|
2025-09-13 15:37:58 +09:00
|
|
|
builder.ret(ret_val)
|