Files
ViperC/App/lib/core/Gen/Call/CallGenLeafKit.vp
T
TermiNexus 0a108e6289 refactor(tpv): 删除 FuncEntry.TypeStr 旧路径,函数类型文本改由 Spec 派生
- 删 FuncEntry.TypeStr 字段、_build_func_type_str 生产者、_check_func_spec_text 闸门
- update_global_func_typestr 改名 update_global_func_spec,仅填 Spec(去 pool 参数)
- ClassGenVTable vtable 合并 func_valid 分支,统一 SpecToLLVMText 派生
- 同步 FuncGenDef/ClassGenVTable/GenApi/VpiReader 调用点
2026-09-11 21:33:58 +08:00

1463 lines
74 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 stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeSpec as TSpec
from . import 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 = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_g)
code_ptr = self.Gen.VarGen._alloca_at_entry(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: sizet | 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: sizet | 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) -> sizet | t.CPtr:
"""从 isinstance 第二参数提取类名数组(Name→id / Attribute→attr / Tuple→遍历)"""
arr: sizet | 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] = sizet(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] = sizet(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: sizet = 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] = sizet(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] = sizet(eat.attr)
count += 1
arr[count] = 0
return arr
def _collect_subclass_vtable_classes(self, pool: memhub.MemBuddy | t.CPtr,
class_name: str) -> sizet | t.CPtr:
"""反向 BFS 收集目标类及全部子类(含自身,仅 HasVTable==1),返回 0 结尾数组"""
if class_name is None:
return None
names: sizet | t.CPtr = pool.alloc(8 * (ISINSTANCE_CLASS_MAX + 1))
queue: sizet | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
visited: sizet | 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] = sizet(class_name) # 存疑
while qcount > 0:
qcount -= 1
cur_addr: sizet = 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: sizet = 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] = sizet(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] = sizet(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: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(e2.ParentNames, t.CPtr))
for j in range(e2.ParentCount):
pa: sizet = 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] = sizet(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: sizet | 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: sizet,
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.factory.PermPool.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: sizet = sizet(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: sizet
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.factory.PermPool.alloc(64 * MAX_FUNC_PTR_SIGS)
self.FuncptrRets = CompilerFactory.factory.PermPool.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: sizet = sizet(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.factory.PermPool, ret_ty)
if ret_copy_fp is not None:
self.FuncptrRets[self.FuncptrCount] = uint64(ret_copy_fp)
else:
self.FuncptrRets[self.FuncptrCount] = uint64(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: sizet = sizet(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: sizet,
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: sizet
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: sizet = 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: sizet = 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: sizet = 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。
VLogger.error(f"print 参数翻译失败(表达式返回 None): [arg={ai}]", "PRINT")
self.Gen.TypeGenErr.fatal_error(arg, "print 参数翻译失败(表达式返回 None)", "TypeError")
return
# 数组名(如 print(l),l: t.CArray[char, 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: sizet = 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,
owner_sha1: str = None) -> llvmlite.Function | t.CPtr:
"""在函数表中按名称查找,返回 Function 指针或 None
owner_sha1: 当前模块 SHA1。非 None 时"本模块函数优先"——表里同名条目
可能同时含本模块函数与依赖 stub 注入的外来函数(LoadDepVpiIntoModule
在 forward_declare_functions 之前把依赖模块同名函数以裸名注册进本文件
_funcs,槽位在前;本模块条目注册在后)。本模块函数 mangled 名为
"{owner_sha1}.{name}",按其 16 位前缀优先返回;无本模块条目
(纯 from-import/外部 stub)时回落首个同名条目(旧语义)。
"""
if name is None or funcs is None:
return None
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
# 单遍扫描(合并原 owner 优先遍 + 通用遍,语义完全一致但省一半遍历):
# 扫描中同时记录"首个同裸名条目"作通用兜底;一旦发现本模块条目
# (mangled 名以 "{owner_sha1}." 开头,16 位前缀 + '.')立即返回。
# 等价性:原两遍先做 owner 前缀遍(命中即返),再从头做首个同裸名遍;
# 本单遍按同一顺序扫描,owner 命中时机相同,未命中时返回的首个同裸名
# 条目也与原第二遍一致(含 entry.Func 为 None 时返回 None 的旧语义)。
# 前缀相同但非 mangled 名(CExport 等裸名条目名不含 '.')不命中,
# 由首个同裸名兜底返回(同旧语义)。
fallback_entry: GenTypes.FuncEntry | t.CPtr = None
for i in range(func_count):
entry_addr: uint64 = uint64(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
if fallback_entry is None:
fallback_entry = entry
if entry.Func is not None and owner_sha1 is not None:
fn_nm_o: str = llvmlite.function_get_name(entry.Func)
if fn_nm_o is not None:
if string.strncmp(fn_nm_o, owner_sha1, 16) == 0 and fn_nm_o[16] == '.':
return entry.Func
if fallback_entry is not None:
return fallback_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.CPtr = None,
default_count: int = 0,
param_count: int = 0,
pool: memhub.MemBuddy | t.CPtr = None,
def_sha1: str = None) -> int:
"""添加函数到函数表,返回 0 成功
Args:
defaults: ast.Arguments.defaults 列表节点指针,None=无默认值
default_count: 默认值数量(len(defaults))
param_count: 形参数量(不含 self,len(args.args))
pool: 内存池(保留兼容参数;Spec 由 arena/PermPool 分配)
"""
if name is None or func is None or funcs is None:
return 1
if func_count >= max_count:
return 1
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
entry_addr: uint64 = uint64(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
# [S3 条目双写] Spec 在形参 add_param 完成后由 update_global_func_spec 填
entry.Spec = None
# 同时注册到全局函数表(供跨模块 vtable 初始化查找)
self._register_global_func(name, func, pool)
# 同时注册到全局默认参数表(供跨模块调用时填充默认值)
if default_count > 0:
self._register_global_defaults(name, defaults, default_count, param_count, def_sha1)
return 0
def _init_global_func_table(self) -> int:
"""初始化全局函数表(用 stdlib.malloc 分配,生命周期=整个程序)"""
if self.GlobalFuncs is not None:
return 1
size: sizet = GLOBAL_FUNC_MAX * GenTypes.FuncEntry.__sizeof__()
self.GlobalFuncs = CompilerFactory.factory.PermPool.alloc(size)
if self.GlobalFuncs is None:
return 0
string.memset(self.GlobalFuncs, 0, size)
self.GlobalFuncCount = 0
return 1
def _register_global_func(self, name: str, func: llvmlite.Function | t.CPtr,
pool: memhub.MemBuddy | t.CPtr = None) -> int:
"""注册函数到全局表(已存在则跳过),返回 0 成功
直接操作全局表,不调用 add_func_to_table,避免递归。
"""
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: sizet = GenTypes.FuncEntry.__sizeof__()
entry_addr: uint64 = uint64(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: sizet = string.strlen(name)
name_copy: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if name_copy is None:
return 1
string.strcpy(name_copy, name)
entry.Name = name_copy
entry.Func = func
# [S3 条目双写] Spec 待 update_global_func_spec 填(形参未 add 完)
entry.Spec = None
# Spec 不在此生成:函数参数在 add_func_to_table 之后才添加,
# 需由调用方在参数添加完成后调用 update_global_func_spec 补全。
self.GlobalFuncCount += 1
return 0
def update_global_func_spec(self, name: str,
func: llvmlite.Function | t.CPtr) -> int:
"""更新全局函数表的 Spec(在函数参数全部添加完成后调用)
调用时机:add_func_to_table 注册后、llvmlite.add_param 全部执行完。
用 NewTypeFunc 构造整签名 Spec(PermPool 驻留,跨 1b reset 安全),
供 vtable 生成在 func 被 1b reset 后经 SpecToLLVMText 派生类型文本。
"""
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.Spec = self._build_func_spec(func)
return 0
# ============================================================
# _build_func_spec - 由函数对象构造整签名 TypeSpec(S3 条目双写)
#
# Kind=TS_FUNC:Ret + 形参 Spec 数组 + ParamCount + IsVarArg。必须在形参
# 全部 add_param 完成后调用。
# 参数沿 `llvmlite.function_get_param_head` 的 Param 链表**两遍遍历**(先计数
# 再填充)——llvmlite 无按下标取参,且无法预知长度以一次分配。
# 注意与 `TSpec.SpecFromLLVMType` 的 Func 分支的区别:后者只能看到
# `LLVMType.Func`(**不含变参信息**,恒置 IsVarArg=0),故此处直接用
# `NewTypeFunc(..., func.IsVarArg)` 构造。
# 失败(池分配失败等)返回 None;调用方保留 None,不静默造伪 spec。
# ============================================================
def _build_func_spec(self, func: llvmlite.Function | t.CPtr) -> TSpec.TypeSpec | t.CPtr:
"""由函数对象构造整签名 TypeSpec,失败返回 None"""
if func is None:
return None
arena: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
if arena is None:
return None
ret_s: TSpec.TypeSpec | t.CPtr = TSpec.SpecFromLLVMType(
arena, llvmlite.function_get_ret_ty(func))
if ret_s is None:
return None
# 第一遍:计数
cnt: int = 0
cur: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
while cur is not None and cnt < 256:
cnt += 1
cur = llvmlite.param_get_next(cur)
# 第二遍:逐个构造并写入连续参数数组
params_arr: sizet | t.CPtr = None
if cnt > 0:
params_arr = CompilerFactory.factory.PermPool.alloc(8 * cnt)
if params_arr is None:
return None
cur = llvmlite.function_get_param_head(func)
i: int = 0
while cur is not None and i < cnt:
ps: TSpec.TypeSpec | t.CPtr = TSpec.SpecFromLLVMType(
arena, llvmlite.param_get_ty(cur))
if ps is None:
return None
slot: sizet | t.CPtr = (sizet | t.CPtr)(
t.CVoid(uint64(params_arr) + uint64(i * 8), t.CPtr))
if slot is None:
return None
slot[0] = sizet(ps)
cur = llvmlite.param_get_next(cur)
i += 1
return TSpec.NewTypeFunc(arena, ret_s, params_arr, cnt, func.IsVarArg)
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(含 Spec)或 None"""
if name is None or self.GlobalFuncs is None:
return None
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
for i in range(self.GlobalFuncCount):
entry_addr: uint64 = uint64(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: sizet = GLOBAL_DEFAULTS_MAX * GenTypes.FuncDefaultsEntry.__sizeof__()
self.GlobalDefaults = CompilerFactory.factory.PermPool.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.CPtr,
default_count: int,
param_count: int,
def_sha1: str = None) -> int:
"""注册函数默认参数到全局表,返回 0 成功
注册时提取每个默认值 Constant 的常量快照到 stdlib(ConstKinds/ConstInts/ConstStrs),
跨模块调用时优先用快照重建常量 IR,避免读取被 reset 回收的 pool 内 AST。
def_sha1:**定义该函数的模块 SHA1**。依赖注入路径(VpiReader)必须显式传入,
否则会退回 CurrentModuleSha1 —— 注入期它是**导入方**,会把被依赖模块函数的
默认值挂到导入方名下(如 mm.init 的 2 个默认值挂到 usb)→ usb 的
`uhci.init(io_base)` 经 find_func_defaults("init", usb_sha) 命中该错账条目
而被补 2 个 i32 0(N17 真因;模块函数表的"按定义模块限定查找"救不了这条
全局表路径,因为该处 found_func 仍为 None、交叉校验被整体跳过)。
"""
if name is None or defaults is None:
return 1
if self._init_global_defaults_table() == 0:
return 1
# 限定键 = **定义该函数的模块 SHA1**:同一函数名在不同模块各存一条,查询时
# 按「名 + 定义模块」精确匹配,消除跨模块同名串味。def_sha1 由依赖注入
# 路径显式传入被依赖模块自身 sha1;非注入路径(本模块真实定义)取
# CurrentModuleSha1(此时它就是定义模块)。
cur_sha: str = def_sha1
if cur_sha is None and self.Gen is not None and self.Gen.TypeGenBase is not None:
cur_sha = self.Gen.TypeGenBase.CurrentModuleSha1
# 同模块同名已存在则跳过
if self.find_func_defaults(name, cur_sha) is not None:
return 0
if self.GlobalDefaultsCount >= GLOBAL_DEFAULTS_MAX:
return 1
entry_size: sizet = GenTypes.FuncDefaultsEntry.__sizeof__()
entry_addr: uint64 = uint64(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: sizet = string.strlen(name)
nm_copy: str = CompilerFactory.factory.PermPool.alloc(nm_len + 1)
if nm_copy is None:
return 1
string.strcpy(nm_copy, name)
entry.Name = nm_copy
entry.ModuleSha1 = None
if cur_sha is not None:
sh_len: sizet = string.strlen(cur_sha)
sh_copy: str = CompilerFactory.factory.PermPool.alloc(sh_len + 1)
if sh_copy is not None:
string.strcpy(sh_copy, cur_sha)
entry.ModuleSha1 = sh_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: int | t.CPtr = CompilerFactory.factory.PermPool.alloc(default_count * 4)
ints: int64 | t.CPtr = CompilerFactory.factory.PermPool.alloc(default_count * 8)
strs: sizet | t.CPtr = CompilerFactory.factory.PermPool.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(uint64(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: sizet = string.strlen(dcn.str_val)
scopy: str = CompilerFactory.factory.PermPool.alloc(slen + 1)
if scopy is not None:
string.strcpy(scopy, dcn.str_val)
strs[di] = sizet(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
elif dnode is not None and dnode.kind() == ast.ASTKind.UnaryOp:
# 一元字面量默认值(如 ex3: int = -1):AST 是
# UnaryOp(USub, Constant(1)),不是 Constant —— 此前无此分支
# → kinds[di] 保持 0 → 快照重建失败 → "跨模块默认参数无法
# 重建(快照缺失,默认值需为简单常量)"(AppGui.top_index
# 的 ex3 = -1 根因)。只对"操作数是整型 Constant"求值,
# 其它(浮点取负 / 取负变量)保持 kinds=0 → 重建时 fail-fast。
du: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(dnode)
if du.operand is not None and du.operand.kind() == ast.ASTKind.Constant:
doc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(du.operand)
if doc.const_kind == ast.CONST_INT:
if du.op == ast.OpKind.USub:
kinds[di] = ast.CONST_INT
ints[di] = -doc.int_val
elif du.op == ast.OpKind.UAdd:
kinds[di] = ast.CONST_INT
ints[di] = doc.int_val
elif du.op == ast.OpKind.Invert:
kinds[di] = ast.CONST_INT
ints[di] = ~doc.int_val
entry.ConstKinds = kinds
entry.ConstInts = ints
entry.ConstStrs = strs
self.GlobalDefaultsCount += 1
return 0
def find_func_defaults(self, name: str,
module_sha1: str = None) -> GenTypes.FuncDefaultsEntry | t.CPtr:
"""按函数名(可选按定义模块 SHA1)查找默认参数信息,返回 FuncDefaultsEntry 或 None
module_sha1 非 None 时要求条目定义模块完全一致(限定名匹配,消除跨模块同名串味);
为 None 时退化为按名匹配(兼容旧调用点)。
"""
if name is None or self.GlobalDefaults is None:
return None
entry_size: sizet = GenTypes.FuncDefaultsEntry.__sizeof__()
for i in range(self.GlobalDefaultsCount):
entry_addr: uint64 = uint64(self.GlobalDefaults) + i * entry_size
entry: GenTypes.FuncDefaultsEntry | t.CPtr = (GenTypes.FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is None:
continue
if string.strcmp(entry.Name, name) != 0:
continue
if module_sha1 is not None:
if entry.ModuleSha1 is None:
continue
if string.strcmp(entry.ModuleSha1, module_sha1) != 0:
continue
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: int = gd_entry.ConstKinds[di]
ival: int64 = 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: sizet = GenTypes.FuncEntry.__sizeof__()
for i in range(func_count):
entry_addr: uint64 = uint64(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 find_func_entry_in_table_owned(self, funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str,
owner_sha1: str) -> GenTypes.FuncEntry | t.CPtr:
"""按「裸名 + 定义模块 SHA1」查找函数表条目,返回 FuncEntry 或 None
函数表以**裸名**登记,且 LoadDepVpiIntoModule 会把依赖模块的同名函数
(同样裸名)注入到本模块表 —— 裸名首命中会跨模块串味:usb.vp 的
`uhci.init(io_base)`(1 形参)会命中先注入的 `mm.init`(5 形参/2 默认值)
→ 调用点被补 2 个 `i32 0` → call 3 实参 vs declare/define 1 形参(N17)。
故以 `entry.Func` 的 mangled 名("{定义模块 sha1}.{name}")前缀限定定义
模块;owner_sha1 为 None(模块名不可解)时退化为裸名首命中(旧语义,
避免丢失合法的默认参数填充)。
"""
if name is None or funcs is None:
return None
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
for i in range(func_count):
entry_addr: uint64 = uint64(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is None or entry.Name is None:
continue
if string.strcmp(entry.Name, name) != 0:
continue
if owner_sha1 is None:
return entry
if entry.Func is not None:
fn_nm_o: str = llvmlite.function_get_name(entry.Func)
if fn_nm_o is not None:
if string.strncmp(fn_nm_o, owner_sha1, 16) == 0 and fn_nm_o[16] == '.':
return entry
return None
def NewExprCallHandle(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> ExprCallHandle | t.CPtr:
h: ExprCallHandle | t.CPtr = pool.alloc(ExprCallHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, ExprCallHandle.__sizeof__())
h.Trans = trans
return h
# ============================================================
# ExprCallHandle - 函数调用处理器(自 HandlesExprCall 迁入)
#
# HandleCall / HandlePrintfCall / HandlePrintCall 提供 trans 接口,
# 内部委托 GenApi.translate_call 等。
# ============================================================
@t.NoVTable
class ExprCallHandle(HT.Mixin):
"""函数调用处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# HandleCall - 翻译函数调用,返回 call 结果 Value
# ============================================================
def HandleCall(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译函数调用,从 self.Trans 获取共享状态"""
return GenApi.translate_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
node,
self.Trans._funcs, self.Trans._func_count, self.Trans)
# ============================================================
# HandlePrintfCall - 翻译 printf 调用
# ============================================================
def HandlePrintfCall(self, cl: ast.Call | t.CPtr):
"""翻译 printf 调用,从 self.Trans 获取共享状态"""
GenApi.translate_printf_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
cl, self.Trans)
# ============================================================
# HandlePrintCall - 翻译 print 调用
# ============================================================
def HandlePrintCall(self, cl: ast.Call | t.CPtr):
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
GenApi.translate_print_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
cl, self.Trans)