Files
hakorune/src/llvm_py/instructions/ret.py

143 lines
6.5 KiB
Python
Raw Normal View History

"""
Return instruction lowering
Handles void and value returns
"""
import llvmlite.ir as ir
from typing import Dict, Optional, Any
def lower_return(
builder: ir.IRBuilder,
value_id: Optional[int],
vmap: Dict[int, ir.Value],
return_type: ir.Type,
resolver=None,
preds=None,
block_end_values=None,
bb_map=None,
ctx: Optional[Any] = None,
) -> 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
"""
# 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
if value_id is None:
# Void return
builder.ret_void()
else:
# Get return value (prefer resolver)
ret_val = None
# Fast path: if vmap has a concrete non-PHI value defined in this block, use it directly
if isinstance(value_id, int):
tmp0 = vmap.get(value_id)
try:
is_phi0 = hasattr(tmp0, 'add_incoming')
except Exception:
is_phi0 = False
if tmp0 is not None and not is_phi0:
ret_val = tmp0
if ret_val is None:
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
# Multi-pred special case: construct a PHI at block head and wire per-pred values
# to keep PHIs grouped at top and avoid late synthesis that violates ordering.
cur_bid = int(str(builder.block.name).replace('bb','')) if hasattr(builder.block, 'name') else -1
pred_ids = [p for p in preds.get(cur_bid, []) if p != cur_bid] if isinstance(preds, dict) else []
if isinstance(value_id, int) and len(pred_ids) > 1 and isinstance(return_type, ir.IntType):
# Create PHI at block head
btop = ir.IRBuilder(builder.block)
try:
btop.position_at_start(builder.block)
except Exception:
pass
ph = btop.phi(return_type, name=f"res_phi_{value_id}_{cur_bid}")
# Wire per-pred end values
for pred_bid in pred_ids:
pred_bb = bb_map.get(pred_bid) if isinstance(bb_map, dict) else None
if pred_bb is None:
continue
val = resolver._value_at_end_i64(value_id, pred_bid, preds, block_end_values, vmap, bb_map)
if not hasattr(val, 'type'):
val = ir.Constant(return_type, 0)
ph.add_incoming(val, pred_bb)
ret_val = ph
else:
# Resolve direct value
if isinstance(return_type, ir.PointerType):
ret_val = resolver.resolve_ptr(value_id, builder.block, preds, block_end_values, vmap)
else:
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'):
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)
if ret_val is None:
# Default to vmap (non-PHI) if available
tmp = vmap.get(value_id)
try:
is_phi = hasattr(tmp, 'add_incoming')
except Exception:
is_phi = False
if tmp is not None and not is_phi:
ret_val = tmp
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
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)
builder.ret(ret_val)