104 lines
3.3 KiB
Python
104 lines
3.3 KiB
Python
|
|
"""
|
||
|
|
BoxCall instruction lowering
|
||
|
|
Core of Nyash's "Everything is Box" philosophy
|
||
|
|
"""
|
||
|
|
|
||
|
|
import llvmlite.ir as ir
|
||
|
|
from typing import Dict, List, Optional
|
||
|
|
|
||
|
|
def lower_boxcall(
|
||
|
|
builder: ir.IRBuilder,
|
||
|
|
module: ir.Module,
|
||
|
|
box_vid: int,
|
||
|
|
method_name: str,
|
||
|
|
args: List[int],
|
||
|
|
dst_vid: Optional[int],
|
||
|
|
vmap: Dict[int, ir.Value],
|
||
|
|
resolver=None
|
||
|
|
) -> None:
|
||
|
|
"""
|
||
|
|
Lower MIR BoxCall instruction
|
||
|
|
|
||
|
|
Current implementation uses method_id approach for plugin boxes.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
builder: Current LLVM IR builder
|
||
|
|
module: LLVM module
|
||
|
|
box_vid: Box instance value ID (handle)
|
||
|
|
method_name: Method name to call
|
||
|
|
args: List of argument value IDs
|
||
|
|
dst_vid: Optional destination for return value
|
||
|
|
vmap: Value map
|
||
|
|
resolver: Optional resolver for type handling
|
||
|
|
"""
|
||
|
|
# Get box handle (i64)
|
||
|
|
box_handle = vmap.get(box_vid, ir.Constant(ir.IntType(64), 0))
|
||
|
|
|
||
|
|
# Ensure handle is i64
|
||
|
|
if hasattr(box_handle, 'type') and box_handle.type.is_pointer:
|
||
|
|
box_handle = builder.ptrtoint(box_handle, ir.IntType(64))
|
||
|
|
|
||
|
|
# Method ID dispatch for plugin boxes
|
||
|
|
# This matches the current LLVM backend approach
|
||
|
|
method_id = hash(method_name) & 0xFFFF # Simple hash for demo
|
||
|
|
|
||
|
|
# Look up or create ny_boxcall_by_id function
|
||
|
|
boxcall_func = None
|
||
|
|
for f in module.functions:
|
||
|
|
if f.name == "ny_boxcall_by_id":
|
||
|
|
boxcall_func = f
|
||
|
|
break
|
||
|
|
|
||
|
|
if not boxcall_func:
|
||
|
|
# Declare ny_boxcall_by_id(handle: i64, method_id: i64, args: i8*) -> i64
|
||
|
|
i8 = ir.IntType(8)
|
||
|
|
i64 = ir.IntType(64)
|
||
|
|
i8_ptr = i8.as_pointer()
|
||
|
|
|
||
|
|
func_type = ir.FunctionType(i64, [i64, i64, i8_ptr])
|
||
|
|
boxcall_func = ir.Function(module, func_type, name="ny_boxcall_by_id")
|
||
|
|
|
||
|
|
# Prepare arguments array
|
||
|
|
i8 = ir.IntType(8)
|
||
|
|
i64 = ir.IntType(64)
|
||
|
|
|
||
|
|
if args:
|
||
|
|
# Allocate space for arguments (8 bytes per arg)
|
||
|
|
args_size = len(args) * 8
|
||
|
|
args_ptr = builder.alloca(i8, size=args_size, name="boxcall_args")
|
||
|
|
|
||
|
|
# Cast to i64* for storing arguments
|
||
|
|
i64_ptr_type = i64.as_pointer()
|
||
|
|
args_i64_ptr = builder.bitcast(args_ptr, i64_ptr_type)
|
||
|
|
|
||
|
|
# Store each argument
|
||
|
|
for i, arg_id in enumerate(args):
|
||
|
|
arg_val = vmap.get(arg_id, ir.Constant(i64, 0))
|
||
|
|
|
||
|
|
# Ensure i64
|
||
|
|
if hasattr(arg_val, 'type'):
|
||
|
|
if arg_val.type.is_pointer:
|
||
|
|
arg_val = builder.ptrtoint(arg_val, i64)
|
||
|
|
elif arg_val.type != i64:
|
||
|
|
# TODO: Handle other conversions
|
||
|
|
pass
|
||
|
|
|
||
|
|
# Calculate offset and store
|
||
|
|
idx = ir.Constant(ir.IntType(32), i)
|
||
|
|
ptr = builder.gep(args_i64_ptr, [idx])
|
||
|
|
builder.store(arg_val, ptr)
|
||
|
|
|
||
|
|
# Cast back to i8* for call
|
||
|
|
call_args_ptr = builder.bitcast(args_i64_ptr, i8.as_pointer())
|
||
|
|
else:
|
||
|
|
# No arguments - pass null
|
||
|
|
call_args_ptr = ir.Constant(i8.as_pointer(), None)
|
||
|
|
|
||
|
|
# Make the boxcall
|
||
|
|
method_id_val = ir.Constant(i64, method_id)
|
||
|
|
result = builder.call(boxcall_func, [box_handle, method_id_val, call_args_ptr],
|
||
|
|
name=f"boxcall_{method_name}")
|
||
|
|
|
||
|
|
# Store result if needed
|
||
|
|
if dst_vid is not None:
|
||
|
|
vmap[dst_vid] = result
|