📚 Phase 15計画を詳細化・更新: Python/llvmlite正式採用とプラグイン全方向ビルド戦略

 主な更新内容:
- Python/llvmlite実装の正式採用を明記(開発速度10倍、~2400行)
- プラグイン全方向ビルド戦略(.so/.o/.a同時生成)で単一EXE生成可能に
- 各実装の予想コード量を具体化(パーサー800行、MIR Builder 2500行、VM 5000行)
- 循環依存問題の解決を明記(nyrtがC ABI経由で提供)
- 現実的なスケジュール調整(2025年9月~2026年3月)

🎉 最新進捗:
- dep_tree_min_string.nyashオブジェクト生成成功(10.4KB)
- LLVM verifier green - dominance違反解決
- Resolver patternでSSA安全性確保

🚀 次のマイルストーン:
- Python/llvmliteでEXE生成パイプライン完成
- nyash-llvm-compiler分離設計
- NyashパーサーMVP実装開始

Everything is Boxの究極形が、ついに実現へ!

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-13 15:37:58 +09:00
parent 8e4f6d774d
commit 1d6fab4eda
44 changed files with 1653 additions and 598 deletions

View File

@ -47,7 +47,11 @@ def lower_atomic_op(
val_vid: Optional[int],
dst_vid: Optional[int],
vmap: Dict[int, ir.Value],
ordering: str = "seq_cst"
ordering: str = "seq_cst",
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower atomic operations
@ -62,7 +66,10 @@ def lower_atomic_op(
ordering: Memory ordering
"""
# Get pointer
ptr = vmap.get(ptr_vid)
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
ptr = resolver.resolve_ptr(ptr_vid, builder.block, preds, block_end_values, vmap)
else:
ptr = vmap.get(ptr_vid)
if not ptr:
# Create dummy pointer
i64 = ir.IntType(64)
@ -78,13 +85,19 @@ def lower_atomic_op(
elif op == "store":
# Atomic store
if val_vid is not None:
val = vmap.get(val_vid, ir.Constant(ir.IntType(64), 0))
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
val = resolver.resolve_i64(val_vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
val = vmap.get(val_vid, ir.Constant(ir.IntType(64), 0))
builder.store_atomic(val, ptr, ordering=ordering, align=8)
elif op == "add":
# Atomic add (fetch_add)
if val_vid is not None:
val = vmap.get(val_vid, ir.Constant(ir.IntType(64), 1))
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
val = resolver.resolve_i64(val_vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
val = ir.Constant(ir.IntType(64), 1)
result = builder.atomic_rmw("add", ptr, val, ordering=ordering)
if dst_vid is not None:
vmap[dst_vid] = result
@ -114,4 +127,4 @@ def insert_thread_fence(
elif fence_type == "write":
builder.fence("release")
else:
builder.fence("seq_cst")
builder.fence("seq_cst")

View File

@ -5,6 +5,8 @@ Handles +, -, *, /, %, &, |, ^, <<, >>
import llvmlite.ir as ir
from typing import Dict
from .compare import lower_compare
import llvmlite.ir as ir
def lower_binop(
builder: ir.IRBuilder,
@ -14,7 +16,10 @@ def lower_binop(
rhs: int,
dst: int,
vmap: Dict[int, ir.Value],
current_block: ir.Block
current_block: ir.Block,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR BinOp instruction
@ -31,18 +36,72 @@ def lower_binop(
"""
# Resolve operands as i64 (using resolver when available)
# For now, simple vmap lookup
lhs_val = vmap.get(lhs, ir.Constant(ir.IntType(64), 0))
rhs_val = vmap.get(rhs, ir.Constant(ir.IntType(64), 0))
if resolver is not None and preds is not None and block_end_values is not None:
lhs_val = resolver.resolve_i64(lhs, current_block, preds, block_end_values, vmap, bb_map)
rhs_val = resolver.resolve_i64(rhs, current_block, preds, block_end_values, vmap, bb_map)
else:
lhs_val = vmap.get(lhs, ir.Constant(ir.IntType(64), 0))
rhs_val = vmap.get(rhs, ir.Constant(ir.IntType(64), 0))
# Relational/equality operators delegate to compare
if op in ('==','!=','<','>','<=','>='):
lower_compare(builder, op, lhs, rhs, dst, vmap)
return
# String-aware concatenation unified to handles (i64) when any side is pointer string
if op == '+':
i64 = ir.IntType(64)
i8p = ir.IntType(8).as_pointer()
lhs_raw = vmap.get(lhs)
rhs_raw = vmap.get(rhs)
is_str = (hasattr(lhs_raw, 'type') and isinstance(lhs_raw.type, ir.PointerType)) or \
(hasattr(rhs_raw, 'type') and isinstance(rhs_raw.type, ir.PointerType))
if is_str:
# Helper: convert raw or resolved value to string handle
def to_handle(raw, val, tag: str):
if raw is not None and hasattr(raw, 'type') and isinstance(raw.type, ir.PointerType):
# pointer-to-array -> GEP
try:
if isinstance(raw.type.pointee, ir.ArrayType):
c0 = ir.Constant(ir.IntType(32), 0)
raw = builder.gep(raw, [c0, c0], name=f"bin_gep_{tag}_{dst}")
except Exception:
pass
cal = None
for f in builder.module.functions:
if f.name == 'nyash.box.from_i8_string':
cal = f; break
if cal is None:
cal = ir.Function(builder.module, ir.FunctionType(i64, [i8p]), name='nyash.box.from_i8_string')
return builder.call(cal, [raw], name=f"str_ptr2h_{tag}_{dst}")
# if already i64
if val is not None and hasattr(val, 'type') and isinstance(val.type, ir.IntType) and val.type.width == 64:
return val
return ir.Constant(i64, 0)
hl = to_handle(lhs_raw, lhs_val, 'l')
hr = to_handle(rhs_raw, rhs_val, 'r')
# concat_hh(handle, handle) -> handle
hh_fnty = ir.FunctionType(i64, [i64, i64])
callee = None
for f in builder.module.functions:
if f.name == 'nyash.string.concat_hh':
callee = f; break
if callee is None:
callee = ir.Function(builder.module, hh_fnty, name='nyash.string.concat_hh')
res = builder.call(callee, [hl, hr], name=f"concat_hh_{dst}")
vmap[dst] = res
return
# Ensure both are i64
i64 = ir.IntType(64)
if hasattr(lhs_val, 'type') and lhs_val.type != i64:
# Type conversion if needed
if lhs_val.type.is_pointer:
lhs_val = builder.ptrtoint(lhs_val, i64)
lhs_val = builder.ptrtoint(lhs_val, i64, name=f"binop_lhs_p2i_{dst}")
if hasattr(rhs_val, 'type') and rhs_val.type != i64:
if rhs_val.type.is_pointer:
rhs_val = builder.ptrtoint(rhs_val, i64)
rhs_val = builder.ptrtoint(rhs_val, i64, name=f"binop_rhs_p2i_{dst}")
# Perform operation
if op == '+':
@ -73,4 +132,4 @@ def lower_binop(
result = ir.Constant(i64, 0)
# Store result
vmap[dst] = result
vmap[dst] = result

View File

@ -6,6 +6,36 @@ Core of Nyash's "Everything is Box" philosophy
import llvmlite.ir as ir
from typing import Dict, List, Optional
def _declare(module: ir.Module, name: str, ret, args):
for f in module.functions:
if f.name == name:
return f
fnty = ir.FunctionType(ret, args)
return ir.Function(module, fnty, name=name)
def _ensure_handle(builder: ir.IRBuilder, module: ir.Module, v: ir.Value) -> ir.Value:
"""Coerce a value to i64 handle. If pointer, box via nyash.box.from_i8_string."""
i64 = ir.IntType(64)
if hasattr(v, 'type'):
if isinstance(v.type, ir.IntType) and v.type.width == 64:
return v
if isinstance(v.type, ir.PointerType):
# call nyash.box.from_i8_string(i8*) -> i64
i8p = ir.IntType(8).as_pointer()
# If pointer-to-array, GEP to first element
try:
if isinstance(v.type.pointee, ir.ArrayType):
c0 = ir.IntType(32)(0)
v = builder.gep(v, [c0, c0], name="bc_str_gep")
except Exception:
pass
callee = _declare(module, "nyash.box.from_i8_string", i64, [i8p])
return builder.call(callee, [v], name="str_ptr2h")
if isinstance(v.type, ir.IntType):
# extend/trunc to i64
return builder.zext(v, i64) if v.type.width < 64 else builder.trunc(v, i64)
return ir.Constant(i64, 0)
def lower_boxcall(
builder: ir.IRBuilder,
module: ir.Module,
@ -14,7 +44,10 @@ def lower_boxcall(
args: List[int],
dst_vid: Optional[int],
vmap: Dict[int, ir.Value],
resolver=None
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR BoxCall instruction
@ -31,74 +64,153 @@ def lower_boxcall(
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())
i8 = ir.IntType(8)
i8p = i8.as_pointer()
# Receiver value
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
recv_val = resolver.resolve_i64(box_vid, builder.block, preds, block_end_values, vmap, bb_map)
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
recv_val = vmap.get(box_vid, ir.Constant(i64, 0))
# Minimal method bridging for strings and console
if method_name in ("length", "len"):
# Prefer handle-based len_h
recv_h = _ensure_handle(builder, module, recv_val)
callee = _declare(module, "nyash.string.len_h", i64, [i64])
result = builder.call(callee, [recv_h], name="strlen_h")
if dst_vid is not None:
vmap[dst_vid] = result
return
if method_name == "substring":
# substring(start, end) with pointer-based API
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
s = resolver.resolve_i64(args[0], builder.block, preds, block_end_values, vmap, bb_map) if args else ir.Constant(i64, 0)
e = resolver.resolve_i64(args[1], builder.block, preds, block_end_values, vmap, bb_map) if len(args) > 1 else ir.Constant(i64, 0)
else:
s = vmap.get(args[0], ir.Constant(i64, 0)) if args else ir.Constant(i64, 0)
e = vmap.get(args[1], ir.Constant(i64, 0)) if len(args) > 1 else ir.Constant(i64, 0)
# Coerce recv to i8*
recv_p = recv_val
if hasattr(recv_p, 'type') and isinstance(recv_p.type, ir.IntType):
recv_p = builder.inttoptr(recv_p, i8p, name="bc_i2p_recv")
elif hasattr(recv_p, 'type') and isinstance(recv_p.type, ir.PointerType):
try:
if isinstance(recv_p.type.pointee, ir.ArrayType):
c0 = ir.Constant(ir.IntType(32), 0)
recv_p = builder.gep(recv_p, [c0, c0], name="bc_gep_recv")
except Exception:
pass
else:
recv_p = ir.Constant(i8p, None)
# Coerce indices
if hasattr(s, 'type') and isinstance(s.type, ir.PointerType):
s = builder.ptrtoint(s, i64)
if hasattr(e, 'type') and isinstance(e.type, ir.PointerType):
e = builder.ptrtoint(e, i64)
callee = _declare(module, "nyash.string.substring_sii", i8p, [i8p, i64, i64])
p = builder.call(callee, [recv_p, s, e], name="substring")
# Return as handle across blocks (i8* -> i64 via nyash.box.from_i8_string)
conv_fnty = ir.FunctionType(i64, [i8p])
conv = _declare(module, "nyash.box.from_i8_string", i64, [i8p])
h = builder.call(conv, [p], name="str_ptr2h_sub")
if dst_vid is not None:
vmap[dst_vid] = h
return
if method_name == "lastIndexOf":
# lastIndexOf(needle)
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
needle = resolver.resolve_ptr(args[0], builder.block, preds, block_end_values, vmap) if args else ir.Constant(i8p, None)
else:
needle = vmap.get(args[0], ir.Constant(i8p, None)) if args else ir.Constant(i8p, None)
recv_p = recv_val
if hasattr(recv_p, 'type') and isinstance(recv_p.type, ir.IntType):
recv_p = builder.inttoptr(recv_p, i8p, name="bc_i2p_recv2")
elif hasattr(recv_p, 'type') and isinstance(recv_p.type, ir.PointerType):
try:
if isinstance(recv_p.type.pointee, ir.ArrayType):
c0 = ir.Constant(ir.IntType(32), 0)
recv_p = builder.gep(recv_p, [c0, c0], name="bc_gep_recv2")
except Exception:
pass
if hasattr(needle, 'type') and isinstance(needle.type, ir.IntType):
needle = builder.inttoptr(needle, i8p, name="bc_i2p_needle")
elif hasattr(needle, 'type') and isinstance(needle.type, ir.PointerType):
try:
if isinstance(needle.type.pointee, ir.ArrayType):
c0 = ir.Constant(ir.IntType(32), 0)
needle = builder.gep(needle, [c0, c0], name="bc_gep_needle")
except Exception:
pass
elif not hasattr(needle, 'type'):
needle = ir.Constant(i8p, None)
callee = _declare(module, "nyash.string.lastIndexOf_ss", i64, [i8p, i8p])
res = builder.call(callee, [recv_p, needle], name="lastIndexOf")
if dst_vid is not None:
vmap[dst_vid] = res
return
if method_name in ("print", "println", "log"):
# Console mapping
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
arg0 = resolver.resolve_i64(args[0], builder.block, preds, block_end_values, vmap, bb_map) if args else None
else:
arg0 = vmap.get(args[0]) if args else None
if arg0 is None:
arg0 = ir.Constant(i8p, None)
# Prefer handle API if arg is i64, else pointer API
if hasattr(arg0, 'type') and isinstance(arg0.type, ir.IntType) and arg0.type.width == 64:
callee = _declare(module, "nyash.console.log_handle", i64, [i64])
_ = builder.call(callee, [arg0], name="console_log_h")
else:
if hasattr(arg0, 'type') and isinstance(arg0.type, ir.IntType):
arg0 = builder.inttoptr(arg0, i8p)
callee = _declare(module, "nyash.console.log", i64, [i8p])
_ = builder.call(callee, [arg0], name="console_log")
if dst_vid is not None:
vmap[dst_vid] = ir.Constant(i64, 0)
return
# Default: invoke via NyRT by-name shim (runtime resolves method id)
recv_h = _ensure_handle(builder, module, recv_val)
# Build C string for method name
mbytes = (method_name + "\0").encode('utf-8')
arr_ty = ir.ArrayType(ir.IntType(8), len(mbytes))
try:
fn = builder.block.parent
fn_name = getattr(fn, 'name', 'fn')
except Exception:
fn_name = 'fn'
base = f".meth_{fn_name}_{method_name}"
existing = {g.name for g in module.global_values}
gname = base
k = 1
while gname in existing:
gname = f"{base}.{k}"; k += 1
g = ir.GlobalVariable(module, arr_ty, name=gname)
g.linkage = 'private'
g.global_constant = True
g.initializer = ir.Constant(arr_ty, bytearray(mbytes))
c0 = ir.Constant(ir.IntType(32), 0)
mptr = builder.gep(g, [c0, c0], inbounds=True)
# Up to 2 args for minimal path
argc = ir.Constant(i64, min(len(args), 2))
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
a1 = resolver.resolve_i64(args[0], builder.block, preds, block_end_values, vmap, bb_map) if len(args) >= 1 else ir.Constant(i64, 0)
a2 = resolver.resolve_i64(args[1], builder.block, preds, block_end_values, vmap, bb_map) if len(args) >= 2 else ir.Constant(i64, 0)
else:
a1 = vmap.get(args[0], ir.Constant(i64, 0)) if len(args) >= 1 else ir.Constant(i64, 0)
a2 = vmap.get(args[1], ir.Constant(i64, 0)) if len(args) >= 2 else ir.Constant(i64, 0)
if hasattr(a1, 'type') and isinstance(a1.type, ir.PointerType):
a1 = builder.ptrtoint(a1, i64)
if hasattr(a2, 'type') and isinstance(a2.type, ir.PointerType):
a2 = builder.ptrtoint(a2, i64)
callee = _declare(module, "nyash.plugin.invoke_by_name_i64", i64, [i64, i8p, i64, i64, i64])
result = builder.call(callee, [recv_h, mptr, argc, a1, a2], name="pinvoke_by_name")
if dst_vid is not None:
vmap[dst_vid] = result
vmap[dst_vid] = result

View File

@ -12,7 +12,10 @@ def lower_branch(
then_bid: int,
else_bid: int,
vmap: Dict[int, ir.Value],
bb_map: Dict[int, ir.Block]
bb_map: Dict[int, ir.Block],
resolver=None,
preds=None,
block_end_values=None
) -> None:
"""
Lower MIR Branch instruction
@ -26,7 +29,10 @@ def lower_branch(
bb_map: Block map
"""
# Get condition value
cond = vmap.get(cond_vid)
if resolver is not None and preds is not None and block_end_values is not None:
cond = resolver.resolve_i64(cond_vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
cond = vmap.get(cond_vid)
if not cond:
# Default to false if missing
cond = ir.Constant(ir.IntType(1), 0)
@ -47,4 +53,4 @@ def lower_branch(
else_bb = bb_map.get(else_bid)
if then_bb and else_bb:
builder.cbranch(cond, then_bb, else_bb)
builder.cbranch(cond, then_bb, else_bb)

View File

@ -13,7 +13,10 @@ def lower_call(
args: List[int],
dst_vid: Optional[int],
vmap: Dict[int, ir.Value],
resolver=None
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR Call instruction
@ -27,49 +30,59 @@ def lower_call(
vmap: Value map
resolver: Optional resolver for type handling
"""
# Resolve function: accepts string name or value-id referencing a string literal
actual_name = func_name
if not isinstance(func_name, str):
# Try resolver.string_literals
if resolver is not None and hasattr(resolver, 'string_literals'):
actual_name = resolver.string_literals.get(func_name)
# Look up function in module
func = None
for f in module.functions:
if f.name == func_name:
func = f
break
if isinstance(actual_name, str):
for f in module.functions:
if f.name == actual_name:
func = f
break
if not func:
# Function not found - create declaration
# Default: i64(i64, ...) signature
# Function not found - create declaration with default i64 signature
ret_type = ir.IntType(64)
arg_types = [ir.IntType(64)] * len(args)
name = actual_name if isinstance(actual_name, str) else "unknown_fn"
func_type = ir.FunctionType(ret_type, arg_types)
func = ir.Function(module, func_type, name=func_name)
func = ir.Function(module, func_type, name=name)
# Prepare arguments
call_args = []
for i, arg_id in enumerate(args):
arg_val = vmap.get(arg_id)
if not arg_val:
# Default based on expected type
arg_val = None
if i < len(func.args):
expected_type = func.args[i].type
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
if hasattr(expected_type, 'is_pointer') and expected_type.is_pointer:
arg_val = resolver.resolve_ptr(arg_id, builder.block, preds, block_end_values, vmap)
else:
arg_val = resolver.resolve_i64(arg_id, builder.block, preds, block_end_values, vmap, bb_map)
if arg_val is None:
arg_val = vmap.get(arg_id)
if arg_val is None:
if i < len(func.args):
expected_type = func.args[i].type
else:
expected_type = ir.IntType(64)
if isinstance(expected_type, ir.IntType):
arg_val = ir.Constant(expected_type, 0)
elif isinstance(expected_type, ir.DoubleType):
arg_val = ir.Constant(expected_type, 0.0)
else:
arg_val = ir.Constant(expected_type, None)
# Type conversion if needed
if i < len(func.args):
expected_type = func.args[i].type
if hasattr(arg_val, 'type') and arg_val.type != expected_type:
if expected_type.is_pointer and isinstance(arg_val.type, ir.IntType):
arg_val = builder.inttoptr(arg_val, expected_type)
arg_val = builder.inttoptr(arg_val, expected_type, name=f"call_i2p_{i}")
elif isinstance(expected_type, ir.IntType) and arg_val.type.is_pointer:
arg_val = builder.ptrtoint(arg_val, expected_type)
arg_val = builder.ptrtoint(arg_val, expected_type, name=f"call_p2i_{i}")
call_args.append(arg_val)
# Make the call
@ -77,4 +90,4 @@ def lower_call(
# Store result if needed
if dst_vid is not None:
vmap[dst_vid] = result
vmap[dst_vid] = result

View File

@ -5,6 +5,7 @@ Handles comparison operations (<, >, <=, >=, ==, !=)
import llvmlite.ir as ir
from typing import Dict
from .externcall import lower_externcall
def lower_compare(
builder: ir.IRBuilder,
@ -12,7 +13,12 @@ def lower_compare(
lhs: int,
rhs: int,
dst: int,
vmap: Dict[int, ir.Value]
vmap: Dict[int, ir.Value],
resolver=None,
current_block=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR Compare instruction
@ -26,29 +32,62 @@ def lower_compare(
vmap: Value map
"""
# Get operands
lhs_val = vmap.get(lhs, ir.Constant(ir.IntType(64), 0))
rhs_val = vmap.get(rhs, ir.Constant(ir.IntType(64), 0))
# Ensure both are i64
if resolver is not None and preds is not None and block_end_values is not None and current_block is not None:
lhs_val = resolver.resolve_i64(lhs, current_block, preds, block_end_values, vmap, bb_map)
rhs_val = resolver.resolve_i64(rhs, current_block, preds, block_end_values, vmap, bb_map)
else:
lhs_val = vmap.get(lhs)
rhs_val = vmap.get(rhs)
i64 = ir.IntType(64)
if hasattr(lhs_val, 'type') and lhs_val.type.is_pointer:
i8p = ir.IntType(8).as_pointer()
# String-aware equality: if both are pointers, assume i8* strings
if op in ('==','!=') and hasattr(lhs_val, 'type') and hasattr(rhs_val, 'type'):
if isinstance(lhs_val.type, ir.PointerType) and isinstance(rhs_val.type, ir.PointerType):
# Box both to handles and call nyash.string.eq_hh
# nyash.box.from_i8_string(i8*) -> i64
box_from = None
for f in builder.module.functions:
if f.name == 'nyash.box.from_i8_string':
box_from = f
break
if not box_from:
box_from = ir.Function(builder.module, ir.FunctionType(i64, [i8p]), name='nyash.box.from_i8_string')
lh = builder.call(box_from, [lhs_val], name='lhs_ptr2h')
rh = builder.call(box_from, [rhs_val], name='rhs_ptr2h')
eqf = None
for f in builder.module.functions:
if f.name == 'nyash.string.eq_hh':
eqf = f
break
if not eqf:
eqf = ir.Function(builder.module, ir.FunctionType(i64, [i64, i64]), name='nyash.string.eq_hh')
eq = builder.call(eqf, [lh, rh], name='str_eq')
if op == '==':
vmap[dst] = eq
else:
# ne = 1 - eq
one = ir.Constant(i64, 1)
ne = builder.sub(one, eq, name='str_ne')
vmap[dst] = ne
return
# Default integer compare path
if lhs_val is None:
lhs_val = ir.Constant(i64, 0)
if rhs_val is None:
rhs_val = ir.Constant(i64, 0)
# Ensure both are i64
if hasattr(lhs_val, 'type') and isinstance(lhs_val.type, ir.PointerType):
lhs_val = builder.ptrtoint(lhs_val, i64)
if hasattr(rhs_val, 'type') and rhs_val.type.is_pointer:
if hasattr(rhs_val, 'type') and isinstance(rhs_val.type, ir.PointerType):
rhs_val = builder.ptrtoint(rhs_val, i64)
# Map operations to LLVM predicates
op_map = {
'<': 'slt', # signed less than
'>': 'sgt', # signed greater than
'<=': 'sle', # signed less or equal
'>=': 'sge', # signed greater or equal
'==': 'eq', # equal
'!=': 'ne' # not equal
}
pred = op_map.get(op, 'eq')
# Perform comparison (returns i1)
# Perform signed comparison using canonical predicates ('<','>','<=','>=','==','!=')
pred = op if op in ('<','>','<=','>=','==','!=') else '=='
cmp_result = builder.icmp_signed(pred, lhs_val, rhs_val, name=f"cmp_{dst}")
# Convert i1 to i64 (0 or 1)
@ -81,19 +120,8 @@ def lower_fcmp(
lhs_val = vmap.get(lhs, ir.Constant(f64, 0.0))
rhs_val = vmap.get(rhs, ir.Constant(f64, 0.0))
# Map operations to LLVM predicates
op_map = {
'<': 'olt', # ordered less than
'>': 'ogt', # ordered greater than
'<=': 'ole', # ordered less or equal
'>=': 'oge', # ordered greater or equal
'==': 'oeq', # ordered equal
'!=': 'one' # ordered not equal
}
pred = op_map.get(op, 'oeq')
# Perform comparison (returns i1)
# Perform ordered comparison using canonical predicates
pred = op if op in ('<','>','<=','>=','==','!=') else '=='
cmp_result = builder.fcmp_ordered(pred, lhs_val, rhs_val, name=f"fcmp_{dst}")
# Convert i1 to i64
@ -101,4 +129,4 @@ def lower_fcmp(
result = builder.zext(cmp_result, i64, name=f"fcmp_i64_{dst}")
# Store result
vmap[dst] = result
vmap[dst] = result

View File

@ -11,7 +11,8 @@ def lower_const(
module: ir.Module,
dst: int,
value: Dict[str, Any],
vmap: Dict[int, ir.Value]
vmap: Dict[int, ir.Value],
resolver=None
) -> None:
"""
Lower MIR Const instruction
@ -39,25 +40,31 @@ def lower_const(
vmap[dst] = llvm_val
elif const_type == 'string':
# String constant - create global and get pointer
# String constant - create global, store GlobalVariable (not GEP) to avoid dominance issues
i8 = ir.IntType(8)
str_val = str(const_val)
# Create array constant for the string
str_bytes = str_val.encode('utf-8') + b'\0'
str_const = ir.Constant(ir.ArrayType(i8, len(str_bytes)),
bytearray(str_bytes))
# Create global string constant
global_name = f".str.{dst}"
global_str = ir.GlobalVariable(module, str_const.type, name=global_name)
global_str.initializer = str_const
global_str.linkage = 'private'
global_str.global_constant = True
# Get pointer to first element
indices = [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)]
ptr = builder.gep(global_str, indices, name=f"str_ptr_{dst}")
vmap[dst] = ptr
arr_ty = ir.ArrayType(i8, len(str_bytes))
str_const = ir.Constant(arr_ty, bytearray(str_bytes))
try:
fn = builder.block.parent
fn_name = getattr(fn, 'name', 'fn')
except Exception:
fn_name = 'fn'
base = f".str.{fn_name}.{dst}"
existing = {g.name for g in module.global_values}
name = base
n = 1
while name in existing:
name = f"{base}.{n}"; n += 1
g = ir.GlobalVariable(module, arr_ty, name=name)
g.initializer = str_const
g.linkage = 'private'
g.global_constant = True
# Store the GlobalVariable; resolver.resolve_ptr will emit GEP in the current block
vmap[dst] = g
if resolver is not None and hasattr(resolver, 'string_literals'):
resolver.string_literals[dst] = str_val
elif const_type == 'void':
# Void/null constant - use i64 zero
@ -67,4 +74,4 @@ def lower_const(
else:
# Unknown type - default to i64 zero
i64 = ir.IntType(64)
vmap[dst] = ir.Constant(i64, 0)
vmap[dst] = ir.Constant(i64, 0)

View File

@ -1,40 +1,11 @@
"""
ExternCall instruction lowering
Handles the minimal 5 runtime functions: print, error, panic, exit, now
Minimal mapping for NyRT-exported symbols (console/log family等)
"""
import llvmlite.ir as ir
from typing import Dict, List, Optional
# The 5 minimal external functions
EXTERN_FUNCS = {
"print": {
"ret": "void",
"args": ["i8*"], # String pointer
"llvm_name": "ny_print"
},
"error": {
"ret": "void",
"args": ["i8*"], # Error message
"llvm_name": "ny_error"
},
"panic": {
"ret": "void",
"args": ["i8*"], # Panic message
"llvm_name": "ny_panic"
},
"exit": {
"ret": "void",
"args": ["i64"], # Exit code
"llvm_name": "ny_exit"
},
"now": {
"ret": "i64",
"args": [], # No arguments
"llvm_name": "ny_now"
}
}
def lower_externcall(
builder: ir.IRBuilder,
module: ir.Module,
@ -42,7 +13,10 @@ def lower_externcall(
args: List[int],
dst_vid: Optional[int],
vmap: Dict[int, ir.Value],
resolver=None
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR ExternCall instruction
@ -56,94 +30,121 @@ def lower_externcall(
vmap: Value map
resolver: Optional resolver for type handling
"""
if func_name not in EXTERN_FUNCS:
# Unknown extern function - treat as void()
print(f"Warning: Unknown extern function: {func_name}")
return
extern_info = EXTERN_FUNCS[func_name]
llvm_name = extern_info["llvm_name"]
# Look up or declare function
# Accept full symbol names (e.g., "nyash.console.log", "nyash.string.len_h").
llvm_name = func_name
i8 = ir.IntType(8)
i64 = ir.IntType(64)
i8p = i8.as_pointer()
void = ir.VoidType()
# Known NyRT signatures
sig_map = {
# Strings (handle-based)
"nyash.string.len_h": (i64, [i64]),
"nyash.string.charCodeAt_h": (i64, [i64, i64]),
"nyash.string.concat_hh": (i64, [i64, i64]),
"nyash.string.eq_hh": (i64, [i64, i64]),
# Strings (pointer-based plugin functions)
"nyash.string.concat_ss": (i8p, [i8p, i8p]),
"nyash.string.concat_si": (i8p, [i8p, i64]),
"nyash.string.concat_is": (i8p, [i64, i8p]),
"nyash.string.substring_sii": (i8p, [i8p, i64, i64]),
"nyash.string.lastIndexOf_ss": (i64, [i8p, i8p]),
# Boxing helpers
"nyash.box.from_i8_string": (i64, [i8p]),
# Console (string pointer expected)
# Many call sites pass handles or pointers; we coerce below.
}
# Find or declare function with appropriate prototype
func = None
for f in module.functions:
if f.name == llvm_name:
func = f
break
if not func:
# Build function type
i8 = ir.IntType(8)
i64 = ir.IntType(64)
void = ir.VoidType()
# Return type
if extern_info["ret"] == "void":
ret_type = void
elif extern_info["ret"] == "i64":
ret_type = i64
if llvm_name in sig_map:
ret_ty, arg_tys = sig_map[llvm_name]
fnty = ir.FunctionType(ret_ty, arg_tys)
func = ir.Function(module, fnty, name=llvm_name)
elif llvm_name.startswith("nyash.console."):
# console.*: (i8*) -> i64
fnty = ir.FunctionType(i64, [i8p])
func = ir.Function(module, fnty, name=llvm_name)
else:
ret_type = void
# Argument types
arg_types = []
for arg_type_str in extern_info["args"]:
if arg_type_str == "i8*":
arg_types.append(i8.as_pointer())
elif arg_type_str == "i64":
arg_types.append(i64)
func_type = ir.FunctionType(ret_type, arg_types)
func = ir.Function(module, func_type, name=llvm_name)
# Prepare arguments
call_args = []
# Unknown extern: declare as void(...no args...) and call without args
fnty = ir.FunctionType(void, [])
func = ir.Function(module, fnty, name=llvm_name)
# Prepare/coerce arguments
call_args: List[ir.Value] = []
for i, arg_id in enumerate(args):
if i >= len(extern_info["args"]):
break # Too many arguments
expected_type_str = extern_info["args"][i]
arg_val = vmap.get(arg_id)
if not arg_val:
# Default value
if expected_type_str == "i8*":
# Null string
i8 = ir.IntType(8)
arg_val = ir.Constant(i8.as_pointer(), None)
elif expected_type_str == "i64":
arg_val = ir.Constant(ir.IntType(64), 0)
# Type conversion
if expected_type_str == "i8*":
# Need string pointer
if hasattr(arg_val, 'type'):
if isinstance(arg_val.type, ir.IntType):
# int to ptr
i8 = ir.IntType(8)
arg_val = builder.inttoptr(arg_val, i8.as_pointer())
elif not arg_val.type.is_pointer:
# Need pointer type
i8 = ir.IntType(8)
arg_val = ir.Constant(i8.as_pointer(), None)
elif expected_type_str == "i64":
# Need i64
if hasattr(arg_val, 'type'):
if arg_val.type.is_pointer:
arg_val = builder.ptrtoint(arg_val, ir.IntType(64))
elif arg_val.type != ir.IntType(64):
# Convert to i64
pass # TODO: Handle other conversions
call_args.append(arg_val)
# Make the call
if extern_info["ret"] == "void":
builder.call(func, call_args)
if dst_vid is not None:
# Void return - store 0
vmap[dst_vid] = ir.Constant(ir.IntType(64), 0)
else:
# Prefer resolver
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
if len(func.args) > i and isinstance(func.args[i].type, ir.PointerType):
aval = resolver.resolve_ptr(arg_id, builder.block, preds, block_end_values, vmap)
else:
aval = resolver.resolve_i64(arg_id, builder.block, preds, block_end_values, vmap, bb_map)
else:
aval = vmap.get(arg_id)
if aval is None:
# Default guess
aval = ir.Constant(i64, 0)
# If function prototype is known, coerce to expected type
if len(func.args) > i:
expected_ty = func.args[i].type
if isinstance(expected_ty, ir.PointerType):
# Need pointer
if hasattr(aval, 'type'):
if isinstance(aval.type, ir.IntType):
aval = builder.inttoptr(aval, expected_ty, name=f"ext_i2p_arg{i}")
elif not aval.type.is_pointer:
aval = ir.Constant(expected_ty, None)
else:
# Pointer but wrong element type: if pointer-to-array -> GEP to i8*
try:
if isinstance(aval.type.pointee, ir.ArrayType) and isinstance(expected_ty.pointee, ir.IntType) and expected_ty.pointee.width == 8:
c0 = ir.Constant(ir.IntType(32), 0)
aval = builder.gep(aval, [c0, c0], name=f"ext_gep_arg{i}")
except Exception:
pass
else:
aval = ir.Constant(expected_ty, None)
elif isinstance(expected_ty, ir.IntType) and expected_ty.width == 64:
# Need i64
if hasattr(aval, 'type'):
if isinstance(aval.type, ir.PointerType):
aval = builder.ptrtoint(aval, i64, name=f"ext_p2i_arg{i}")
elif isinstance(aval.type, ir.IntType) and aval.type.width != 64:
# extend/trunc
if aval.type.width < 64:
aval = builder.zext(aval, i64, name=f"ext_zext_{i}")
else:
aval = builder.trunc(aval, i64, name=f"ext_trunc_{i}")
else:
aval = ir.Constant(i64, 0)
else:
# Prototype shorter than args: best-effort pointer->i64 for string-ish APIs
if hasattr(aval, 'type') and isinstance(aval.type, ir.PointerType):
aval = builder.ptrtoint(aval, i64, name=f"ext_p2i_arg{i}")
call_args.append(aval)
# Truncate extra args if prototype shorter
if len(call_args) > len(func.args):
call_args = call_args[:len(func.args)]
# Issue the call
if len(call_args) == len(func.args):
result = builder.call(func, call_args, name=f"extern_{func_name}")
if dst_vid is not None:
vmap[dst_vid] = result
else:
result = builder.call(func, call_args[:len(func.args)])
# Materialize result into vmap
if dst_vid is not None:
rty = func.function_type.return_type
if isinstance(rty, ir.VoidType):
vmap[dst_vid] = ir.Constant(i64, 0)
else:
vmap[dst_vid] = result

View File

@ -50,7 +50,10 @@ def lower_while_loopform(
body_instructions: List[Any],
loop_id: int,
vmap: Dict[int, ir.Value],
bb_map: Dict[int, ir.Block]
bb_map: Dict[int, ir.Block],
resolver=None,
preds=None,
block_end_values=None
) -> bool:
"""
Lower a while loop using LoopForm structure
@ -71,7 +74,12 @@ def lower_while_loopform(
# Header: Evaluate condition
builder.position_at_end(lf.header)
cond = vmap.get(condition_vid, ir.Constant(ir.IntType(1), 0))
if resolver is not None and preds is not None and block_end_values is not None:
cond64 = resolver.resolve_i64(condition_vid, builder.block, preds, block_end_values, vmap, bb_map)
zero64 = ir.IntType(64)(0)
cond = builder.icmp_unsigned('!=', cond64, zero64)
else:
cond = vmap.get(condition_vid, ir.Constant(ir.IntType(1), 0))
# Convert to i1 if needed
if hasattr(cond, 'type') and cond.type == ir.IntType(64):
cond = builder.icmp_unsigned('!=', cond, ir.Constant(ir.IntType(64), 0))
@ -118,4 +126,4 @@ def lower_while_loopform(
if os.environ.get('NYASH_CLI_VERBOSE') == '1':
print(f"[LoopForm] Created loop structure (id={loop_id})")
return True
return True

View File

@ -29,60 +29,40 @@ def lower_newbox(
vmap: Value map
resolver: Optional resolver for type handling
"""
# Look up or declare the box creation function
create_func_name = f"ny_create_{box_type}"
create_func = None
# Use NyRT shim: nyash.env.box.new(type_name: i8*) -> i64
i64 = ir.IntType(64)
i8p = ir.IntType(8).as_pointer()
# Prefer variadic shim: nyash.env.box.new_i64x(type_name, argc, a1, a2, a3, a4)
new_i64x = None
for f in module.functions:
if f.name == create_func_name:
create_func = f
if f.name == "nyash.env.box.new_i64x":
new_i64x = f
break
if not create_func:
# Declare box creation function
# Signature depends on box type
i64 = ir.IntType(64)
i8 = ir.IntType(8)
if box_type in ["StringBox", "IntegerBox", "BoolBox"]:
# Built-in boxes - default constructors (no args)
# Real implementation may have optional args
func_type = ir.FunctionType(i64, [])
else:
# Generic box - variable arguments
# For now, assume no args
func_type = ir.FunctionType(i64, [])
create_func = ir.Function(module, func_type, name=create_func_name)
# Prepare arguments
call_args = []
for i, arg_id in enumerate(args):
arg_val = vmap.get(arg_id)
if not arg_val:
# Default based on box type
if box_type == "StringBox":
# Empty string
i8 = ir.IntType(8)
arg_val = ir.Constant(i8.as_pointer(), None)
else:
# Zero
arg_val = ir.Constant(ir.IntType(64), 0)
# Type conversion if needed
if box_type == "StringBox" and hasattr(arg_val, 'type'):
if isinstance(arg_val.type, ir.IntType):
# int to string ptr
i8 = ir.IntType(8)
arg_val = builder.inttoptr(arg_val, i8.as_pointer())
call_args.append(arg_val)
# Create the box
handle = builder.call(create_func, call_args, name=f"new_{box_type}")
# Store handle
if not new_i64x:
new_i64x = ir.Function(module, ir.FunctionType(i64, [i8p, i64, i64, i64, i64, i64]), name="nyash.env.box.new_i64x")
# Build C-string for type name (unique global per function)
sbytes = (box_type + "\0").encode('utf-8')
arr_ty = ir.ArrayType(ir.IntType(8), len(sbytes))
try:
fn = builder.block.parent
fn_name = getattr(fn, 'name', 'fn')
except Exception:
fn_name = 'fn'
base = f".box_ty_{fn_name}_{dst_vid}"
existing = {g.name for g in module.global_values}
name = base
n = 1
while name in existing:
name = f"{base}.{n}"; n += 1
g = ir.GlobalVariable(module, arr_ty, name=name)
g.linkage = 'private'
g.global_constant = True
g.initializer = ir.Constant(arr_ty, bytearray(sbytes))
c0 = ir.Constant(ir.IntType(32), 0)
ptr = builder.gep(g, [c0, c0], inbounds=True)
zero = ir.Constant(i64, 0)
handle = builder.call(new_i64x, [ptr, zero, zero, zero, zero, zero], name=f"new_{box_type}")
vmap[dst_vid] = handle
def lower_newbox_generic(
@ -113,4 +93,4 @@ def lower_newbox_generic(
size = ir.Constant(ir.IntType(64), 64)
handle = builder.call(alloc_func, [size], name="new_box")
vmap[dst_vid] = handle
vmap[dst_vid] = handle

View File

@ -13,7 +13,9 @@ def lower_phi(
vmap: Dict[int, ir.Value],
bb_map: Dict[int, ir.Block],
current_block: ir.Block,
resolver=None # Resolver instance (optional)
resolver=None, # Resolver instance (optional)
block_end_values: Optional[Dict[int, Dict[int, ir.Value]]] = None,
preds_map: Optional[Dict[int, List[int]]] = None
) -> None:
"""
Lower MIR PHI instruction
@ -32,23 +34,50 @@ def lower_phi(
vmap[dst_vid] = ir.Constant(ir.IntType(64), 0)
return
# Determine PHI type from first incoming value
first_val_id = incoming[0][0]
first_val = vmap.get(first_val_id)
if first_val and hasattr(first_val, 'type'):
phi_type = first_val.type
else:
# Default to i64
phi_type = ir.IntType(64)
# Determine PHI type from snapshots or fallback i64
phi_type = ir.IntType(64)
if block_end_values is not None:
for val_id, pred_bid in incoming:
snap = block_end_values.get(pred_bid, {})
val = snap.get(val_id)
if val is not None and hasattr(val, 'type'):
phi_type = val.type
# Prefer pointer type
if hasattr(phi_type, 'is_pointer') and phi_type.is_pointer:
break
# Create PHI instruction
phi = builder.phi(phi_type, name=f"phi_{dst_vid}")
# Add incoming values
# Build map from provided incoming
incoming_map: Dict[int, int] = {}
for val_id, block_id in incoming:
val = vmap.get(val_id)
incoming_map[block_id] = val_id
# Resolve actual predecessor set
cur_bid = None
try:
cur_bid = int(str(current_block.name).replace('bb',''))
except Exception:
pass
actual_preds = []
if preds_map is not None and cur_bid is not None:
actual_preds = [p for p in preds_map.get(cur_bid, []) if p != cur_bid]
else:
# Fallback: use blocks in incoming list
actual_preds = [b for _, b in incoming]
# Add incoming for each actual predecessor
for block_id in actual_preds:
block = bb_map.get(block_id)
# Prefer pred snapshot
if block_end_values is not None:
snap = block_end_values.get(block_id, {})
vid = incoming_map.get(block_id)
val = snap.get(vid) if vid is not None else None
else:
vid = incoming_map.get(block_id)
val = vmap.get(vid) if vid is not None else None
if not val:
# Create default value based on type
@ -66,34 +95,32 @@ def lower_phi(
# Type conversion if needed
if hasattr(val, 'type') and val.type != phi_type:
# Save current position
saved_block = builder.block
saved_pos = None
if hasattr(builder, '_anchor'):
saved_pos = builder._anchor
# Position at end of predecessor block
builder.position_at_end(block)
# Position at end (before terminator) of predecessor block
pb = ir.IRBuilder(block)
try:
term = block.terminator
if term is not None:
pb.position_before(term)
else:
pb.position_at_end(block)
except Exception:
pb.position_at_end(block)
# Convert types
if isinstance(phi_type, ir.IntType) and val.type.is_pointer:
val = builder.ptrtoint(val, phi_type, name=f"cast_p2i_{val_id}")
val = pb.ptrtoint(val, phi_type, name=f"cast_p2i_{val_id}")
elif phi_type.is_pointer and isinstance(val.type, ir.IntType):
val = builder.inttoptr(val, phi_type, name=f"cast_i2p_{val_id}")
val = pb.inttoptr(val, phi_type, name=f"cast_i2p_{val_id}")
elif isinstance(phi_type, ir.IntType) and isinstance(val.type, ir.IntType):
# Int to int
if phi_type.width > val.type.width:
val = builder.zext(val, phi_type, name=f"zext_{val_id}")
val = pb.zext(val, phi_type, name=f"zext_{val_id}")
else:
val = builder.trunc(val, phi_type, name=f"trunc_{val_id}")
# Restore position
builder.position_at_end(saved_block)
if saved_pos and hasattr(builder, '_anchor'):
builder._anchor = saved_pos
val = pb.trunc(val, phi_type, name=f"trunc_{val_id}")
# Add to PHI
phi.add_incoming(val, block)
# Add to PHI (skip if no block)
if block is not None:
phi.add_incoming(val, block)
# Store PHI result
vmap[dst_vid] = phi
@ -111,4 +138,4 @@ def defer_phi_wiring(
incoming: Incoming edges
phi_deferrals: List to store deferred PHIs
"""
phi_deferrals.append((dst_vid, incoming))
phi_deferrals.append((dst_vid, incoming))

View File

@ -10,7 +10,11 @@ def lower_return(
builder: ir.IRBuilder,
value_id: Optional[int],
vmap: Dict[int, ir.Value],
return_type: ir.Type
return_type: ir.Type,
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR Return instruction
@ -25,8 +29,14 @@ def lower_return(
# Void return
builder.ret_void()
else:
# Get return value
ret_val = vmap.get(value_id)
# Get return value (prefer resolver)
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
if isinstance(return_type, ir.PointerType):
ret_val = resolver.resolve_ptr(value_id, builder.block, preds, block_end_values, vmap)
else:
ret_val = resolver.resolve_i64(value_id, builder.block, preds, block_end_values, vmap, bb_map)
else:
ret_val = vmap.get(value_id)
if not ret_val:
# Default based on return type
if isinstance(return_type, ir.IntType):
@ -41,10 +51,10 @@ def lower_return(
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)
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)
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:
@ -54,4 +64,4 @@ def lower_return(
# Zero extend
ret_val = builder.zext(ret_val, return_type)
builder.ret(ret_val)
builder.ret(ret_val)

View File

@ -11,7 +11,11 @@ def lower_safepoint(
module: ir.Module,
live_values: List[int],
vmap: Dict[int, ir.Value],
safepoint_id: Optional[int] = None
safepoint_id: Optional[int] = None,
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR Safepoint instruction
@ -49,11 +53,14 @@ def lower_safepoint(
# Store each live value
for i, vid in enumerate(live_values):
val = vmap.get(vid, ir.Constant(i64, 0))
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
val = resolver.resolve_i64(vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
val = vmap.get(vid, ir.Constant(i64, 0))
# Ensure i64 (handles are i64)
if hasattr(val, 'type') and val.type.is_pointer:
val = builder.ptrtoint(val, i64)
val = builder.ptrtoint(val, i64, name=f"sp_p2i_{vid}")
idx = ir.Constant(ir.IntType(32), i)
ptr = builder.gep(live_array, [idx])
@ -104,4 +111,4 @@ def insert_automatic_safepoint(
check_func = ir.Function(module, func_type, name="ny_check_safepoint")
# Insert safepoint check
builder.call(check_func, [], name=f"safepoint_{location}")
builder.call(check_func, [], name=f"safepoint_{location}")

View File

@ -13,7 +13,10 @@ def lower_typeop(
dst_vid: int,
target_type: Optional[str],
vmap: Dict[int, ir.Value],
resolver=None
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower MIR TypeOp instruction
@ -32,7 +35,10 @@ def lower_typeop(
vmap: Value map
resolver: Optional resolver for type handling
"""
src_val = vmap.get(src_vid, ir.Constant(ir.IntType(64), 0))
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
src_val = resolver.resolve_i64(src_vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
src_val = vmap.get(src_vid, ir.Constant(ir.IntType(64), 0))
if op == "cast":
# Type casting - for now just pass through
@ -73,7 +79,11 @@ def lower_convert(
dst_vid: int,
from_type: str,
to_type: str,
vmap: Dict[int, ir.Value]
vmap: Dict[int, ir.Value],
resolver=None,
preds=None,
block_end_values=None,
bb_map=None
) -> None:
"""
Lower type conversion between primitive types
@ -86,7 +96,14 @@ def lower_convert(
to_type: Target type
vmap: Value map
"""
src_val = vmap.get(src_vid)
if resolver is not None and preds is not None and block_end_values is not None and bb_map is not None:
# Choose resolution based on from_type
if from_type == "ptr":
src_val = resolver.resolve_ptr(src_vid, builder.block, preds, block_end_values, vmap)
else:
src_val = resolver.resolve_i64(src_vid, builder.block, preds, block_end_values, vmap, bb_map)
else:
src_val = vmap.get(src_vid)
if not src_val:
# Default based on target type
if to_type == "f64":
@ -108,10 +125,10 @@ def lower_convert(
elif from_type == "i64" and to_type == "ptr":
# int to pointer
i8 = ir.IntType(8)
result = builder.inttoptr(src_val, i8.as_pointer())
result = builder.inttoptr(src_val, i8.as_pointer(), name=f"conv_i2p_{dst_vid}")
elif from_type == "ptr" and to_type == "i64":
# pointer to int
result = builder.ptrtoint(src_val, ir.IntType(64))
result = builder.ptrtoint(src_val, ir.IntType(64), name=f"conv_p2i_{dst_vid}")
elif from_type == "i32" and to_type == "i64":
# sign extend
result = builder.sext(src_val, ir.IntType(64))
@ -122,4 +139,4 @@ def lower_convert(
# Unknown conversion - pass through
result = src_val
vmap[dst_vid] = result
vmap[dst_vid] = result