📚 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
|
||||
|
||||
@ -14,6 +14,7 @@ import llvmlite.binding as llvm
|
||||
# Import instruction handlers
|
||||
from instructions.const import lower_const
|
||||
from instructions.binop import lower_binop
|
||||
from instructions.compare import lower_compare
|
||||
from instructions.jump import lower_jump
|
||||
from instructions.branch import lower_branch
|
||||
from instructions.ret import lower_return
|
||||
@ -53,8 +54,11 @@ class NyashLLVMBuilder:
|
||||
self.vmap: Dict[int, ir.Value] = {} # value_id -> LLVM value
|
||||
self.bb_map: Dict[int, ir.Block] = {} # block_id -> LLVM block
|
||||
|
||||
# PHI deferrals for sealed block approach
|
||||
self.phi_deferrals: List[Tuple[int, List[Tuple[int, int]]]] = []
|
||||
# PHI deferrals for sealed block approach: (block_id, dst_vid, incoming)
|
||||
self.phi_deferrals: List[Tuple[int, int, List[Tuple[int, int]]]] = []
|
||||
# Predecessor map and per-block end snapshots
|
||||
self.preds: Dict[int, List[int]] = {}
|
||||
self.block_end_values: Dict[int, Dict[int, ir.Value]] = {}
|
||||
|
||||
# Resolver for unified value resolution
|
||||
self.resolver = Resolver(self.vmap, self.bb_map)
|
||||
@ -72,12 +76,66 @@ class NyashLLVMBuilder:
|
||||
# No functions - create dummy ny_main
|
||||
return self._create_dummy_main()
|
||||
|
||||
# Pre-declare all functions with default i64 signature to allow cross-calls
|
||||
import re
|
||||
for func_data in functions:
|
||||
name = func_data.get("name", "unknown")
|
||||
# Derive arity from name suffix '/N' if params list is empty
|
||||
m = re.search(r"/(\d+)$", name)
|
||||
if m:
|
||||
arity = int(m.group(1))
|
||||
else:
|
||||
arity = len(func_data.get("params", []))
|
||||
if name == "ny_main":
|
||||
fty = ir.FunctionType(self.i32, [])
|
||||
else:
|
||||
fty = ir.FunctionType(self.i64, [self.i64] * arity)
|
||||
exists = False
|
||||
for f in self.module.functions:
|
||||
if f.name == name:
|
||||
exists = True
|
||||
break
|
||||
if not exists:
|
||||
ir.Function(self.module, fty, name=name)
|
||||
|
||||
# Process each function
|
||||
for func_data in functions:
|
||||
self.lower_function(func_data)
|
||||
|
||||
# Wire deferred PHIs
|
||||
self._wire_deferred_phis()
|
||||
|
||||
# Create ny_main wrapper if necessary
|
||||
has_ny_main = any(f.name == 'ny_main' for f in self.module.functions)
|
||||
main_fn = None
|
||||
for f in self.module.functions:
|
||||
if f.name == 'main':
|
||||
main_fn = f
|
||||
break
|
||||
if main_fn is not None:
|
||||
# Hide the user main to avoid conflict with NyRT's main symbol
|
||||
try:
|
||||
main_fn.linkage = 'private'
|
||||
except Exception:
|
||||
pass
|
||||
if not has_ny_main:
|
||||
# i32 ny_main() { return (i32) main(); }
|
||||
ny_main_ty = ir.FunctionType(self.i32, [])
|
||||
ny_main = ir.Function(self.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
if len(main_fn.args) == 0:
|
||||
rv = b.call(main_fn, [], name='call_user_main')
|
||||
else:
|
||||
# If signature mismatches, return 0
|
||||
rv = ir.Constant(self.i64, 0)
|
||||
if hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width != 32:
|
||||
rv32 = b.trunc(rv, self.i32) if rv.type.width > 32 else b.zext(rv, self.i32)
|
||||
b.ret(rv32)
|
||||
elif hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width == 32:
|
||||
b.ret(rv)
|
||||
else:
|
||||
b.ret(ir.Constant(self.i32, 0))
|
||||
|
||||
return str(self.module)
|
||||
|
||||
@ -93,6 +151,7 @@ class NyashLLVMBuilder:
|
||||
def lower_function(self, func_data: Dict[str, Any]):
|
||||
"""Lower a single MIR function to LLVM IR"""
|
||||
name = func_data.get("name", "unknown")
|
||||
import re
|
||||
params = func_data.get("params", [])
|
||||
blocks = func_data.get("blocks", [])
|
||||
|
||||
@ -101,13 +160,50 @@ class NyashLLVMBuilder:
|
||||
# Special case: ny_main returns i32
|
||||
func_ty = ir.FunctionType(self.i32, [])
|
||||
else:
|
||||
# Default: i64(i64, ...) signature
|
||||
param_types = [self.i64] * len(params)
|
||||
# Default: i64(i64, ...) signature; derive arity from '/N' suffix when params missing
|
||||
m = re.search(r"/(\d+)$", name)
|
||||
arity = int(m.group(1)) if m else len(params)
|
||||
param_types = [self.i64] * arity
|
||||
func_ty = ir.FunctionType(self.i64, param_types)
|
||||
|
||||
# Create function
|
||||
func = ir.Function(self.module, func_ty, name=name)
|
||||
# Create or reuse function
|
||||
func = None
|
||||
for f in self.module.functions:
|
||||
if f.name == name:
|
||||
func = f
|
||||
break
|
||||
if func is None:
|
||||
func = ir.Function(self.module, func_ty, name=name)
|
||||
|
||||
# Map parameters to vmap (value_id: 0..arity-1)
|
||||
try:
|
||||
arity = len(func.args)
|
||||
for i in range(arity):
|
||||
self.vmap[i] = func.args[i]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Build predecessor map from control-flow edges
|
||||
self.preds = {}
|
||||
for block_data in blocks:
|
||||
bid = block_data.get("id", 0)
|
||||
self.preds.setdefault(bid, [])
|
||||
for block_data in blocks:
|
||||
src = block_data.get("id", 0)
|
||||
for inst in block_data.get("instructions", []):
|
||||
op = inst.get("op")
|
||||
if op == "jump":
|
||||
t = inst.get("target")
|
||||
if t is not None:
|
||||
self.preds.setdefault(t, []).append(src)
|
||||
elif op == "branch":
|
||||
th = inst.get("then")
|
||||
el = inst.get("else")
|
||||
if th is not None:
|
||||
self.preds.setdefault(th, []).append(src)
|
||||
if el is not None:
|
||||
self.preds.setdefault(el, []).append(src)
|
||||
|
||||
# Create all blocks first
|
||||
for block_data in blocks:
|
||||
bid = block_data.get("id", 0)
|
||||
@ -124,11 +220,40 @@ class NyashLLVMBuilder:
|
||||
def lower_block(self, bb: ir.Block, block_data: Dict[str, Any], func: ir.Function):
|
||||
"""Lower a single basic block"""
|
||||
builder = ir.IRBuilder(bb)
|
||||
# Provide builder/module to resolver for PHI/casts insertion
|
||||
try:
|
||||
self.resolver.builder = builder
|
||||
self.resolver.module = self.module
|
||||
except Exception:
|
||||
pass
|
||||
instructions = block_data.get("instructions", [])
|
||||
created_ids: List[int] = []
|
||||
|
||||
# Process each instruction
|
||||
for inst in instructions:
|
||||
self.lower_instruction(builder, inst, func)
|
||||
try:
|
||||
dst = inst.get("dst")
|
||||
if isinstance(dst, int) and dst not in created_ids and dst in self.vmap:
|
||||
created_ids.append(dst)
|
||||
except Exception:
|
||||
pass
|
||||
# Snapshot end-of-block values for sealed PHI wiring
|
||||
bid = block_data.get("id", 0)
|
||||
snap: Dict[int, ir.Value] = {}
|
||||
# include function args (avoid 0 constant confusion later via special-case)
|
||||
try:
|
||||
arity = len(func.args)
|
||||
except Exception:
|
||||
arity = 0
|
||||
for i in range(arity):
|
||||
if i in self.vmap:
|
||||
snap[i] = self.vmap[i]
|
||||
for vid in created_ids:
|
||||
val = self.vmap.get(vid)
|
||||
if val is not None:
|
||||
snap[vid] = val
|
||||
self.block_end_values[bid] = snap
|
||||
|
||||
def lower_instruction(self, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||
"""Dispatch instruction to appropriate handler"""
|
||||
@ -137,15 +262,15 @@ class NyashLLVMBuilder:
|
||||
if op == "const":
|
||||
dst = inst.get("dst")
|
||||
value = inst.get("value")
|
||||
lower_const(builder, self.module, dst, value, self.vmap)
|
||||
lower_const(builder, self.module, dst, value, self.vmap, self.resolver)
|
||||
|
||||
elif op == "binop":
|
||||
operation = inst.get("operation")
|
||||
lhs = inst.get("lhs")
|
||||
rhs = inst.get("rhs")
|
||||
dst = inst.get("dst")
|
||||
lower_binop(builder, self.resolver, operation, lhs, rhs, dst,
|
||||
self.vmap, builder.block)
|
||||
lower_binop(builder, self.resolver, operation, lhs, rhs, dst,
|
||||
self.vmap, builder.block, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "jump":
|
||||
target = inst.get("target")
|
||||
@ -155,38 +280,48 @@ class NyashLLVMBuilder:
|
||||
cond = inst.get("cond")
|
||||
then_bid = inst.get("then")
|
||||
else_bid = inst.get("else")
|
||||
lower_branch(builder, cond, then_bid, else_bid, self.vmap, self.bb_map)
|
||||
lower_branch(builder, cond, then_bid, else_bid, self.vmap, self.bb_map, self.resolver, self.preds, self.block_end_values)
|
||||
|
||||
elif op == "ret":
|
||||
value = inst.get("value")
|
||||
lower_return(builder, value, self.vmap, func.return_value.type)
|
||||
lower_return(builder, value, self.vmap, func.function_type.return_type,
|
||||
self.resolver, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "phi":
|
||||
dst = inst.get("dst")
|
||||
incoming = inst.get("incoming", [])
|
||||
# Defer PHI wiring for now
|
||||
defer_phi_wiring(dst, incoming, self.phi_deferrals)
|
||||
# Wire PHI immediately at the start of the current block using snapshots
|
||||
lower_phi(builder, dst, incoming, self.vmap, self.bb_map, builder.block, self.resolver, self.block_end_values, self.preds)
|
||||
|
||||
elif op == "compare":
|
||||
# Dedicated compare op
|
||||
operation = inst.get("operation") or inst.get("op")
|
||||
lhs = inst.get("lhs")
|
||||
rhs = inst.get("rhs")
|
||||
dst = inst.get("dst")
|
||||
lower_compare(builder, operation, lhs, rhs, dst, self.vmap,
|
||||
self.resolver, builder.block, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "call":
|
||||
func_name = inst.get("func")
|
||||
args = inst.get("args", [])
|
||||
dst = inst.get("dst")
|
||||
lower_call(builder, self.module, func_name, args, dst, self.vmap, self.resolver)
|
||||
lower_call(builder, self.module, func_name, args, dst, self.vmap, self.resolver, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "boxcall":
|
||||
box_vid = inst.get("box")
|
||||
method = inst.get("method")
|
||||
args = inst.get("args", [])
|
||||
dst = inst.get("dst")
|
||||
lower_boxcall(builder, self.module, box_vid, method, args, dst,
|
||||
self.vmap, self.resolver)
|
||||
lower_boxcall(builder, self.module, box_vid, method, args, dst,
|
||||
self.vmap, self.resolver, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "externcall":
|
||||
func_name = inst.get("func")
|
||||
args = inst.get("args", [])
|
||||
dst = inst.get("dst")
|
||||
lower_externcall(builder, self.module, func_name, args, dst,
|
||||
self.vmap, self.resolver)
|
||||
lower_externcall(builder, self.module, func_name, args, dst,
|
||||
self.vmap, self.resolver, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "newbox":
|
||||
box_type = inst.get("type")
|
||||
@ -200,12 +335,14 @@ class NyashLLVMBuilder:
|
||||
src = inst.get("src")
|
||||
dst = inst.get("dst")
|
||||
target_type = inst.get("target_type")
|
||||
lower_typeop(builder, operation, src, dst, target_type,
|
||||
self.vmap, self.resolver)
|
||||
lower_typeop(builder, operation, src, dst, target_type,
|
||||
self.vmap, self.resolver, self.preds, self.block_end_values, self.bb_map)
|
||||
|
||||
elif op == "safepoint":
|
||||
live = inst.get("live", [])
|
||||
lower_safepoint(builder, self.module, live, self.vmap)
|
||||
lower_safepoint(builder, self.module, live, self.vmap,
|
||||
resolver=self.resolver, preds=self.preds,
|
||||
block_end_values=self.block_end_values, bb_map=self.bb_map)
|
||||
|
||||
elif op == "barrier":
|
||||
barrier_type = inst.get("type", "memory")
|
||||
@ -216,8 +353,9 @@ class NyashLLVMBuilder:
|
||||
cond = inst.get("cond")
|
||||
body = inst.get("body", [])
|
||||
self.loop_count += 1
|
||||
if not lower_while_loopform(builder, func, cond, body,
|
||||
self.loop_count, self.vmap, self.bb_map):
|
||||
if not lower_while_loopform(builder, func, cond, body,
|
||||
self.loop_count, self.vmap, self.bb_map,
|
||||
self.resolver, self.preds, self.block_end_values):
|
||||
# Fallback to regular while
|
||||
self._lower_while_regular(builder, inst, func)
|
||||
else:
|
||||
@ -226,16 +364,130 @@ class NyashLLVMBuilder:
|
||||
|
||||
def _lower_while_regular(self, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||
"""Fallback regular while lowering"""
|
||||
# TODO: Implement regular while lowering
|
||||
pass
|
||||
# Create basic blocks: cond -> body -> cond, and exit
|
||||
cond_vid = inst.get("cond")
|
||||
body_insts = inst.get("body", [])
|
||||
|
||||
cur_bb = builder.block
|
||||
cond_bb = func.append_basic_block(name=f"while{self.loop_count}_cond")
|
||||
body_bb = func.append_basic_block(name=f"while{self.loop_count}_body")
|
||||
exit_bb = func.append_basic_block(name=f"while{self.loop_count}_exit")
|
||||
|
||||
# Jump from current to cond
|
||||
builder.branch(cond_bb)
|
||||
|
||||
# Cond block
|
||||
cbuild = ir.IRBuilder(cond_bb)
|
||||
try:
|
||||
cond_val = self.resolver.resolve_i64(cond_vid, builder.block, self.preds, self.block_end_values, self.vmap, self.bb_map)
|
||||
except Exception:
|
||||
cond_val = self.vmap.get(cond_vid)
|
||||
if cond_val is None:
|
||||
cond_val = ir.Constant(self.i1, 0)
|
||||
# Normalize to i1
|
||||
if hasattr(cond_val, 'type'):
|
||||
if isinstance(cond_val.type, ir.IntType) and cond_val.type.width == 64:
|
||||
zero64 = ir.Constant(self.i64, 0)
|
||||
cond_val = cbuild.icmp_unsigned('!=', cond_val, zero64, name="while_cond_i1")
|
||||
elif isinstance(cond_val.type, ir.PointerType):
|
||||
nullp = ir.Constant(cond_val.type, None)
|
||||
cond_val = cbuild.icmp_unsigned('!=', cond_val, nullp, name="while_cond_p1")
|
||||
elif isinstance(cond_val.type, ir.IntType) and cond_val.type.width == 1:
|
||||
# already i1
|
||||
pass
|
||||
else:
|
||||
# Fallback: treat as false
|
||||
cond_val = ir.Constant(self.i1, 0)
|
||||
else:
|
||||
cond_val = ir.Constant(self.i1, 0)
|
||||
|
||||
cbuild.cbranch(cond_val, body_bb, exit_bb)
|
||||
|
||||
# Body block
|
||||
bbuild = ir.IRBuilder(body_bb)
|
||||
# Allow nested lowering of body instructions within this block
|
||||
self._lower_instruction_list(bbuild, body_insts, func)
|
||||
# Ensure terminator: if not terminated, branch back to cond
|
||||
if bbuild.block.terminator is None:
|
||||
bbuild.branch(cond_bb)
|
||||
|
||||
# Continue at exit
|
||||
builder.position_at_end(exit_bb)
|
||||
|
||||
def _lower_instruction_list(self, builder: ir.IRBuilder, insts: List[Dict[str, Any]], func: ir.Function):
|
||||
"""Lower a flat list of instructions using current builder and function."""
|
||||
for sub in insts:
|
||||
# If current block already has a terminator, create a continuation block
|
||||
if builder.block.terminator is not None:
|
||||
cont = func.append_basic_block(name=f"cont_bb_{builder.block.name}")
|
||||
builder.position_at_end(cont)
|
||||
self.lower_instruction(builder, sub, func)
|
||||
|
||||
def _wire_deferred_phis(self):
|
||||
"""Wire all deferred PHI nodes"""
|
||||
# TODO: Implement PHI wiring after all blocks are created
|
||||
for dst_vid, incoming in self.phi_deferrals:
|
||||
# Find the block containing this PHI
|
||||
# Wire the incoming edges
|
||||
pass
|
||||
for cur_bid, dst_vid, incoming in self.phi_deferrals:
|
||||
bb = self.bb_map.get(cur_bid)
|
||||
if bb is None:
|
||||
continue
|
||||
b = ir.IRBuilder(bb)
|
||||
b.position_at_start(bb)
|
||||
# Determine phi type: prefer pointer if any incoming is pointer; else f64; else i64
|
||||
phi_type = self.i64
|
||||
for (val_id, pred_bid) in incoming:
|
||||
snap = self.block_end_values.get(pred_bid, {})
|
||||
val = snap.get(val_id)
|
||||
if val is not None and hasattr(val, 'type'):
|
||||
if hasattr(val.type, 'is_pointer') and val.type.is_pointer:
|
||||
phi_type = val.type
|
||||
break
|
||||
elif str(val.type) == str(self.f64):
|
||||
phi_type = self.f64
|
||||
phi = b.phi(phi_type, name=f"phi_{dst_vid}")
|
||||
for (val_id, pred_bid) in incoming:
|
||||
pred_bb = self.bb_map.get(pred_bid)
|
||||
if pred_bb is None:
|
||||
continue
|
||||
# Self-reference takes precedence regardless of snapshot
|
||||
if val_id == dst_vid:
|
||||
val = phi
|
||||
else:
|
||||
snap = self.block_end_values.get(pred_bid, {})
|
||||
# Special-case: incoming 0 means typed zero/null, not value-id 0
|
||||
if isinstance(val_id, int) and val_id == 0:
|
||||
val = None
|
||||
else:
|
||||
val = snap.get(val_id)
|
||||
if val is None:
|
||||
# Default based on phi type
|
||||
if isinstance(phi_type, ir.IntType):
|
||||
val = ir.Constant(phi_type, 0)
|
||||
elif isinstance(phi_type, ir.DoubleType):
|
||||
val = ir.Constant(phi_type, 0.0)
|
||||
else:
|
||||
val = ir.Constant(phi_type, None)
|
||||
# Type adjust if needed
|
||||
if hasattr(val, 'type') and val.type != phi_type:
|
||||
# Insert cast in predecessor block before its terminator
|
||||
pb = ir.IRBuilder(pred_bb)
|
||||
try:
|
||||
term = pred_bb.terminator
|
||||
if term is not None:
|
||||
pb.position_before(term)
|
||||
else:
|
||||
pb.position_at_end(pred_bb)
|
||||
except Exception:
|
||||
pb.position_at_end(pred_bb)
|
||||
if isinstance(phi_type, ir.IntType) and isinstance(val.type, ir.PointerType):
|
||||
val = pb.ptrtoint(val, phi_type, name=f"phi_p2i_{dst_vid}_{pred_bid}")
|
||||
elif isinstance(phi_type, ir.PointerType) and isinstance(val.type, ir.IntType):
|
||||
val = pb.inttoptr(val, phi_type, name=f"phi_i2p_{dst_vid}_{pred_bid}")
|
||||
elif isinstance(phi_type, ir.IntType) and isinstance(val.type, ir.IntType):
|
||||
if phi_type.width > val.type.width:
|
||||
val = pb.zext(val, phi_type, name=f"phi_zext_{dst_vid}_{pred_bid}")
|
||||
elif phi_type.width < val.type.width:
|
||||
val = pb.trunc(val, phi_type, name=f"phi_trunc_{dst_vid}_{pred_bid}")
|
||||
phi.add_incoming(val, pred_bb)
|
||||
self.vmap[dst_vid] = phi
|
||||
|
||||
def compile_to_object(self, output_path: str):
|
||||
"""Compile module to object file"""
|
||||
@ -255,31 +507,52 @@ class NyashLLVMBuilder:
|
||||
f.write(obj)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: llvm_builder.py <input.mir.json> [-o output.o]")
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
# CLI:
|
||||
# llvm_builder.py <input.mir.json> [-o output.o]
|
||||
# llvm_builder.py --dummy [-o output.o]
|
||||
output_file = "nyash_llvm_py.o"
|
||||
|
||||
if "-o" in sys.argv:
|
||||
idx = sys.argv.index("-o")
|
||||
if idx + 1 < len(sys.argv):
|
||||
output_file = sys.argv[idx + 1]
|
||||
|
||||
# Read MIR JSON
|
||||
args = sys.argv[1:]
|
||||
dummy = False
|
||||
|
||||
if not args:
|
||||
print("Usage: llvm_builder.py <input.mir.json> [-o output.o] | --dummy [-o output.o]")
|
||||
sys.exit(1)
|
||||
|
||||
if "-o" in args:
|
||||
idx = args.index("-o")
|
||||
if idx + 1 < len(args):
|
||||
output_file = args[idx + 1]
|
||||
del args[idx:idx+2]
|
||||
|
||||
if args and args[0] == "--dummy":
|
||||
dummy = True
|
||||
del args[0]
|
||||
|
||||
builder = NyashLLVMBuilder()
|
||||
|
||||
if dummy:
|
||||
# Emit dummy ny_main
|
||||
ir_text = builder._create_dummy_main()
|
||||
if os.environ.get('NYASH_CLI_VERBOSE') == '1':
|
||||
print(f"[Python LLVM] Generated dummy IR:\n{ir_text}")
|
||||
builder.compile_to_object(output_file)
|
||||
print(f"Compiled to {output_file}")
|
||||
return
|
||||
|
||||
if not args:
|
||||
print("error: missing input MIR JSON (or use --dummy)", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
input_file = args[0]
|
||||
with open(input_file, 'r') as f:
|
||||
mir_json = json.load(f)
|
||||
|
||||
# Build LLVM IR
|
||||
builder = NyashLLVMBuilder()
|
||||
|
||||
llvm_ir = builder.build_from_mir(mir_json)
|
||||
|
||||
print(f"Generated LLVM IR:\n{llvm_ir}")
|
||||
|
||||
# Compile to object
|
||||
if os.environ.get('NYASH_CLI_VERBOSE') == '1':
|
||||
print(f"[Python LLVM] Generated LLVM IR:\n{llvm_ir}")
|
||||
|
||||
builder.compile_to_object(output_file)
|
||||
print(f"Compiled to {output_file}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@ -15,14 +15,23 @@ class Resolver:
|
||||
- Cache per (block, value) to avoid redundant PHIs
|
||||
"""
|
||||
|
||||
def __init__(self, builder: ir.IRBuilder, module: ir.Module):
|
||||
self.builder = builder
|
||||
self.module = module
|
||||
def __init__(self, a, b=None):
|
||||
"""Flexible init: either (builder, module) or (vmap, bb_map) for legacy wiring."""
|
||||
if hasattr(a, 'position_at_end'):
|
||||
# a is IRBuilder
|
||||
self.builder = a
|
||||
self.module = b
|
||||
else:
|
||||
# Legacy constructor (vmap, bb_map) — builder/module will be set later when available
|
||||
self.builder = None
|
||||
self.module = None
|
||||
|
||||
# Caches: (block_name, value_id) -> llvm value
|
||||
self.i64_cache: Dict[Tuple[str, int], ir.Value] = {}
|
||||
self.ptr_cache: Dict[Tuple[str, int], ir.Value] = {}
|
||||
self.f64_cache: Dict[Tuple[str, int], ir.Value] = {}
|
||||
# String literal map: value_id -> Python string (for by-name calls)
|
||||
self.string_literals: Dict[int, str] = {}
|
||||
|
||||
# Type shortcuts
|
||||
self.i64 = ir.IntType(64)
|
||||
@ -33,9 +42,10 @@ class Resolver:
|
||||
self,
|
||||
value_id: int,
|
||||
current_block: ir.Block,
|
||||
preds: Dict[str, list],
|
||||
block_end_values: Dict[str, Dict[int, Any]],
|
||||
vmap: Dict[int, Any]
|
||||
preds: Dict[int, list],
|
||||
block_end_values: Dict[int, Dict[int, Any]],
|
||||
vmap: Dict[int, Any],
|
||||
bb_map: Optional[Dict[int, ir.Block]] = None
|
||||
) -> ir.Value:
|
||||
"""
|
||||
Resolve a MIR value as i64 dominating the current block.
|
||||
@ -46,31 +56,81 @@ class Resolver:
|
||||
# Check cache
|
||||
if cache_key in self.i64_cache:
|
||||
return self.i64_cache[cache_key]
|
||||
|
||||
# Do not trust global vmap across blocks: always localize via preds when available
|
||||
|
||||
# Get predecessor blocks
|
||||
pred_names = preds.get(current_block.name, [])
|
||||
try:
|
||||
bid = int(str(current_block.name).replace('bb',''))
|
||||
except Exception:
|
||||
bid = -1
|
||||
pred_ids = [p for p in preds.get(bid, []) if p != bid]
|
||||
|
||||
if not pred_names:
|
||||
if not pred_ids:
|
||||
# Entry block or no predecessors
|
||||
base_val = vmap.get(value_id, ir.Constant(self.i64, 0))
|
||||
result = self._coerce_to_i64(base_val)
|
||||
# Do not emit casts here; if pointer, fall back to zero
|
||||
if hasattr(base_val, 'type') and isinstance(base_val.type, ir.IntType):
|
||||
result = base_val if base_val.type.width == 64 else ir.Constant(self.i64, 0)
|
||||
elif hasattr(base_val, 'type') and isinstance(base_val.type, ir.PointerType):
|
||||
result = ir.Constant(self.i64, 0)
|
||||
else:
|
||||
result = ir.Constant(self.i64, 0)
|
||||
else:
|
||||
# Create PHI at block start
|
||||
saved_pos = self.builder.block
|
||||
self.builder.position_at_start(current_block)
|
||||
saved_pos = None
|
||||
if self.builder is not None:
|
||||
saved_pos = self.builder.block
|
||||
self.builder.position_at_start(current_block)
|
||||
|
||||
phi = self.builder.phi(self.i64, name=f"loc_i64_{value_id}")
|
||||
|
||||
# Add incoming values from predecessors
|
||||
for pred_name in pred_names:
|
||||
pred_vals = block_end_values.get(pred_name, {})
|
||||
val = pred_vals.get(value_id, ir.Constant(self.i64, 0))
|
||||
coerced = self._coerce_to_i64(val)
|
||||
# Note: In real implementation, need pred block reference
|
||||
phi.add_incoming(coerced, pred_name) # Simplified
|
||||
for pred_id in pred_ids:
|
||||
pred_vals = block_end_values.get(pred_id, {})
|
||||
val = pred_vals.get(value_id)
|
||||
# Coerce in predecessor block if needed
|
||||
if val is None:
|
||||
coerced = ir.Constant(self.i64, 0)
|
||||
else:
|
||||
if hasattr(val, 'type') and isinstance(val.type, ir.IntType):
|
||||
coerced = val if val.type.width == 64 else ir.Constant(self.i64, 0)
|
||||
elif hasattr(val, 'type') and isinstance(val.type, ir.PointerType):
|
||||
# insert ptrtoint in predecessor
|
||||
pred_bb = bb_map.get(pred_id) if bb_map is not None else None
|
||||
if pred_bb is not None:
|
||||
pb = ir.IRBuilder(pred_bb)
|
||||
try:
|
||||
term = pred_bb.terminator
|
||||
if term is not None:
|
||||
pb.position_before(term)
|
||||
else:
|
||||
pb.position_at_end(pred_bb)
|
||||
except Exception:
|
||||
pb.position_at_end(pred_bb)
|
||||
coerced = pb.ptrtoint(val, self.i64, name=f"res_p2i_{value_id}_{pred_id}")
|
||||
else:
|
||||
coerced = ir.Constant(self.i64, 0)
|
||||
else:
|
||||
coerced = ir.Constant(self.i64, 0)
|
||||
# Use predecessor block if available
|
||||
pred_bb = None
|
||||
if bb_map is not None:
|
||||
pred_bb = bb_map.get(pred_id)
|
||||
if pred_bb is not None:
|
||||
phi.add_incoming(coerced, pred_bb)
|
||||
# If no valid incoming were added, fold to zero to avoid invalid PHI
|
||||
if len(getattr(phi, 'incoming', [])) == 0:
|
||||
# Replace with zero constant and discard phi
|
||||
result = ir.Constant(self.i64, 0)
|
||||
# Restore position and cache
|
||||
if saved_pos and self.builder is not None:
|
||||
self.builder.position_at_end(saved_pos)
|
||||
self.i64_cache[cache_key] = result
|
||||
return result
|
||||
|
||||
# Restore position
|
||||
if saved_pos:
|
||||
if saved_pos and self.builder is not None:
|
||||
self.builder.position_at_end(saved_pos)
|
||||
|
||||
result = phi
|
||||
@ -82,16 +142,51 @@ class Resolver:
|
||||
def resolve_ptr(self, value_id: int, current_block: ir.Block,
|
||||
preds: Dict, block_end_values: Dict, vmap: Dict) -> ir.Value:
|
||||
"""Resolve as i8* pointer"""
|
||||
# Similar to resolve_i64 but with pointer type
|
||||
# TODO: Implement
|
||||
pass
|
||||
cache_key = (current_block.name, value_id)
|
||||
if cache_key in self.ptr_cache:
|
||||
return self.ptr_cache[cache_key]
|
||||
# Coerce current vmap value or GlobalVariable to i8*
|
||||
val = vmap.get(value_id)
|
||||
if val is None:
|
||||
result = ir.Constant(self.i8p, None)
|
||||
else:
|
||||
if hasattr(val, 'type') and isinstance(val, ir.PointerType):
|
||||
# If pointer to array (GlobalVariable), GEP to first element
|
||||
ty = val.type.pointee if hasattr(val.type, 'pointee') else None
|
||||
if ty is not None and hasattr(ty, 'element'):
|
||||
c0 = ir.Constant(ir.IntType(32), 0)
|
||||
result = self.builder.gep(val, [c0, c0], name=f"res_str_gep_{value_id}")
|
||||
else:
|
||||
result = val
|
||||
elif hasattr(val, 'type') and isinstance(val.type, ir.IntType):
|
||||
result = self.builder.inttoptr(val, self.i8p, name=f"res_i2p_{value_id}")
|
||||
else:
|
||||
# f64 or others -> zero
|
||||
result = ir.Constant(self.i8p, None)
|
||||
self.ptr_cache[cache_key] = result
|
||||
return result
|
||||
|
||||
def resolve_f64(self, value_id: int, current_block: ir.Block,
|
||||
preds: Dict, block_end_values: Dict, vmap: Dict) -> ir.Value:
|
||||
"""Resolve as f64"""
|
||||
# Similar pattern
|
||||
# TODO: Implement
|
||||
pass
|
||||
cache_key = (current_block.name, value_id)
|
||||
if cache_key in self.f64_cache:
|
||||
return self.f64_cache[cache_key]
|
||||
val = vmap.get(value_id)
|
||||
if val is None:
|
||||
result = ir.Constant(self.f64_type, 0.0)
|
||||
else:
|
||||
if hasattr(val, 'type') and val.type == self.f64_type:
|
||||
result = val
|
||||
elif hasattr(val, 'type') and isinstance(val.type, ir.IntType):
|
||||
result = self.builder.sitofp(val, self.f64_type)
|
||||
elif hasattr(val, 'type') and isinstance(val.type, ir.PointerType):
|
||||
tmp = self.builder.ptrtoint(val, self.i64, name=f"res_p2i_{value_id}")
|
||||
result = self.builder.sitofp(tmp, self.f64_type, name=f"res_i2f_{value_id}")
|
||||
else:
|
||||
result = ir.Constant(self.f64_type, 0.0)
|
||||
self.f64_cache[cache_key] = result
|
||||
return result
|
||||
|
||||
def _coerce_to_i64(self, val: Any) -> ir.Value:
|
||||
"""Coerce various types to i64"""
|
||||
@ -99,14 +194,14 @@ class Resolver:
|
||||
return val
|
||||
elif hasattr(val, 'type') and val.type.is_pointer:
|
||||
# ptr to int
|
||||
return self.builder.ptrtoint(val, self.i64)
|
||||
return self.builder.ptrtoint(val, self.i64, name=f"res_p2i_{getattr(val,'name','x')}") if self.builder is not None else ir.Constant(self.i64, 0)
|
||||
elif hasattr(val, 'type') and isinstance(val.type, ir.IntType):
|
||||
# int to int (extend/trunc)
|
||||
if val.type.width < 64:
|
||||
return self.builder.zext(val, self.i64)
|
||||
return self.builder.zext(val, self.i64) if self.builder is not None else ir.Constant(self.i64, 0)
|
||||
elif val.type.width > 64:
|
||||
return self.builder.trunc(val, self.i64)
|
||||
return self.builder.trunc(val, self.i64) if self.builder is not None else ir.Constant(self.i64, 0)
|
||||
return val
|
||||
else:
|
||||
# Default zero
|
||||
return ir.Constant(self.i64, 0)
|
||||
return ir.Constant(self.i64, 0)
|
||||
|
||||
Reference in New Issue
Block a user