Files
ViperC/App/lib/core/Gen/CallGenLeafKit.vp
T
2026-08-27 09:48:20 +08:00

1307 lines
64 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.CallGenCBuiltin as CallGenCBuiltin
# ============================================================
# CallGenLeafKit - CallGenMixin 拆分 3/9:EH/isinstance/闭包/print/函数表
#
# 按功能域从 CallGenMixin.vp 拆出(500~1500 行/文件架构重构):
# EH 防护 + isinstance + 闭包/函数指针 + printf/print + 全局函数表/默认参数表。
# 继承 CallGenCBuiltin。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# CallGenLeafKit - EH/isinstance/闭包/print/函数表
# ============================================================
@t.NoVTable
class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""EH 防护 + isinstance + 闭包 + print + 全局函数表(继承 CBuiltin)"""
# ============================================================
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
#
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
# ============================================================
def _func_has_eh_params(self, func: llvmlite.Function | t.CPtr) -> int:
"""返回 1=函数带 EH 输出参数 / 0=不带"""
if func is None:
return 0
cur_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
while cur_p is not None:
pn: str = llvmlite.param_get_name(cur_p)
if pn is not None and string.strcmp(pn, "%__eh_msg_out__") == 0:
return 1
cur_p = llvmlite.param_get_next(cur_p)
return 0
# ============================================================
# _emit_eh_guarded_call - 调用侧 try-except EH 检测(对齐 TPC
# HandlesExprCall._append_eh_msg_out_arg + _check_eh_return)
#
# 被调函数带 EH 输出参数时:
# 1. 确定 msg/code 输出指针:
# - 当前在 try(trans._eh_dispatch_bb 非空):复用 try 的
# eh_message/exception_code alloca——被调函数直接写入,检查
# 路径读同一 alloca 即得异常,无需搬运
# - 当前函数有 EH 输出参数:复用本函数 out 参数(异常直接写进
# 本函数输出,检查命中后仅返回,由调用方接管传播)
# - 两者皆无:局部 null alloca(异常被吞)
# 2. 调用前清零输出指针(复用 alloca 必须复位,防上次残留误判)
# 3. 追加 msg/code 两个实参(count+2)
# 4. 调用后检查 msg != null:命中 → err 块(try 内分支 DispatchBB;
# 否则仅返回默认值);未命中 → ok 块继续
# ============================================================
# ============================================================
def _emit_eh_guarded_call(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
func: llvmlite.Function | t.CPtr,
call_name: str,
head: llvmlite.Value | t.CPtr,
count: int,
ret_ty: llvmlite.LLVMType | t.CPtr,
is_variadic: int) -> llvmlite.Value | t.CPtr:
"""带 try-except EH 检测的函数调用,返回 call 结果(检查后位于 ok 块)"""
if trans is None or self._func_has_eh_params(func) == 0:
return llvmlite.build_call(builder, call_name, head, count, ret_ty, is_variadic)
# ---- 1. 确定输出指针 ----
i8_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_g)
i32_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if i8_ty_g is None or i8_ptr_ty_g is None or i32_ty_g is None:
return llvmlite.build_call(builder, call_name, head, count, ret_ty, is_variadic)
msg_ptr: llvmlite.Value | t.CPtr = None
code_ptr: llvmlite.Value | t.CPtr = None
if trans._eh_msg_ptr is not None and trans._eh_code_ptr is not None:
msg_ptr = trans._eh_msg_ptr
code_ptr = trans._eh_code_ptr
elif trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \
and trans._eh_code_out_param is not None:
msg_ptr = trans._eh_msg_out_param
code_ptr = trans._eh_code_out_param
if msg_ptr is None:
msg_ptr = llvmlite.build_alloca(builder, i8_ptr_ty_g)
code_ptr = llvmlite.build_alloca(builder, i32_ty_g)
# ---- 2. 调用前清零 ----
null_msg_g: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_g, "null")
zero_code_g: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
if null_msg_g is not None:
llvmlite.build_store(builder, null_msg_g, msg_ptr)
if zero_code_g is not None:
llvmlite.build_store(builder, zero_code_g, code_ptr)
# ---- 3. 追加实参(链表末尾 + count+2)----
msg_ptr.Next = code_ptr
code_ptr.Next = None
if head is None:
head = msg_ptr
else:
tail_g: llvmlite.Value | t.CPtr = head
while tail_g.Next is not None:
tail_g = tail_g.Next
tail_g.Next = msg_ptr
count = count + 2
# ---- 4. 调用 + 检查 ----
result: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, call_name, head, count, ret_ty, is_variadic)
cur_func_g: llvmlite.Function | t.CPtr = trans._cur_func
if cur_func_g is None or null_msg_g is None:
return result
msg_val_g: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i8_ptr_ty_g, msg_ptr)
if msg_val_g is None:
return result
is_err_g: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_NE, msg_val_g, null_msg_g)
if is_err_g is None:
return result
lc_g: int = trans._label_counter
trans._label_counter = lc_g + 1
bb_buf_g: str = pool.alloc(64)
if bb_buf_g is None:
return result
viperlib.snprintf(bb_buf_g, 64, "eh.err.%d", lc_g)
err_bb_g: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, cur_func_g, bb_buf_g)
viperlib.snprintf(bb_buf_g, 64, "eh.ok.%d", lc_g)
ok_bb_g: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, cur_func_g, bb_buf_g)
if err_bb_g is None or ok_bb_g is None:
return result
llvmlite.build_cond_br(builder, is_err_g, err_bb_g, ok_bb_g)
# err 块
llvmlite.position_at_end(builder, err_bb_g)
if trans._eh_dispatch_bb is not None:
# try 内:异常已写入 try 的 alloca(code_ptr 即 try 的 alloca),
# 分支 DispatchBB 交给 except 链
llvmlite.build_br(builder, trans._eh_dispatch_bb)
else:
# 非 try:异常已写入本函数 out 参数(复用)或局部 alloca(吞掉),
# 仅返回默认值,由调用方检测本函数 out 参数
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
ret_ty_g2: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(
cur_func_g)
if ret_ty_g2 is not None:
match ret_ty_g2:
case llvmlite.LLVMType.Void():
llvmlite.build_ret_void(builder)
case llvmlite.LLVMType.Ptr(_pg):
nv_g: llvmlite.Value | t.CPtr = llvmlite.ConstNull(
pool, ret_ty_g2, "null")
if nv_g is not None:
llvmlite.build_ret(builder, nv_g)
case llvmlite.LLVMType.Float(_fg):
zf_g: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(
pool, ret_ty_g2, 0.0)
if zf_g is not None:
llvmlite.build_ret(builder, zf_g)
case _:
zv_g: llvmlite.Value | t.CPtr = llvmlite.ConstZero(
pool, ret_ty_g2)
if zv_g is not None:
llvmlite.build_ret(builder, zv_g)
# ok 块:继续
llvmlite.position_at_end(builder, ok_bb_g)
return result
# ============================================================
# isinstance - 运行时 vtable 子类集合比较(2026-08-10)
#
# CVTable 对象头部 vtable 指针唯一标识对象实际类。isinstance(obj, Cls)
# 语义 = Cls 是实际类的祖先(实际类 == Cls 或继承自 Cls)。运行时只能
# 拿到实际类 vtable 地址,无法向上推导祖先链,因此在编译期沿
# StructEntry.ParentNames 反向 BFS 枚举 Cls 的全部已知子类(含自身),
# 运行时比较 obj->vtable ∈ {Cls 子类 vtable 地址集合}。
# NoVTable / 未知类回退编译时静态判断(obj 静态类型 MRO 判断)。
# ============================================================
def _translate_isinstance(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
obj_node: ast.AST | t.CPtr,
cls_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""isinstance(obj, Cls):运行时 vtable 子类集合比较,返回 i1"""
if obj_node is None or cls_node is None:
# 参数缺失即调用错误:fail-fast,绝不返回 True 掩盖
self.Gen.TypeGenErr.fatal_error(cls_node, "isinstance() 参数类型推断失败", "TypeError")
obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, obj_node, None, 0, trans)
if obj_val is None or obj_val.Ty is None:
# obj 类型推断失败:fail-fast,绝不返回 True 掩盖
self.Gen.TypeGenErr.fatal_error(cls_node, "isinstance() 参数类型推断失败", "TypeError")
# 对象指针规整:二级指针(变量 alloca / 联合 Class|t.CPtr)先 load
obj_ptr: llvmlite.Value | t.CPtr = obj_val
match obj_val.Ty:
case llvmlite.LLVMType.Ptr(inner_ty):
match inner_ty:
case llvmlite.LLVMType.Ptr(_):
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, inner_ty, obj_val)
if loaded is not None:
obj_ptr = loaded
case _:
pass
obj_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(obj_ptr.Ty)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
# 对象 vtable 指针惰性读取(首个 CVTable 类比较时执行一次)
vtable_ptr: llvmlite.Value | t.CPtr = None
vtable_ready: int = 0
res: llvmlite.Value | t.CPtr = None
cls_names: t.CSizeT | t.CPtr = self._extract_isinstance_class_names(pool, cls_node)
if cls_names is None:
# 类名提取失败:fail-fast,绝不返回 True 掩盖
self.Gen.TypeGenErr.fatal_error(cls_node, "isinstance() 参数类型推断失败", "TypeError")
ci: int = 0
while cls_names[ci] != 0:
cn: str = (str | t.CPtr)(t.CVoid(cls_names[ci], t.CPtr))
if cn is not None:
if self.Gen.StructGen.is_novtable_by_name(cn) != 0 \
or self.Gen.StructGen.has_vtable_by_name(cn) == 0:
# NoVTable / 未知类 → 编译时静态判断(obj 静态类型 MRO)
st: int = self._isinstance_compile_time(pool, obj_ptr, cn)
c1: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, 1, 1 if st > 0 else 0)
if res is None:
res = c1
else:
res = llvmlite.build_or(builder, res, c1)
elif obj_is_ptr != 0:
# CVTable → 运行时 vtable 比较(读对象头部 vtable 指针)
if vtable_ready == 0:
vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, obj_ptr, i8_ptr_ptr_ty)
if vtable_slot is not None:
vtable_ptr = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
vtable_ready = 1
if vtable_ptr is not None:
subs: t.CSizeT | t.CPtr = self._collect_subclass_vtable_classes(pool, cn)
if subs is not None:
k: int = 0
while subs[k] != 0:
scn: str = (str | t.CPtr)(t.CVoid(subs[k], t.CPtr))
if scn is not None:
sub_i8: llvmlite.Value | t.CPtr = self._make_vtable_ref_i8(
pool, builder, mod, scn, trans)
if sub_i8 is not None:
cmp: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_EQ, vtable_ptr, sub_i8)
if cmp is not None:
if res is None:
res = cmp
else:
res = llvmlite.build_or(builder, res, cmp)
k += 1
ci += 1
if res is None:
# 未产生任何比较结果(obj 非指针且类全部为 CVTable):推断失败,fail-fast
self.Gen.TypeGenErr.fatal_error(cls_node, "isinstance() 参数类型推断失败", "TypeError")
return res
def _extract_isinstance_class_names(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr) -> t.CSizeT | t.CPtr:
"""从 isinstance 第二参数提取类名数组(Name→id / Attribute→attr / Tuple→遍历)"""
arr: t.CSizeT | t.CPtr = pool.alloc(8 * 16)
if arr is None:
return None
# 全数组清零:pool 分配内存非零时,未初始化槽位会被 while cls_names[ci]!=0
# 误当类名指针解引用 → 段错误
string.memset(arr, 0, 8 * 16)
arr[0] = 0
if node is None:
return arr
count: int = 0
k: int = node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
arr[0] = t.CSizeT(nm.id)
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at.attr is not None:
arr[0] = t.CSizeT(at.attr)
elif k == ast.ASTKind.Tuple:
tp: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
elts: list[ast.AST | t.CPtr] | t.CPtr = tp.elts
if elts is not None:
n: t.CSizeT = len(elts)
for i in range(n):
if count >= 15:
break
elt: ast.AST | t.CPtr = elts.get(i)
if elt is None:
continue
ek: int = elt.kind()
if ek == ast.ASTKind.Name:
enm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(elt)
if enm.id is not None:
arr[count] = t.CSizeT(enm.id)
count += 1
elif ek == ast.ASTKind.Attribute:
eat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(elt)
if eat.attr is not None:
arr[count] = t.CSizeT(eat.attr)
count += 1
arr[count] = 0
return arr
def _collect_subclass_vtable_classes(self, pool: memhub.MemBuddy | t.CPtr,
class_name: str) -> t.CSizeT | t.CPtr:
"""反向 BFS 收集目标类及全部子类(含自身,仅 HasVTable==1),返回 0 结尾数组"""
if class_name is None:
return None
names: t.CSizeT | t.CPtr = pool.alloc(8 * (ISINSTANCE_CLASS_MAX + 1))
queue: t.CSizeT | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
visited: t.CSizeT | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
if names is None or queue is None or visited is None:
return None
string.memset(names, 0, 8 * (ISINSTANCE_CLASS_MAX + 1))
string.memset(queue, 0, 8 * ISINSTANCE_CLASS_MAX)
string.memset(visited, 0, 8 * ISINSTANCE_CLASS_MAX)
qcount: int = 1
outcount: int = 0
vcount: int = 0
queue[0] = t.CSizeT(class_name) # 存疑
while qcount > 0:
qcount -= 1
cur_addr: t.CSizeT = queue[qcount]
cur: str = (str | t.CPtr)(t.CVoid(cur_addr, t.CPtr))
if cur is None:
continue
dup: int = 0
for vi in range(vcount):
va: t.CSizeT = visited[vi]
if va != 0:
vn: str = (str | t.CPtr)(t.CVoid(va, t.CPtr))
if vn is not None and string.strcmp(vn, cur) == 0:
dup = 1
break
if dup != 0:
continue
if vcount < ISINSTANCE_CLASS_MAX:
visited[vcount] = t.CSizeT(cur)
vcount += 1
centry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(cur)
if centry is not None and centry.HasVTable != 0:
if outcount < ISINSTANCE_CLASS_MAX:
names[outcount] = t.CSizeT(cur)
outcount += 1
# 子类入队:遍历全部已注册 struct,ParentNames 含 cur 者
scnt: int = self.Gen.StructGen.get_struct_count()
for i in range(scnt):
e2: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen._get_struct_entry(i)
if e2 is None or e2.Name is None:
continue
if e2.ParentCount > 0 and e2.ParentNames is not None:
parr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(e2.ParentNames, t.CPtr))
for j in range(e2.ParentCount):
pa: t.CSizeT = parr[j]
if pa != 0:
pn: str = (str | t.CPtr)(t.CVoid(pa, t.CPtr))
if pn is not None and string.strcmp(pn, cur) == 0:
if qcount < ISINSTANCE_CLASS_MAX:
queue[qcount] = t.CSizeT(e2.Name)
qcount += 1
break
names[outcount] = 0
return names
def _make_vtable_ref_i8(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""构造类 vtable 地址引用(i8*):本模块已定义直接引用,跨模块声明 external"""
if class_name is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
buf: str = pool.alloc(256)
if buf is None:
return None
viperlib.snprintf(buf, 256, "%s.vtable", class_name)
vn: str = (str | t.CPtr)(buf)
sha1: str = self.Gen.StructGen.get_struct_sha1(class_name)
if sha1 is None:
if trans is None:
return None
sha1 = trans.ModuleSha1
if sha1 is None:
return None
mangled: str = self.Gen.FuncGenBase._mangle_name_with_sha1(sha1, vn)
ref_name: str = pool.alloc(256)
if ref_name is None:
return None
# vtable 名含 sha1 点号前缀,一律 @"..." 引号形式
viperlib.snprintf(ref_name, 256, "@\"%s\"", mangled)
gv: llvmlite.GlobalVariable | t.CPtr = self.Gen.ExprGenAttr.find_global_in_module(mod, mangled)
ref_ty: llvmlite.LLVMType | t.CPtr = None
if gv is not None:
# 本模块已定义 vtable(constant {i8* x N})→ 引用类型 Ptr(struct)
ref_ty = llvmlite.Ptr(pool, gv.Ty)
else:
# 跨模块:声明 @mangled = external global i8*,引用类型 i8**
gv2: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
pool, mangled, i8_ptr_ty)
if gv2 is None:
return None
llvmlite.module_add_global(mod, gv2)
ref_ty = llvmlite.Ptr(pool, i8_ptr_ty)
ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, ref_ty, ref_name)
if ref is None:
return None
return llvmlite.build_bitcast(builder, ref, i8_ptr_ty)
def _isinstance_compile_time(self, pool: memhub.MemBuddy | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr,
class_name: str) -> int:
"""编译时静态判断:obj 静态类型 == class_name 或 class_name 在其 MRO 中"""
if obj_ptr is None or obj_ptr.Ty is None or class_name is None:
return 0
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(obj_struct_ty):
sname: str = self.Gen.StructGen.get_class_name_by_type(pool, obj_struct_ty)
if sname is not None:
if string.strcmp(sname, class_name) == 0:
return 1
mro: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, sname)
if mro is not None:
k: int = 0
while mro[k] != 0:
mn: str = (str | t.CPtr)(t.CVoid(mro[k], t.CPtr))
if mn is not None and string.strcmp(mn, class_name) == 0:
return 1
k += 1
case _:
pass
return 0
# ============================================================
# P4 簇 5+6 迁移方法(函数体来自 HandlesExprCall.vp:
# 闭包调用/函数指针/printf/print/stdarg 糖/变长参数提升/函数表/全局注册表)
# 本模块函数调用改 self.,外部模块调用保留模块前缀,全局变量改 self 字段
# ============================================================
def translate_closure_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
closure_alloca: llvmlite.Value | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""通过闭包指针间接调用(支持带参 lambda:i32(i8*, args...))"""
if builder is None or closure_alloca is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
# 1. 加载闭包指针 (i8*)
closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, closure_alloca)
if closure_ptr is None:
return None
# 2. 加载 fn_ptr (offset 0): bitcast to i8**, load i8*
fn_ptr_addr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty))
if fn_ptr_addr is None:
return None
fn_raw: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, fn_ptr_addr)
if fn_raw is None:
return None
# 3. 加载 env_ptr (offset 8): GEP i8, closure, 8; load i8*
eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8)
env_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, closure_ptr, eight_val)
if env_addr is None:
return None
env_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, env_addr)
if env_ptr is None:
return None
# 4. 构建参数:env 头 + 实参尾(带参 lambda 时翻译实参)
total_args: int = 1
arg_head: llvmlite.Value | t.CPtr = env_ptr
arg_tail: llvmlite.Value | t.CPtr = env_ptr
param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty)
pn_tail: llvmlite.ParamNode | t.CPtr = param_node
if can > 0 and cargs is not None and trans is not None:
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None or arg_val.Ty is None:
continue
arg_val.Next = None
if arg_tail is not None:
arg_tail.Next = arg_val
arg_tail = arg_val
pn_new: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, arg_val.Ty)
if pn_new is not None:
pn_tail.Next = pn_new
pn_tail = pn_new
total_args += 1
if total_args == 1:
env_ptr.Next = None
# 5. bitcast fn_raw 到函数指针类型 i32(i8*, [args...])*
func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, param_node, total_args)
func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, func_ty)
fn_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, fn_raw, func_ptr_ty)
if fn_typed is None:
return None
# 6. 间接调用: call i32 %fn_typed(env, args...)
return llvmlite.build_call_indirect(builder, fn_typed, arg_head, total_args, i32_ty)
def _is_func_ptr_alloca(self, alloca: llvmlite.Value | t.CPtr) -> int:
"""判断 alloca 是否为函数指针变量(类型 Ptr(Ptr(i8))),1=是 / 0=否"""
if alloca is None or alloca.Ty is None:
return 0
if self.Gen.ExprGenBase.is_ptr_type(alloca.Ty) == 0:
return 0
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None or self.Gen.ExprGenBase.is_ptr_type(pointee) == 0:
return 0
inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee
if inner is None or self.Gen.ExprGenBase.is_ptr_type(inner) != 0:
return 0
return 1
def _ensure_closure_names(self) -> int:
"""确保闭包名表已分配,0 失败"""
if self.ClosureNames is not None:
return 1
self.ClosureNames = CompilerFactory.get_perm_pool().alloc(64 * MAX_CLOSURE_NAMES)
if self.ClosureNames is None:
return 0
string.memset(self.ClosureNames, 0, 64 * MAX_CLOSURE_NAMES)
return 1
def register_closure_name(self, name: str) -> int:
"""注册嵌套函数(闭包变量)名,0 成功"""
if name is None:
return 1
if self._ensure_closure_names() == 0:
return 1
if self.ClosureCount >= MAX_CLOSURE_NAMES:
return 1
idx: t.CSizeT = t.CSizeT(self.ClosureCount) * 64
string.strncpy(self.ClosureNames + idx, name, 63)
self.ClosureCount += 1
return 0
def is_closure_name(self, name: str) -> int:
"""检查 name 是否已注册为闭包变量名,1=是 / 0=否"""
if name is None or self.ClosureNames is None:
return 0
i: t.CSizeT
for i in range(self.ClosureCount):
if string.strcmp(self.ClosureNames + i * 64, name) == 0:
return 1
return 0
def _ensure_funcptr_sigs(self) -> int:
"""确保函数指针签名表已分配(stdlib.malloc,懒初始化)"""
if self.FuncptrNames is not None:
return 1
self.FuncptrNames = CompilerFactory.get_perm_pool().alloc(64 * MAX_FUNC_PTR_SIGS)
self.FuncptrRets = CompilerFactory.get_perm_pool().alloc(8 * MAX_FUNC_PTR_SIGS)
if self.FuncptrNames is None or self.FuncptrRets is None:
return 0
string.memset(self.FuncptrNames, 0, 64 * MAX_FUNC_PTR_SIGS)
return 1
def register_func_ptr_sig(self, name: str,
ret_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""注册函数指针变量的返回类型签名,0 成功"""
if name is None or ret_ty is None:
return 1
if self._ensure_funcptr_sigs() == 0:
return 1
if self.FuncptrCount >= MAX_FUNC_PTR_SIGS:
return 1
idx: t.CSizeT = t.CSizeT(self.FuncptrCount) * 64
# 槽位定长 64 字节,与 register_closure_name 一致用 strncpy 钳制防越界
string.strncpy(self.FuncptrNames + idx, name, 63)
# ret_ty 是调用方 pool 分配的裸 LLVMType,1a 每文件 reset 后悬空;
# 深拷贝到 perm_pool(lookup_func_ptr_ret 跨文件读取)
ret_copy_fp: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
CompilerFactory.get_perm_pool(), ret_ty)
if ret_copy_fp is not None:
self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_copy_fp)
else:
self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_ty)
self.FuncptrCount += 1
return 0
def lookup_func_ptr_ret(self, name: str) -> llvmlite.LLVMType | t.CPtr:
"""查函数指针变量的返回类型,None=未注册(调用时默认 i32)"""
if name is None or self.FuncptrNames is None:
return None
for i in range(self.FuncptrCount):
idx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(self.FuncptrNames + idx, name) == 0:
return (llvmlite.LLVMType | t.CPtr)(
t.CVoid(self.FuncptrRets[i], t.CPtr))
return None
def _translate_func_ptr_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fp_alloca: llvmlite.Value | t.CPtr,
func_name: str,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""函数指针变量间接调用,返回结果 Value"""
if builder is None or fp_alloca is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
# 1. load 函数地址 (i8*)
fn_i8: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, fp_alloca)
if fn_i8 is None:
return None
# 2. 翻译实参并链接为 Value 链表
head: llvmlite.Value | t.CPtr = None
tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
ai: t.CSizeT
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is not None:
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is not None:
arg_val.Next = None
if head is None:
head = arg_val
else:
tail.Next = arg_val
tail = arg_val
arg_count += 1
# 3. 按实参类型 + 注册的返回类型构造签名(未注册即 t.Callable 注解缺失,fail-fast)
ret_ty: llvmlite.LLVMType | t.CPtr = self.lookup_func_ptr_ret(func_name)
if ret_ty is None:
self.Gen.TypeGenErr.fatal_error(
None,
f"函数指针变量 '{func_name}' 的返回类型未注册(t.Callable 注解缺失)",
"TypeError")
ret_ty = i32_ty
fp_first: llvmlite.ParamNode | t.CPtr = None
fp_prev: llvmlite.ParamNode | t.CPtr = None
cur_v: llvmlite.Value | t.CPtr = head
for pi in range(arg_count):
if cur_v is None:
break
cur_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(cur_v)
pn2: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, cur_ty)
if pn2 is not None:
if fp_first is None:
fp_first = pn2
if fp_prev is not None:
llvmlite.paramnode_set_next(fp_prev, pn2)
fp_prev = pn2
cur_v = llvmlite.value_get_next(cur_v)
func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, ret_ty, fp_first, arg_count)
if func_ty is None:
return None
func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, func_ty)
# 4. bitcast i8* → 函数指针类型,间接调用
fn_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, fn_i8, func_ptr_ty)
if fn_typed is None:
return None
return llvmlite.build_call_indirect(builder, fn_typed, head, arg_count, ret_ty)
def translate_printf_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> None:
"""翻译 printf 调用(特殊处理格式字符串)"""
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None:
return
can: t.CSizeT = len(cargs)
if can == 0:
return
arg0: ast.AST | t.CPtr = cargs.get(0)
if arg0 is None:
return
fmt_str: str = None
arg0_kind: int = arg0.kind()
if arg0_kind == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(arg0)
if cn.const_kind == ast.CONST_STR:
fmt_str = cn.str_val
if fmt_str is None:
return
fmt_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fmt_str, trans)
if fmt_ptr is None:
return
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
# 翻译额外参数并链接
prev: llvmlite.Value | t.CPtr = fmt_ptr
for ai in range(1, can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is not None:
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is not None:
# C ABI integer promotion: i8/i16 → i32(printf 变参函数要求)
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
if arg_bits != 0 and arg_bits < 32:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, i32_ty, arg)
# C ABI float promotion: float → double(printf 变参函数要求)
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(arg_val.Ty)
if arg_fbits != 0 and arg_fbits < 64:
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, double_ty, arg)
arg_val.Next = None
prev.Next = arg_val
prev = arg_val
prev.Next = None
# is_variadic=1: 生成带显式函数类型签名的 call(如 call i32 (i8*, ...) @printf),
# 让 llc 识别为 variadic 调用,生成正确的 Win64 ABI 代码(浮点参数同时传 XMM 和整数寄存器)
llvmlite.build_call(builder, "printf", fmt_ptr, can, i32_ty, 1)
def translate_print_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> None:
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None:
return
FMT_BUF_SIZE: t.CSizeT = 256
fmt_buf: bytes = stdlib.malloc(FMT_BUF_SIZE)
if fmt_buf is None:
return
fmt_buf[0] = '\0'
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
dbl_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
can: t.CSizeT = len(cargs)
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 1 # 格式串本身是第 1 个 printf 参数
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None or arg_val.Ty is None:
# fail-fast:print 参数翻译失败必须终止编译,绝不在格式串写
# 误导为数据问题而非编译器 bug。
fb_pp: str = VLogger.fmt_buf()
if fb_pp is not None:
viperlib.snprintf(fb_pp, 1024, "print 参数翻译失败(表达式返回 None): [arg=%d]", ai)
VLogger.error(fb_pp, "PRINT")
self.Gen.TypeGenErr.fatal_error(arg, "print 参数翻译失败(表达式返回 None)", "TypeError")
return
# 数组名(如 print(l),l: t.CArray[t.CChar, N]):
# translate_value 会 load 出数组值([N x T]),print 无法格式化;
# 按数组退化语义取数组 alloca 地址——字符数组 → %s(打印字符串内容),
# 其他元素类型 → 0x%llx(打印数组地址)。
array_ptr: llvmlite.Value | t.CPtr = None
array_elem_bits: int = 0
if arg.kind() == ast.ASTKind.Name and trans is not None:
anm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg)
if anm.id is not None:
arr_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, anm.id)
if arr_alloca is not None and arr_alloca.Ty is not None \
and self.Gen.ExprGenBase.is_ptr_type(arr_alloca.Ty) != 0:
arr_pointee: llvmlite.LLVMType | t.CPtr = arr_alloca.Ty.Pointee
if arr_pointee is not None:
match arr_pointee:
case llvmlite.LLVMType.Array(arr_et, arr_n):
array_ptr = arr_alloca
array_elem_bits = self.Gen.ExprGenBase.get_llvm_type_bits(arr_et)
case _:
pass
token: str = None
chain_arg: int = 1 # 1=该 token 消耗一个参数,0=纯文本
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(arg_val.Ty)
if array_ptr is not None:
# 数组退化:字符数组 → %s(bitcast [N x i8]* 到 i8*);其他 → 0x%llx
if array_elem_bits == 8:
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
token = "%s"
arg_val = llvmlite.build_bitcast(builder, array_ptr, i8_ptr_ty)
else:
token = "0x%llx"
arg_val = llvmlite.build_ptrtoint(builder, array_ptr, i64_ty)
elif self.Gen.ExprGenBase.is_ptr_type(arg_val.Ty) != 0:
# i8*(str/bytes)→ %s;其他指针 → 0x%llx
pointee: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee
if pointee is not None and self.Gen.ExprGenBase.get_llvm_type_bits(pointee) == 8:
token = "%s"
else:
token = "0x%llx"
arg_val = llvmlite.build_ptrtoint(builder, arg_val, i64_ty)
elif arg_bits != 0:
if arg_bits == 1:
token = "%d"
arg_val = llvmlite.build_zext(builder, arg_val, i32_ty)
elif arg_bits == 8:
token = "%d"
arg_val = llvmlite.build_sext(builder, arg_val, i32_ty)
elif arg_bits == 16:
token = "%d"
arg_val = llvmlite.build_sext(builder, arg_val, i32_ty)
elif arg_bits == 32:
token = "%d"
else:
token = "%lld"
elif arg_fbits != 0:
token = "%f"
if arg_fbits < 64:
arg_val = llvmlite.build_fpext(builder, arg_val, dbl_ty)
else:
# 结构体值/数组/函数等 → "(unknown)",不传参
token = "(unknown)"
chain_arg = 0
# 追加格式 token
if string.strlen(fmt_buf) < FMT_BUF_SIZE - 16:
viperlib.snprintf(fmt_buf + string.strlen(fmt_buf), 16, "%s", token)
# 链接参数
if chain_arg != 0:
arg_val.Next = None
if arg_tail is None:
arg_head = arg_val
else:
arg_tail.Next = arg_val
arg_tail = arg_val
arg_count += 1
# 追加默认换行(Python print 默认 end="\n")
if string.strlen(fmt_buf) < FMT_BUF_SIZE - 2:
viperlib.snprintf(fmt_buf + string.strlen(fmt_buf), 2, "\n")
fmt_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fmt_buf, trans)
stdlib.free(fmt_buf)
if fmt_ptr is None:
return
fmt_ptr.Next = arg_head
# is_variadic=1: 带显式函数类型签名(C ABI 变参调用)
llvmlite.build_call(builder, "printf", fmt_ptr, arg_count, i32_ty, 1)
def init_func_table(self, pool: memhub.MemBuddy | t.CPtr,
max_count: int) -> GenTypes.FuncEntry | t.CPtr:
"""分配并清零函数表数组"""
size: t.CSizeT = max_count * GenTypes.FuncEntry.__sizeof__()
funcs_ptr: GenTypes.FuncEntry | t.CPtr = pool.alloc(size)
if funcs_ptr is not None:
string.memset(funcs_ptr, 0, size)
return funcs_ptr
def find_func_in_table(self, funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str) -> llvmlite.Function | t.CPtr:
"""在函数表中按名称查找,返回 Function 指针或 None"""
if name is None or funcs is None:
return None
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
for i in range(func_count):
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry.Func
return None
def add_func_to_table(self, funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str,
func: llvmlite.Function | t.CPtr,
max_count: int,
defaults: t.CVoid | t.CPtr = None,
default_count: int = 0,
param_count: int = 0,
pool: memhub.MemBuddy | t.CPtr = None) -> int:
"""添加函数到函数表,返回 0 成功
Args:
defaults: ast.Arguments.defaults 列表节点指针,None=无默认值
default_count: 默认值数量(len(defaults))
param_count: 形参数量(不含 self,len(args.args))
pool: 内存池(用于生成 TypeStr;None 时 TypeStr 为空,vtable 回退读 Func 对象)
"""
if name is None or func is None or funcs is None:
return 1
if func_count >= max_count:
return 1
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + func_count * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
entry.Name = name
entry.Func = func
entry.Defaults = defaults
entry.DefaultCount = default_count
entry.ParamCount = param_count
# 同时注册到全局函数表(供跨模块 vtable 初始化查找)
self._register_global_func(name, func, pool)
# 同时注册到全局默认参数表(供跨模块调用时填充默认值)
if default_count > 0:
self._register_global_defaults(name, defaults, default_count, param_count)
return 0
def _init_global_func_table(self) -> int:
"""初始化全局函数表(用 stdlib.malloc 分配,生命周期=整个程序)"""
if self.GlobalFuncs is not None:
return 1
size: t.CSizeT = GLOBAL_FUNC_MAX * GenTypes.FuncEntry.__sizeof__()
self.GlobalFuncs = CompilerFactory.get_perm_pool().alloc(size)
if self.GlobalFuncs is None:
return 0
string.memset(self.GlobalFuncs, 0, size)
self.GlobalFuncCount = 0
return 1
def _build_func_type_str(self, pool: memhub.MemBuddy | t.CPtr,
func: llvmlite.Function | t.CPtr) -> str:
"""生成函数类型文本(stdlib 分配)"""
if func is None:
return None
# 类型文本可能较长(SHA1 限定命名结构体 + 多参数),用 1024 缓冲
ty_buf: str = pool.alloc(1024)
if ty_buf is None:
return None
ty_buf[0] = '\0'
# 返回类型
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(func)
ret_part: str = pool.alloc(512)
if ret_part is not None and ret_ty is not None:
ret_part[0] = '\0'
llvmlite.TypePrint(ret_part, 512, ret_ty, pool)
self._asm_append_cstr(ty_buf, 1024, ret_part)
self._asm_append_cstr(ty_buf, 1024, " (")
# 参数类型(全部经 _asm_append_cstr 有界追加,防止 strcat 无界写穿 1024 缓冲)
cur: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
first: int = 1
param_part: str = pool.alloc(512)
while cur is not None:
if first == 0:
self._asm_append_cstr(ty_buf, 1024, ", ")
if param_part is not None:
param_part[0] = '\0'
param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur)
if param_ty is not None:
llvmlite.TypePrint(param_part, 512, param_ty, pool)
self._asm_append_cstr(ty_buf, 1024, param_part)
cur = llvmlite.param_get_next(cur)
first = 0
self._asm_append_cstr(ty_buf, 1024, ")")
# 深拷贝到 stdlib 区(TypeStr 跨文件永久存活)
ts_len: t.CSizeT = string.strlen(ty_buf)
ts_copy: str = CompilerFactory.get_perm_pool().alloc(ts_len + 1)
if ts_copy is None:
return None
string.strcpy(ts_copy, ty_buf)
return ts_copy
def _register_global_func(self, name: str, func: llvmlite.Function | t.CPtr,
pool: memhub.MemBuddy | t.CPtr = None) -> int:
"""注册函数到全局表(已存在则跳过),返回 0 成功
直接操作全局表,不调用 add_func_to_table,避免递归。
pool 用于生成 TypeStr(None 时 TypeStr 为空)。
"""
if name is None or func is None:
return 1
if self._init_global_func_table() == 0:
return 1
# 已存在则跳过(避免重复)
if self.find_func_in_table(self.GlobalFuncs, self.GlobalFuncCount, name) is not None:
return 0
if self.GlobalFuncCount >= GLOBAL_FUNC_MAX:
return 1
# 直接写入全局表(不调用 add_func_to_table,避免递归)
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(self.GlobalFuncs) + self.GlobalFuncCount * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
# Name 深拷贝到 stdlib 区:全局表跨文件永久存活,pool 内名字会被 1b 每文件
# reset_to(mark1) 回收导致悬空(vtable 生成 find_func_global 后 strcmp 崩溃)
name_len: t.CSizeT = string.strlen(name)
name_copy: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
if name_copy is None:
return 1
string.strcpy(name_copy, name)
entry.Name = name_copy
entry.Func = func
# TypeStr 不在此生成:函数参数在 add_func_to_table 之后才添加,
# 需由调用方在参数添加完成后调用 update_global_func_typestr 补全。
self.GlobalFuncCount += 1
return 0
def update_global_func_typestr(self, pool: memhub.MemBuddy | t.CPtr,
name: str,
func: llvmlite.Function | t.CPtr) -> int:
"""更新全局函数表的 TypeStr(在函数参数全部添加完成后调用)
调用时机:add_func_to_table 注册后、llvmlite.add_param 全部执行完。
生成 ret_ty (param1_ty, ...) 类型文本(stdlib 深拷贝),
供 vtable 生成在 func 被 1b reset 后使用。
"""
if name is None or func is None:
return 1
entry: GenTypes.FuncEntry | t.CPtr = self.find_func_entry_global(name)
if entry is None:
return 1
entry.TypeStr = self._build_func_type_str(pool, func)
return 0
def find_func_global(self, name: str) -> llvmlite.Function | t.CPtr:
"""在全局函数表中按名称查找,返回 Function 指针或 None"""
if name is None or self.GlobalFuncs is None:
return None
return self.find_func_in_table(self.GlobalFuncs, self.GlobalFuncCount, name)
def find_func_entry_global(self, name: str) -> GenTypes.FuncEntry | t.CPtr:
"""在全局函数表中按名称查找,返回完整 FuncEntry(含 TypeStr)或 None"""
if name is None or self.GlobalFuncs is None:
return None
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
for i in range(self.GlobalFuncCount):
entry_addr: t.CUInt64T = t.CUInt64T(self.GlobalFuncs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
def _init_global_defaults_table(self) -> int:
"""初始化全局默认参数表"""
if self.GlobalDefaults is not None:
return 1
size: t.CSizeT = GLOBAL_DEFAULTS_MAX * GenTypes.FuncDefaultsEntry.__sizeof__()
self.GlobalDefaults = CompilerFactory.get_perm_pool().alloc(size)
if self.GlobalDefaults is None:
return 0
string.memset(self.GlobalDefaults, 0, size)
self.GlobalDefaultsCount = 0
return 1
def _register_global_defaults(self, name: str,
defaults: t.CVoid | t.CPtr,
default_count: int,
param_count: int) -> int:
"""注册函数默认参数到全局表,返回 0 成功
注册时提取每个默认值 Constant 的常量快照到 stdlib(ConstKinds/ConstInts/ConstStrs),
跨模块调用时优先用快照重建常量 IR,避免读取被 reset 回收的 pool 内 AST。
"""
if name is None or defaults is None:
return 1
if self._init_global_defaults_table() == 0:
return 1
# 已存在则跳过
if self.find_func_defaults(name) is not None:
return 0
if self.GlobalDefaultsCount >= GLOBAL_DEFAULTS_MAX:
return 1
entry_size: t.CSizeT = GenTypes.FuncDefaultsEntry.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(self.GlobalDefaults) + self.GlobalDefaultsCount * entry_size
entry: GenTypes.FuncDefaultsEntry | t.CPtr = (GenTypes.FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
# Name 深拷贝到 stdlib(全局表跨文件永久存活)
nm_len: t.CSizeT = string.strlen(name)
nm_copy: str = CompilerFactory.get_perm_pool().alloc(nm_len + 1)
if nm_copy is None:
return 1
string.strcpy(nm_copy, name)
entry.Name = nm_copy
entry.Defaults = defaults
entry.DefaultCount = default_count
entry.ParamCount = param_count
# 范围防御:default_count 异常(vpi tree 损坏时 len 垃圾大数)时放弃
# 提取快照(调用方哨兵已拦截,此为第二道),避免 alloc(垃圾) + 数组
# 越界写 perm_pool 覆盖当前模块 AST/表(SlotsBugTest 崩溃根因候选)。
if default_count > 128:
entry.Defaults = None
entry.DefaultCount = 0
return 0
# 提取常量快照到 stdlib
if default_count > 0:
kinds: t.CInt | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 4)
ints: t.CInt64T | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 8)
strs: t.CSizeT | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 8)
if kinds is not None and ints is not None and strs is not None:
string.memset(kinds, 0, default_count * 4)
string.memset(ints, 0, default_count * 8)
string.memset(strs, 0, default_count * 8)
dl: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(defaults), t.CPtr))
di: int
for di in range(default_count):
dnode: ast.AST | t.CPtr = dl.get(di)
if dnode is not None and dnode.kind() == ast.ASTKind.Constant:
dcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(dnode)
kinds[di] = dcn.const_kind
ints[di] = dcn.int_val
if dcn.const_kind == ast.CONST_STR and dcn.str_val is not None:
slen: t.CSizeT = string.strlen(dcn.str_val)
scopy: str = CompilerFactory.get_perm_pool().alloc(slen + 1)
if scopy is not None:
string.strcpy(scopy, dcn.str_val)
strs[di] = t.CSizeT(scopy)
elif dnode is not None and dnode.kind() == ast.ASTKind.Name:
# CDefine Name 引用默认值(如 share: ULONG = SHARE_READ):
# 注册时求值 CDefine 存快照——否则跨模块重建时快照为空
# → "跨模块默认参数无法重建"(FileIoTest File.__init__ 根因)。
# 仅支持已注册的整型 CDefine;未注册保持 kinds=0(重建时
# 仍 fail-fast,绝不静默回退)。
dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(dnode)
if dnm.id is not None:
cdef_v: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(dnm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
kinds[di] = ast.CONST_INT
ints[di] = cdef_v
entry.ConstKinds = kinds
entry.ConstInts = ints
entry.ConstStrs = strs
self.GlobalDefaultsCount += 1
return 0
def find_func_defaults(self, name: str) -> GenTypes.FuncDefaultsEntry | t.CPtr:
"""按函数名查找默认参数信息,返回 FuncDefaultsEntry 或 None"""
if name is None or self.GlobalDefaults is None:
return None
entry_size: t.CSizeT = GenTypes.FuncDefaultsEntry.__sizeof__()
for i in range(self.GlobalDefaultsCount):
entry_addr: t.CUInt64T = t.CUInt64T(self.GlobalDefaults) + i * entry_size
entry: GenTypes.FuncDefaultsEntry | t.CPtr = (GenTypes.FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
def _build_const_from_snapshot(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr,
di: int,
trans: HT.Translator | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr) -> llvmlite.Value | t.CPtr:
"""从快照重建第 di 个默认值常量 IR"""
if gd_entry is None or gd_entry.ConstKinds is None or gd_entry.ConstInts is None:
return None
if di < 0 or di >= gd_entry.DefaultCount:
return None
kind: t.CInt = gd_entry.ConstKinds[di]
ival: t.CInt64T = gd_entry.ConstInts[di]
if kind == ast.CONST_INT:
if ival > 2147483647 or ival < -2147483648:
return llvmlite.const_int64(pool, ival)
return llvmlite.const_int32(pool, ival)
elif kind == ast.CONST_BOOL:
if ival != 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
elif kind == ast.CONST_NONE:
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
return llvmlite.ConstNull(pool, i8_ptr_ty, "null")
elif kind == ast.CONST_STR:
if gd_entry.ConstStrs is not None and gd_entry.ConstStrs[di] != 0:
sv: str = (str | t.CPtr)(t.CVoid(gd_entry.ConstStrs[di], t.CPtr))
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, sv, trans)
# 无法重建的(如 CONST_FLOAT/复杂表达式)返回 None,调用方回退到 AST
return None
def find_func_entry_in_table(self, funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str) -> GenTypes.FuncEntry | t.CPtr:
"""在函数表中按名称查找,返回完整 FuncEntry 或 None"""
if name is None or funcs is None:
return None
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
for i in range(func_count):
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
def NewExprCallHandle(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> HandlesExprCall.ExprCallHandle | t.CPtr:
h: HandlesExprCall.ExprCallHandle | t.CPtr = pool.alloc(HandlesExprCall.ExprCallHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, HandlesExprCall.ExprCallHandle.__sizeof__())
h.Trans = trans
return h