runner(cli): adopt CliConfig::as_groups across runner modules (dispatch/common/pipe_io/mir/bench). llvm-builder: extract ny_main wrapper to builders.entry; add optional env-gated function_lower delegation; keep default behavior unchanged
This commit is contained in:
68
src/llvm_py/builders/entry.py
Normal file
68
src/llvm_py/builders/entry.py
Normal file
@ -0,0 +1,68 @@
|
||||
from typing import Optional
|
||||
|
||||
def ensure_ny_main(builder) -> None:
|
||||
"""Ensure ny_main wrapper exists by delegating to Main.main/1 or main().
|
||||
Modifies builder.module in place; no return value.
|
||||
"""
|
||||
has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions)
|
||||
fn_main_box = None
|
||||
fn_main_plain = None
|
||||
for f in builder.module.functions:
|
||||
if f.name == 'Main.main/1':
|
||||
fn_main_box = f
|
||||
elif f.name == 'main':
|
||||
fn_main_plain = f
|
||||
target_fn = fn_main_box or fn_main_plain
|
||||
if target_fn is None or has_ny_main:
|
||||
return
|
||||
|
||||
# Hide the target to avoid symbol conflicts
|
||||
try:
|
||||
target_fn.linkage = 'private'
|
||||
except Exception:
|
||||
pass
|
||||
# i32 ny_main() { return (i32) Main.main(args) | main(); }
|
||||
from llvmlite import ir
|
||||
ny_main_ty = ir.FunctionType(builder.i64, [])
|
||||
ny_main = ir.Function(builder.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
if fn_main_box is not None:
|
||||
# Build default args = new ArrayBox() via nyash.env.box.new_i64x
|
||||
i64 = builder.i64
|
||||
i8 = builder.i8
|
||||
i8p = builder.i8p
|
||||
# Declare callee
|
||||
callee = None
|
||||
for f in builder.module.functions:
|
||||
if f.name == 'nyash.env.box.new_i64x':
|
||||
callee = f
|
||||
break
|
||||
if callee is None:
|
||||
callee = ir.Function(builder.module, ir.FunctionType(i64, [i8p, i64, i64, i64, i64, i64]), name='nyash.env.box.new_i64x')
|
||||
# Create "ArrayBox\0" global
|
||||
sbytes = b"ArrayBox\0"
|
||||
arr_ty = ir.ArrayType(i8, len(sbytes))
|
||||
g = ir.GlobalVariable(builder.module, arr_ty, name='.ny_main_arraybox')
|
||||
g.linkage = 'private'
|
||||
g.global_constant = True
|
||||
g.initializer = ir.Constant(arr_ty, bytearray(sbytes))
|
||||
c0 = ir.Constant(builder.i32, 0)
|
||||
ptr = b.gep(g, [c0, c0], inbounds=True)
|
||||
zero = ir.Constant(i64, 0)
|
||||
args_handle = b.call(callee, [ptr, zero, zero, zero, zero, zero], name='ny_main_args')
|
||||
rv = b.call(fn_main_box, [args_handle], name='call_Main_main_1')
|
||||
else:
|
||||
# Plain main() fallback
|
||||
if len(fn_main_plain.args) == 0:
|
||||
rv = b.call(fn_main_plain, [], name='call_user_main')
|
||||
else:
|
||||
rv = ir.Constant(builder.i64, 0)
|
||||
if hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width != 32:
|
||||
rv64 = b.trunc(rv, builder.i64) if rv.type.width > 64 else b.zext(rv, builder.i64)
|
||||
b.ret(rv64)
|
||||
elif hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width == 64:
|
||||
b.ret(rv)
|
||||
else:
|
||||
b.ret(ir.Constant(builder.i64, 0))
|
||||
|
||||
84
src/llvm_py/builders/function_lower.py
Normal file
84
src/llvm_py/builders/function_lower.py
Normal file
@ -0,0 +1,84 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
def lower_function(builder, func_data: Dict[str, Any]):
|
||||
"""Lower a single MIR function to LLVM IR using the given builder context.
|
||||
|
||||
This helper is a thin wrapper that delegates to the NyashLLVMBuilder's
|
||||
existing methods/attributes, enabling gradual file decomposition without
|
||||
changing semantics.
|
||||
"""
|
||||
name = func_data.get("name", "unknown")
|
||||
builder.current_function_name = name
|
||||
|
||||
import re
|
||||
params = func_data.get("params", [])
|
||||
blocks = func_data.get("blocks", [])
|
||||
|
||||
# Determine function signature
|
||||
if name == "ny_main":
|
||||
func_ty = builder.i32.func_type([])
|
||||
else:
|
||||
m = re.search(r"/(\d+)$", name)
|
||||
arity = int(m.group(1)) if m else len(params)
|
||||
param_types = [builder.i64] * arity
|
||||
func_ty = builder.i64.func_type(param_types)
|
||||
|
||||
try:
|
||||
builder.vmap.clear()
|
||||
except Exception:
|
||||
builder.vmap = {}
|
||||
builder.bb_map = {}
|
||||
builder.preds = {}
|
||||
builder.block_end_values = {}
|
||||
builder.def_blocks = {}
|
||||
builder.predeclared_ret_phis = {}
|
||||
|
||||
# Ensure function exists or create one
|
||||
fn = None
|
||||
for f in builder.module.functions:
|
||||
if f.name == name:
|
||||
fn = f
|
||||
break
|
||||
if fn is None:
|
||||
from llvmlite import ir
|
||||
fn = ir.Function(builder.module, func_ty, name=name)
|
||||
|
||||
# Create all basic blocks first
|
||||
from llvmlite import ir
|
||||
block_by_id = {}
|
||||
for b in blocks:
|
||||
bbid = int(b.get("id", 0))
|
||||
bb = fn.append_basic_block(name=f"bb{bbid}")
|
||||
block_by_id[bbid] = bb
|
||||
builder.bb_map[bbid] = bb
|
||||
|
||||
# Predeclare ret PHIs if needed (if-merge prepass)
|
||||
from prepass.if_merge import plan_ret_phi_predeclare
|
||||
plan = plan_ret_phi_predeclare(block_by_id)
|
||||
if plan:
|
||||
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
|
||||
builder.block_phi_incomings = {}
|
||||
for (bbid, pairs) in plan.items():
|
||||
for (ret_vid, preds_list) in pairs.items():
|
||||
builder.block_phi_incomings.setdefault(int(bbid), {}).setdefault(int(ret_vid), [])
|
||||
builder.block_phi_incomings[int(bbid)][int(ret_vid)] = [(int(p), int(ret_vid)) for p in preds_list]
|
||||
|
||||
# Lower instructions per block
|
||||
from llvmlite.ir import IRBuilder
|
||||
from instructions import dispatcher # if exists; else inline lowerers
|
||||
for b in blocks:
|
||||
bbid = int(b.get("id", 0))
|
||||
bb = block_by_id[bbid]
|
||||
builder_bb = IRBuilder(bb)
|
||||
builder.resolver.attach_function_and_block(fn, bb)
|
||||
insts = b.get("insts", [])
|
||||
for inst in insts:
|
||||
op = inst.get("op")
|
||||
# Delegate to existing NyashLLVMBuilder method for now
|
||||
builder.lower_instruction(op, inst, builder_bb)
|
||||
|
||||
# Finalize PHIs after the function is fully lowered
|
||||
from phi_wiring import finalize_phis as _finalize_phis
|
||||
_finalize_phis(builder)
|
||||
|
||||
@ -125,66 +125,35 @@ class NyashLLVMBuilder:
|
||||
for func_data in functions:
|
||||
self.lower_function(func_data)
|
||||
|
||||
# Create ny_main wrapper if necessary
|
||||
has_ny_main = any(f.name == 'ny_main' for f in self.module.functions)
|
||||
# Prefer static box entry: Main.main/1; fallback to plain main (0-arity)
|
||||
fn_main_box = None
|
||||
fn_main_plain = None
|
||||
for f in self.module.functions:
|
||||
if f.name == 'Main.main/1':
|
||||
fn_main_box = f
|
||||
elif f.name == 'main':
|
||||
fn_main_plain = f
|
||||
target_fn = fn_main_box or fn_main_plain
|
||||
if target_fn is not None and not has_ny_main:
|
||||
# Hide the target to avoid symbol conflicts
|
||||
# Create ny_main wrapper if necessary (extracted helper)
|
||||
try:
|
||||
from builders.entry import ensure_ny_main as _ensure_ny_main
|
||||
_ensure_ny_main(self)
|
||||
except Exception:
|
||||
# Fallback to legacy in-place logic if helper import fails
|
||||
try:
|
||||
target_fn.linkage = 'private'
|
||||
has_ny_main = any(f.name == 'ny_main' for f in self.module.functions)
|
||||
fn_main_box = None
|
||||
fn_main_plain = None
|
||||
for f in self.module.functions:
|
||||
if f.name == 'Main.main/1':
|
||||
fn_main_box = f
|
||||
elif f.name == 'main':
|
||||
fn_main_plain = f
|
||||
target_fn = fn_main_box or fn_main_plain
|
||||
if target_fn is not None and not has_ny_main:
|
||||
ny_main_ty = ir.FunctionType(self.i64, [])
|
||||
ny_main = ir.Function(self.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
rv = ir.Constant(self.i64, 0)
|
||||
if fn_main_box is not None:
|
||||
rv = b.call(fn_main_box, [], name='call_Main_main_1')
|
||||
elif fn_main_plain is not None and len(fn_main_plain.args) == 0:
|
||||
rv = b.call(fn_main_plain, [], name='call_user_main')
|
||||
b.ret(rv)
|
||||
except Exception:
|
||||
pass
|
||||
# i32 ny_main() { return (i32) Main.main(args) | main(); }
|
||||
ny_main_ty = ir.FunctionType(self.i64, [])
|
||||
ny_main = ir.Function(self.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
if fn_main_box is not None:
|
||||
# Build default args = new ArrayBox() via nyash.env.box.new_i64x
|
||||
i64 = self.i64
|
||||
i8 = self.i8
|
||||
i8p = self.i8p
|
||||
# Declare callee
|
||||
callee = None
|
||||
for f in self.module.functions:
|
||||
if f.name == 'nyash.env.box.new_i64x':
|
||||
callee = f
|
||||
break
|
||||
if callee is None:
|
||||
callee = ir.Function(self.module, ir.FunctionType(i64, [i8p, i64, i64, i64, i64, i64]), name='nyash.env.box.new_i64x')
|
||||
# Create "ArrayBox\0" global
|
||||
sbytes = b"ArrayBox\0"
|
||||
arr_ty = ir.ArrayType(i8, len(sbytes))
|
||||
g = ir.GlobalVariable(self.module, arr_ty, name='.ny_main_arraybox')
|
||||
g.linkage = 'private'
|
||||
g.global_constant = True
|
||||
g.initializer = ir.Constant(arr_ty, bytearray(sbytes))
|
||||
c0 = ir.Constant(self.i32, 0)
|
||||
ptr = b.gep(g, [c0, c0], inbounds=True)
|
||||
zero = ir.Constant(i64, 0)
|
||||
args_handle = b.call(callee, [ptr, zero, zero, zero, zero, zero], name='ny_main_args')
|
||||
rv = b.call(fn_main_box, [args_handle], name='call_Main_main_1')
|
||||
else:
|
||||
# Plain main() fallback
|
||||
if len(fn_main_plain.args) == 0:
|
||||
rv = b.call(fn_main_plain, [], name='call_user_main')
|
||||
else:
|
||||
rv = ir.Constant(self.i64, 0)
|
||||
if hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width != 32:
|
||||
rv64 = b.trunc(rv, self.i64) if rv.type.width > 64 else b.zext(rv, self.i64)
|
||||
b.ret(rv64)
|
||||
elif hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width == 64:
|
||||
b.ret(rv)
|
||||
else:
|
||||
b.ret(ir.Constant(self.i64, 0))
|
||||
|
||||
ir_text = str(self.module)
|
||||
# Optional IR dump to file for debugging
|
||||
@ -215,6 +184,16 @@ class NyashLLVMBuilder:
|
||||
|
||||
def lower_function(self, func_data: Dict[str, Any]):
|
||||
"""Lower a single MIR function to LLVM IR"""
|
||||
# Optional: delegate to external helper when gated (incremental split)
|
||||
try:
|
||||
if os.environ.get('NYASH_LLVM_USE_HELPER_LOWER') == '1':
|
||||
try:
|
||||
from builders.function_lower import lower_function as _lower
|
||||
return _lower(self, func_data)
|
||||
except Exception as _e:
|
||||
trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}")
|
||||
except Exception:
|
||||
pass
|
||||
name = func_data.get("name", "unknown")
|
||||
self.current_function_name = name
|
||||
import re
|
||||
|
||||
Reference in New Issue
Block a user