✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
"""
|
|
|
|
|
Call instruction lowering
|
|
|
|
|
Handles regular function calls (not BoxCall or ExternCall)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import llvmlite.ir as ir
|
2025-09-17 16:11:01 +09:00
|
|
|
from typing import Dict, List, Optional, Any
|
|
|
|
|
from trace import debug as trace_debug
|
2025-09-17 20:33:19 +09:00
|
|
|
from instructions.safepoint import insert_automatic_safepoint
|
2025-11-21 09:38:49 +09:00
|
|
|
from naming_helper import encode_static_method
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
|
|
|
|
|
def lower_call(
|
|
|
|
|
builder: ir.IRBuilder,
|
|
|
|
|
module: ir.Module,
|
|
|
|
|
func_name: str,
|
|
|
|
|
args: List[int],
|
|
|
|
|
dst_vid: Optional[int],
|
|
|
|
|
vmap: Dict[int, ir.Value],
|
2025-09-13 15:37:58 +09:00
|
|
|
resolver=None,
|
|
|
|
|
preds=None,
|
|
|
|
|
block_end_values=None,
|
2025-09-17 16:11:01 +09:00
|
|
|
bb_map=None,
|
|
|
|
|
ctx: Optional[Any] = None,
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Lower MIR Call instruction
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
builder: Current LLVM IR builder
|
|
|
|
|
module: LLVM module
|
|
|
|
|
func_name: Function name to call
|
|
|
|
|
args: List of argument value IDs
|
|
|
|
|
dst_vid: Optional destination for return value
|
|
|
|
|
vmap: Value map
|
|
|
|
|
resolver: Optional resolver for type handling
|
|
|
|
|
"""
|
2025-09-17 16:11:01 +09:00
|
|
|
# If BuildCtx is provided, prefer its maps for consistency.
|
|
|
|
|
if ctx is not None:
|
|
|
|
|
try:
|
|
|
|
|
if getattr(ctx, 'resolver', None) is not None:
|
|
|
|
|
resolver = ctx.resolver
|
|
|
|
|
if getattr(ctx, 'preds', None) is not None and preds is None:
|
|
|
|
|
preds = ctx.preds
|
|
|
|
|
if getattr(ctx, 'block_end_values', None) is not None and block_end_values is None:
|
|
|
|
|
block_end_values = ctx.block_end_values
|
|
|
|
|
if getattr(ctx, 'bb_map', None) is not None and bb_map is None:
|
|
|
|
|
bb_map = ctx.bb_map
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
2025-09-17 20:33:19 +09:00
|
|
|
# Insert an automatic safepoint after the function call
|
|
|
|
|
try:
|
|
|
|
|
import os
|
|
|
|
|
if os.environ.get('NYASH_LLVM_AUTO_SAFEPOINT', '1') == '1':
|
|
|
|
|
insert_automatic_safepoint(builder, module, "function_call")
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
2025-09-17 16:11:01 +09:00
|
|
|
# Short-hands with ctx (backward-compatible fallback)
|
|
|
|
|
r = resolver
|
|
|
|
|
p = preds
|
|
|
|
|
bev = block_end_values
|
|
|
|
|
bbm = bb_map
|
|
|
|
|
if ctx is not None:
|
|
|
|
|
try:
|
|
|
|
|
r = getattr(ctx, 'resolver', r)
|
|
|
|
|
p = getattr(ctx, 'preds', p)
|
|
|
|
|
bev = getattr(ctx, 'block_end_values', bev)
|
|
|
|
|
bbm = getattr(ctx, 'bb_map', bbm)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Resolver helpers (prefer resolver when available)
|
|
|
|
|
def _res_i64(vid: int):
|
|
|
|
|
if r is not None and p is not None and bev is not None and bbm is not None:
|
|
|
|
|
try:
|
|
|
|
|
return r.resolve_i64(vid, builder.block, p, bev, vmap, bbm)
|
|
|
|
|
except Exception:
|
|
|
|
|
return None
|
|
|
|
|
return vmap.get(vid)
|
|
|
|
|
|
|
|
|
|
def _res_ptr(vid: int):
|
|
|
|
|
if r is not None and p is not None and bev is not None:
|
|
|
|
|
try:
|
|
|
|
|
return r.resolve_ptr(vid, builder.block, p, bev, vmap)
|
|
|
|
|
except Exception:
|
|
|
|
|
return None
|
|
|
|
|
return vmap.get(vid)
|
|
|
|
|
|
2025-09-13 15:37:58 +09:00
|
|
|
# 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)
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
# Look up function in module
|
|
|
|
|
func = None
|
2025-09-13 15:37:58 +09:00
|
|
|
if isinstance(actual_name, str):
|
|
|
|
|
for f in module.functions:
|
|
|
|
|
if f.name == actual_name:
|
|
|
|
|
func = f
|
|
|
|
|
break
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
|
|
|
|
|
if not func:
|
2025-11-11 17:04:33 +09:00
|
|
|
# Function not found - create declaration. Special-case well-known C symbols
|
|
|
|
|
# (print/println and nyash.console.*) to use pointer-based signature i8* -> i64.
|
|
|
|
|
i64 = ir.IntType(64)
|
|
|
|
|
i8p = ir.IntType(8).as_pointer()
|
2025-09-13 15:37:58 +09:00
|
|
|
name = actual_name if isinstance(actual_name, str) else "unknown_fn"
|
2025-11-11 17:04:33 +09:00
|
|
|
is_console = False
|
|
|
|
|
try:
|
|
|
|
|
if isinstance(name, str):
|
|
|
|
|
is_console = (name in ("print", "println")) or name.startswith("nyash.console.")
|
|
|
|
|
except Exception:
|
|
|
|
|
is_console = False
|
|
|
|
|
if is_console:
|
|
|
|
|
func_type = ir.FunctionType(i64, [i8p])
|
|
|
|
|
else:
|
|
|
|
|
# Default i64(int64,...) prototype
|
|
|
|
|
func_type = ir.FunctionType(i64, [i64] * len(args))
|
2025-09-13 15:37:58 +09:00
|
|
|
func = ir.Function(module, func_type, name=name)
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
|
2025-09-27 08:45:25 +09:00
|
|
|
# If calling a Dev-only predicate name (e.g., 'condition_fn') that lacks a body,
|
|
|
|
|
# synthesize a trivial definition that returns non-zero to satisfy linker during bring-up.
|
|
|
|
|
if isinstance(actual_name, str) and actual_name == 'condition_fn':
|
|
|
|
|
try:
|
|
|
|
|
if func is not None and len(list(func.blocks)) == 0:
|
|
|
|
|
b = ir.IRBuilder(func.append_basic_block('entry'))
|
|
|
|
|
rty = func.function_type.return_type
|
|
|
|
|
if isinstance(rty, ir.IntType):
|
|
|
|
|
b.ret(ir.Constant(rty, 1))
|
|
|
|
|
else:
|
|
|
|
|
b.ret_void()
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
# Prepare arguments
|
|
|
|
|
call_args = []
|
|
|
|
|
for i, arg_id in enumerate(args):
|
2025-09-13 15:37:58 +09:00
|
|
|
arg_val = None
|
|
|
|
|
if i < len(func.args):
|
|
|
|
|
expected_type = func.args[i].type
|
2025-09-17 16:11:01 +09:00
|
|
|
if hasattr(expected_type, 'is_pointer') and expected_type.is_pointer:
|
|
|
|
|
arg_val = _res_ptr(arg_id)
|
|
|
|
|
else:
|
|
|
|
|
arg_val = _res_i64(arg_id)
|
2025-09-13 15:37:58 +09:00
|
|
|
if arg_val is None:
|
|
|
|
|
arg_val = vmap.get(arg_id)
|
|
|
|
|
if arg_val is None:
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
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)
|
|
|
|
|
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):
|
2025-09-13 15:37:58 +09:00
|
|
|
arg_val = builder.inttoptr(arg_val, expected_type, name=f"call_i2p_{i}")
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
elif isinstance(expected_type, ir.IntType) and arg_val.type.is_pointer:
|
2025-09-13 15:37:58 +09:00
|
|
|
arg_val = builder.ptrtoint(arg_val, expected_type, name=f"call_p2i_{i}")
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
call_args.append(arg_val)
|
|
|
|
|
|
|
|
|
|
# Make the call
|
|
|
|
|
result = builder.call(func, call_args, name=f"call_{func_name}")
|
2025-11-21 09:38:49 +09:00
|
|
|
# NamingBox SSOT: Optional trace for final debugging
|
|
|
|
|
traced_names = (
|
|
|
|
|
encode_static_method("Main", "node_json", 3),
|
|
|
|
|
encode_static_method("Main", "esc_json", 1),
|
|
|
|
|
"main"
|
|
|
|
|
)
|
|
|
|
|
if isinstance(actual_name, str) and actual_name in traced_names:
|
2025-09-17 16:11:01 +09:00
|
|
|
trace_debug(f"[TRACE] call {actual_name} args={len(call_args)}")
|
✨ Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md
This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00
|
|
|
|
|
|
|
|
# Store result if needed
|
|
|
|
|
if dst_vid is not None:
|
2025-09-13 15:37:58 +09:00
|
|
|
vmap[dst_vid] = result
|
2025-09-13 19:49:03 +09:00
|
|
|
# Heuristic: mark known string-producing functions as string handles
|
|
|
|
|
try:
|
|
|
|
|
name_for_tag = actual_name if isinstance(actual_name, str) else str(actual_name)
|
|
|
|
|
if resolver is not None and hasattr(resolver, 'mark_string'):
|
|
|
|
|
if any(key in name_for_tag for key in [
|
|
|
|
|
'esc_json', 'node_json', 'dirname', 'join', 'read_all', 'toJson'
|
|
|
|
|
]):
|
|
|
|
|
resolver.mark_string(dst_vid)
|
2025-09-14 04:51:33 +09:00
|
|
|
# Additionally, create a pointer view via bridge for println pointer-API
|
|
|
|
|
if resolver is not None and hasattr(resolver, 'string_ptrs'):
|
|
|
|
|
i64 = ir.IntType(64)
|
|
|
|
|
i8p = ir.IntType(8).as_pointer()
|
|
|
|
|
if hasattr(result, 'type') and isinstance(result.type, ir.IntType) and result.type.width == 64:
|
|
|
|
|
bridge = None
|
|
|
|
|
for f in module.functions:
|
|
|
|
|
if f.name == 'nyash.string.to_i8p_h':
|
|
|
|
|
bridge = f; break
|
|
|
|
|
if bridge is None:
|
|
|
|
|
bridge = ir.Function(module, ir.FunctionType(i8p, [i64]), name='nyash.string.to_i8p_h')
|
|
|
|
|
pv = builder.call(bridge, [result], name=f"ret_h2p_{dst_vid}")
|
|
|
|
|
resolver.string_ptrs[int(dst_vid)] = pv
|
2025-09-13 19:49:03 +09:00
|
|
|
except Exception:
|
|
|
|
|
pass
|