📚 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:
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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}")
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user