From 21e67cac404169191811db0babce60468c6752af Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 25 Aug 2026 14:04:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E5=90=AB=20raise=20=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=B0=83=E7=94=A8=E5=8F=82=E6=95=B0=E8=A1=A5=E5=85=A8?= =?UTF-8?q?=E6=8E=92=E9=99=A4=20EH=20=E8=BE=93=E5=87=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E6=9D=9C=E7=BB=9D=E5=8F=8C=E9=87=8D=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=20-=20CallGenMixin=20=E9=9B=B6=E5=80=BC=E5=85=9C=E5=BA=95?= =?UTF-8?q?=E4=B8=8E=20CallGenCall=20found=5Ffunc=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=95=B0=E5=9B=9E=E9=80=80=E5=8E=9F=E5=B0=86=20%=5F=5Feh=5Fmsg?= =?UTF-8?q?=5Fout=5F=5F/%=5F=5Feh=5Fcode=5Fout=5F=5F=20=E8=AE=A1=E5=85=A5?= =?UTF-8?q?=E5=BD=A2=E5=8F=82=E6=95=B0=EF=BC=8C=E4=B8=BA=20raise=5Fdisk()?= =?UTF-8?q?=20=E6=97=A0=E5=8F=82=E8=B0=83=E7=94=A8=E8=A1=A5=202=20?= =?UTF-8?q?=E4=B8=AA=20zeroinitializer=20=E5=8D=A0=E4=BD=8D=EF=BC=8C=5Femi?= =?UTF-8?q?t=5Feh=5Fguarded=5Fcall=20=E5=8F=88=E8=BF=BD=E5=8A=A0=E7=9C=9F?= =?UTF-8?q?=E5=AE=9E=20EH=20=E5=AE=9E=E5=8F=82=20=E2=86=92=20call=204=20?= =?UTF-8?q?=E5=8F=82=20vs=20define=202=20=E5=8F=82=20=E2=86=92=20x64=20ABI?= =?UTF-8?q?=20=E9=94=99=E4=BD=8D=20=E2=86=92=20=E5=87=BD=E6=95=B0=E5=86=99?= =?UTF-8?q?=20null=20=E8=BE=93=E5=87=BA=E6=8C=87=E9=92=88=200xC0000005(Tes?= =?UTF-8?q?tFBProject=20raise=5Fdisk=20=E5=B4=A9=E6=BA=83=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0)=EF=BC=9B=E4=B8=89=E5=A4=84=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E5=9D=87=E6=8E=92=E9=99=A4=20EH=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TransPyV/App/lib/core/Gen/CallGenCall.vp | 22 +- TransPyV/App/lib/core/Gen/CallGenMixin.vp | 1283 +++++++++++++++++++++ 2 files changed, 1301 insertions(+), 4 deletions(-) create mode 100644 TransPyV/App/lib/core/Gen/CallGenMixin.vp diff --git a/TransPyV/App/lib/core/Gen/CallGenCall.vp b/TransPyV/App/lib/core/Gen/CallGenCall.vp index 7f73d4a..8d61363 100644 --- a/TransPyV/App/lib/core/Gen/CallGenCall.vp +++ b/TransPyV/App/lib/core/Gen/CallGenCall.vp @@ -459,14 +459,21 @@ class CallGenCall(CallGenBase.CallGenBase): # 函数表/默认表无 ParamCount 但 found_func(declare)已找到时用其参数数 # 作补全基准,否则省略默认参数(如 translate_value 的 target_ty=None) # 的调用补全不触发 → call 实参 < define 形参 → ABI 错位读栈残留垃圾。 + # 排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__):含 raise 函数 + # 定义尾部注入 2 个 EH 形参,计入会让 _emit_eh_guarded_call 追加真实 + # EH 实参前先补 zeroinitializer 占位 → 双重注入 call 实参多 2(TestFBProject + # raise_disk() 崩溃根因,与 CallGenMixin 零值兜底同源)。 if mdf_param_count == 0 and found_func is not None: ppcnt_f2: int = 0 ppn_f2: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) while ppn_f2 is not None: - ppcnt_f2 += 1 + fp_nm_f2: str = llvmlite.param_get_name(ppn_f2) + if fp_nm_f2 is None or (string.strcmp(fp_nm_f2, "%__eh_msg_out__") != 0 + and string.strcmp(fp_nm_f2, "%__eh_code_out__") != 0): + ppcnt_f2 += 1 ppn_f2 = llvmlite.param_get_next(ppn_f2) if ppcnt_f2 > 1: - mdf_param_count = ppcnt_f2 - 1 # 非 self 参数数 + mdf_param_count = ppcnt_f2 - 1 # 非 self 非 EH 参数数 # 计算需要填充的参数(不含 self) # extra_count = 已提供的非 self 参数数量 @@ -1410,14 +1417,21 @@ class CallGenCall(CallGenBase.CallGenBase): # target_ty=None)的调用补全不触发 → call 实参(8) < define 形参(9) # → x64 ABI 第 9 参读栈残留垃圾 → is_ptr_type(0x40) 崩溃(SlotsBugTest # 二代编 translate_assign/AugAssign 崩溃根因)。 + # 排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__):含 raise 方法 + # 定义尾部注入 2 个 EH 形参,计入会让补全先补 zeroinitializer 占位, + # _emit_eh_guarded_call 再追加真实 EH 实参 → 双重注入(TestFBProject + # raise_disk() 崩溃同源,与 _call_method_on_ptr/CallGenMixin 一致)。 if mdf_param_count == 0 and found_func is not None: ppcnt_f: int = 0 ppn_f: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) while ppn_f is not None: - ppcnt_f += 1 + fp_nm_f: str = llvmlite.param_get_name(ppn_f) + if fp_nm_f is None or (string.strcmp(fp_nm_f, "%__eh_msg_out__") != 0 + and string.strcmp(fp_nm_f, "%__eh_code_out__") != 0): + ppcnt_f += 1 ppn_f = llvmlite.param_get_next(ppn_f) if ppcnt_f > 1: - mdf_param_count = ppcnt_f - 1 # 非 self 参数数 + mdf_param_count = ppcnt_f - 1 # 非 self 非 EH 参数数 # 计算需要填充的参数(不含 self) # arg_count_eff = 已提供的非 self 参数数量(含关键字模式完整构建) diff --git a/TransPyV/App/lib/core/Gen/CallGenMixin.vp b/TransPyV/App/lib/core/Gen/CallGenMixin.vp new file mode 100644 index 0000000..942b5ab --- /dev/null +++ b/TransPyV/App/lib/core/Gen/CallGenMixin.vp @@ -0,0 +1,1283 @@ +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.Stub.StubGen.VpiReader as VpiReader + +import lib.core.Gen.CallGenBase as CallGenBase +import lib.core.Handles.ExprCallTable as ExprCallTable + + +# ============================================================ +# CallGenMixin - 调用分派 Mixin(拆分后顶层,阶段 A:底层已拆至 +# CallGenBase/CallGenCBuiltin/CallGenLeafKit) +# +# 500~1500 行/文件架构重构阶段 A:底层自包含组(模块查找/返回类型解析/ +# c.*/asm/llvmir/EH/isinstance/闭包/print/函数表)已拆出,本文件保留 +# 调用分派中枢(translate_call 等)。继承链 CallGenMixin → LeafKit → +# CBuiltin → Base,@t.NoVTable 静态绑定沿 MRO 解析 self 调用。 +# +# @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 + + +# ============================================================ +# CallGenMixin - 调用分派中枢(顶层,继承 LeafKit) +# ============================================================ +@t.NoVTable +class CallGenMixin(CallGenBase.CallGenBase): + """调用分派 Mixin(拆分后顶层,直接继承 CallGenBase——TPV self 调用仅支持 1 级父类)""" + + # ============================================================ + # 翻译函数调用 Call(func, args) — 模块级版本 + # ============================================================ + def translate_call(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + funcs_ptr: t.CPtr, + func_count: int, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """翻译函数调用,返回 call 结果 Value""" + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl is None: + return None + + func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func) + + cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args + can: t.CSizeT = 0 + if cargs is not None: + can = len(cargs) + + # super()/ctraits 检测:void 方法(如 __init__)命中后 build_call 返回 + # None 属预期(void call 无 SSA 值),必须用 hit_box 区分"命中已处理" + # 与"未命中",否则 None 被误判继续主路径 → 内层 Call(super) 当裸名 + # 调用报"super 未定义且未导入"(SuperTest 根因)。 + h1_hit: t.CPtr = stdlib.malloc(4) + if h1_hit is not None: + h1_hit[0] = 0 + _tc_h1_super_ctraits_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h1_super_ctraits( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h1_hit) + h1_hit_val: int = 0 + if h1_hit is not None: + h1_hit_val = h1_hit[0] + stdlib.free(h1_hit) + if _tc_h1_super_ctraits_res is not None: + return _tc_h1_super_ctraits_res + if h1_hit_val != 0: + # 命中 super()/ctraits 但无返回值(void 方法/宏调用):视为已处理 + return None + + # ---- func_name is None:泛型实例化 + 类型转换 ---- + if func_name is None: + # 检查是否是 list[int](pool) 形式的泛型实例化调用 + # Subscript 节点作为 Call func: 泛型类构造函数 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Subscript and trans is not None: + sub_node: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(cl.func) + if sub_node.value is not None and sub_node.value.kind() == ast.ASTKind.Name: + gen_nm_node: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_node.value) + gen_class_name: str = gen_nm_node.id + # 检查是否是泛型类模板 + template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name) + if template_cd is not None: + # 提取类型实参 + gen_type_args: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice, trans._from_imports) + if gen_type_args is not None and len(gen_type_args) > 0: + # 特化泛型类,返回特化类名 + spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class( + trans, gen_class_name, gen_type_args) + if spec_name is not None: + # 泛型类构造调用: list[str](pool, 8) → __new__ + __before_init__ + __init__ + # 不能直接用 spec_name 作为 func_name(会走裸名调用路径, + # 被 star import 回退误导用错误的 SHA1,且缺少 __new__ 方法名后缀) + return self.Gen.CallGenCtor._translate_generic_ctor_call( + pool, builder, mod, spec_name, cl, trans) + else: + # 泛型类模板未找到:可能是泛型函数手动传类型参数调用 xxx[T](x) + # (对应 TPC _HandleGenericCallExplicitLlvm;TPV 无泛型函数特化, + # 泛型函数按普通函数编译,类型参数 [T] 仅在函数定义处消费)。 + # 检查当前模块是否已定义同名函数(def double[T] 编译后即普通函数 + # double),若是则丢弃 [t.CInt] 显式类型参数,退回普通函数调用。 + if self.find_func_in_module(mod, gen_class_name) is not None: + func_name = gen_class_name + else: + # 泛型类模板未找到:可能未 import 定义模块 + gt_err: str = pool.alloc(256) + if gt_err is not None: + viperlib.snprintf(gt_err, 256, "泛型类 '%s' 未找到模板,可能未 import 定义模块", gen_class_name) + self.Gen.TypeGenErr.fatal_error(gen_nm_node, gt_err) + self.Gen.TypeGenErr.fatal_error(gen_nm_node, "泛型类模板未找到") + if func_name is None: + # 检查是否是 (A | t.CPtr)(value) 形式的类型转换 + # BinOp 节点作为 Call func: 联合类型注解用作类型转换 + if cl.func is not None and cl.func.kind() == ast.ASTKind.BinOp and trans is not None: + binop_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( + pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0) + if binop_ty is not None: + binop_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args + if binop_args is not None and len(binop_args) == 1: + binop_arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, binop_args.get(0), funcs_ptr, func_count, trans) + if binop_arg_val is not None: + return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, binop_arg_val, binop_ty) + return None + + # c.Asm/c.LLVMIR 等特殊调用:void(无输出)时 translate_c_asm/llvmir + # 返回 None 表示"已处理无返回值",需用 hit_box 区分"未命中", + # 否则会误判未命中继续走外部函数路径(外部函数返回类型未登记 Asm + + # t.ASM_DESCR 被当 CDefine 常量赋值) + h2_hit: t.CPtr = stdlib.malloc(4) + if h2_hit is not None: + h2_hit[0] = 0 + _tc_h2_c_special_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h2_c_special( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h2_hit) + h2_hit_val: int = 0 + if h2_hit is not None: + h2_hit_val = h2_hit[0] + stdlib.free(h2_hit) + if _tc_h2_c_special_res is not None: + return _tc_h2_c_special_res + if h2_hit_val != 0: + # 命中 c.Asm/c.LLVMIR 但无返回值(void):视为已处理 + return None + + _tc_h3_stdarg_cast_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h3_stdarg_cast( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can) + if _tc_h3_stdarg_cast_res is not None: + return _tc_h3_stdarg_cast_res + + _tc_h4_type_casts_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h4_type_casts( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can) + if _tc_h4_type_casts_res is not None: + return _tc_h4_type_casts_res + + _tc_h5_sizeof_len_isinst_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h5_sizeof_len_isinst( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can) + if _tc_h5_sizeof_len_isinst_res is not None: + return _tc_h5_sizeof_len_isinst_res + + # c.Asm/c.LLVMIR 等方法调用分派:方法调用识别成功但返回 None + # (如编译器生成方法 __before_init__/__init__ 恒返回 void,_translate_ + # method_call 或 _emit_eh_guarded_call 处理后可能返回 None)时,须用 + # hit_box 区分"已识别为方法调用"与"未命中",否则 translate_call 误判 + # 未命中继续走跨模块函数路径,把对象名当模块名查 SHA1 → + # "跨模块调用 'buf.__before_init__' 找不到模块 SHA1"(memhub.alloc_buf 根因)。 + h6_hit: t.CPtr = stdlib.malloc(4) + if h6_hit is not None: + h6_hit[0] = 0 + _tc_h6_method_dispatch_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h6_method_dispatch( + pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h6_hit) + h6_hit_val: int = 0 + if h6_hit is not None: + h6_hit_val = h6_hit[0] + stdlib.free(h6_hit) + if _tc_h6_method_dispatch_res is not None: + return _tc_h6_method_dispatch_res + if h6_hit_val != 0: + # 命中方法调用路径但无返回值(编译器生成方法等 void 语义):视为已处理 + return None + + # 获取关键字参数 + keywords: list[ast.AST | t.CPtr] | t.CPtr = cl.keywords + kw_count: t.CSizeT = 0 + if keywords is not None: + kw_count = len(keywords) + + # 检查是否是结构体构造函数: Point() / Point(10, 20) / Point(x=10, y=20) + # 命名空间隔离:严格模式下检查结构体可见性 + # - 裸名 Class(): 需要 from X import Class 或本地定义 + # - 模块限定 Module.Class(): 需要 import Module + # 用 find_struct_by_module 规避跨模块同名 find_struct 找错 + struct_ty: llvmlite.LLVMType | t.CPtr = None + gen_ctor_entry: GenTypes.StructEntry | t.CPtr = None + vis: int = self.Gen.StructGen.is_struct_visible(func_name) + if vis != 0: + if trans is not None: + gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1) + if gen_ctor_entry is None: + gen_ctor_entry = self.Gen.StructGen.find_struct(func_name) + if gen_ctor_entry is not None: + struct_ty = gen_ctor_entry.Ty + elif trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + at_qc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name: + mod_nm_qc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_qc.value) + imp_ok: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_nm_qc.id) + if imp_ok != 0: + if trans is not None: + gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1) + if gen_ctor_entry is None: + gen_ctor_entry = self.Gen.StructGen.find_struct(func_name) + if gen_ctor_entry is not None: + struct_ty = gen_ctor_entry.Ty + elif at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Attribute: + # 多层模块路径类构造: w32.fileio.File(...)(value 是嵌套 Attribute)。 + # 仅两层(fileio.File)支持时,三层会被跳过 → 退化走跨模块普通函数 + # 调用生成 @File 裸名 → 链接 undefined symbol: File(SysLibTest 根因)。 + # 模块路径经 attr_module_path_str 递归拼接("w32.fileio"), + # import 检查用完整路径(import w32.fileio 注册 "w32.fileio"), + # 结构体查找用该模块自身 SHA1 精确命中(find_struct_by_module)。 + # 注:经 HandlesExpr 委托调用(跨 mixin 不能 self. 直接调, + # @t.NoVTable 静态绑定会 mangle 成 CallGenMixin. 前缀 → undefined reference)。 + mod_path_qc: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc.value) + if mod_path_qc is not None: + imp_ok_qc: int = self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, mod_path_qc) + if imp_ok_qc != 0: + mod_sha1_qc: str = self._lookup_module_sha1(mod_path_qc) + if mod_sha1_qc is not None: + gen_ctor_entry = self.Gen.StructGen.find_struct_by_module( + func_name, mod_sha1_qc) + if gen_ctor_entry is None: + gen_ctor_entry = self.Gen.StructGen.find_struct(func_name) + if gen_ctor_entry is not None: + struct_ty = gen_ctor_entry.Ty + # 别名导入的类构造: from X import Class as Alias → Alias(args) + # 可见性列表按别名注册(HandleImportFromNames 用 local_name=asname),但结构体 + # 按原名注册(如 Keyword as KwNode → 可见 "KwNode"、注册名 "Keyword")。 + # 直接按别名 find_struct 查不到 → 误走普通函数路径生成 @sha1.Keyword 调用 + # (该函数不存在)→ 链接期 undefined reference。此处解析别名→原名再查结构体。 + if gen_ctor_entry is None and trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + if trans._from_imports is not None: + ctor_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) + if ctor_sha1 is not None: + ctor_orig: str = self.Gen.ImportsGen.lookup_from_import_original( + trans._from_imports, func_name) + if ctor_orig is not None: + # 复制到独立 null 终止缓冲区(from_imports 内部缓冲未终止) + co_len: t.CSizeT = 0 + while ctor_orig[co_len] != ' ' and ctor_orig[co_len] != '\0': + co_len += 1 + co_buf: str = pool.alloc(co_len + 1) + if co_buf is not None: + for co_i in range(co_len): + co_buf[co_i] = ctor_orig[co_i] + co_buf[co_len] = '\0' + # 原名结构体查找(可见性已由别名保证,原名无需再查) + gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(co_buf, ctor_sha1) + if gen_ctor_entry is None: + gen_ctor_entry = self.Gen.StructGen.find_struct(co_buf) + if gen_ctor_entry is not None: + struct_ty = gen_ctor_entry.Ty + func_name = co_buf + if struct_ty is not None: + # [CLDBG] 诊断(用后移除):打印 cl 节点与 args 字段,确认 + # use-after-free——cl 是否新分配(对比 LISTDBG/ALLOCDBG 地址)、 + # cl.args 读取是否就是 cargs(悬空旧值 vs 当前值) + if memhub.get_debug_log() != 0: + cl_kind_v: t.CInt = -1 + if cl is not None: + cl_kind_v = cl.kind() + cl_args_v: t.CUInt64T = 0 + if cl is not None and cl.args is not None: + cl_args_v = t.CUInt64T(cl.args) + stdio.printf("[CLDBG] mod=%s cls=%s cl=%llu kind=%d can=%d cargs=%llu args_field=%llu\n", + trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?", + func_name if func_name is not None else "?", + t.CUInt64T(cl), cl_kind_v, can, + t.CUInt64T(cargs), cl_args_v) + if can == 0 and kw_count == 0: + # OOP 结构体:即使无参数也调用 __before_init__ + # 用 gen_ctor_entry 规避跨模块同名 find_struct 找错 + ctor_is_oop: int = 0 + if gen_ctor_entry is not None: + ctor_is_oop = gen_ctor_entry.IsOOP + if ctor_is_oop != 0: + return self.Gen.CallGenCtor._translate_struct_ctor( + pool, builder, mod, struct_ty, func_name, cargs, 0, trans) + # @t.NoVTable 类(无方法、类语义/指针对象):无参构造也应分配内存 + # 返回指针。否则按值结构体返回 ConstZero(struct_ty),LHS 联合注解 + # (如 Plain|CPtr)存 0 指针 → p.v 解引用 NULL 段错误 + # (TpvIsinstanceTest Plain 场景崩溃根因)。 + ctor_is_novt: int = 0 + if gen_ctor_entry is not None: + ctor_is_novt = gen_ctor_entry.IsNoVTable + if ctor_is_novt != 0: + return self.Gen.CallGenCtor._translate_struct_ctor( + pool, builder, mod, struct_ty, func_name, cargs, 0, trans) + # 普通结构体:检查是否有默认值(用 entry 直接访问) + gen_has_defaults: int = 0 + if gen_ctor_entry is not None: + for gdi in range(gen_ctor_entry.FieldCount): + gdf: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry( + gen_ctor_entry, gdi) + if gdf is not None and gdf.DefaultVal is not None: + gen_has_defaults = 1 + break + else: + gen_has_defaults = self.Gen.StructGen.struct_has_defaults(func_name) + if gen_has_defaults == 1: + # 有默认值:走构造函数路径应用默认值 + return self.Gen.CallGenCtor._translate_struct_ctor( + pool, builder, mod, struct_ty, func_name, cargs, 0, trans) + # 无默认值:返回 zeroinitializer + ret: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty) + return ret + if kw_count > 0: + # 有关键字参数:按字段名赋值 + return self.Gen.CallGenCtor._translate_struct_ctor_kw( + pool, builder, mod, struct_ty, func_name, cargs, can, + keywords, kw_count, trans) + # 仅位置参数:按字段顺序构建 + return self.Gen.CallGenCtor._translate_struct_ctor( + pool, builder, mod, struct_ty, func_name, cargs, can, trans) + + # 裸名泛型类模板构造: Box(42) → 从实参推断类型实参 → 特化 → 构造 + # (TPC 参考: _HandleGenericClassNewLlvm;GenericTest Test4 Box(42)/Test5 + # Pair(10,20)/Test10 Counter(0) 依赖此路径。显式类型实参 list[int](mb) + # 走 translate_call 上方的 Subscript 分支,此处处理裸名 + 推断。) + # 支持模块限定调用(vector.Vector(...)):func_name 是 attr("Vector"), + # 需确认模块已导入(模板查找兜底——普通函数如 stdlib.free 的 attr 找 + # 不到模板 → 回落普通调用)。 + if struct_ty is None and gen_ctor_entry is None and trans is not None: + is_gen_bare: int = 0 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + is_gen_bare = 1 + elif cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + at_gb: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at_gb.value is not None and at_gb.value.kind() == ast.ASTKind.Name: + am_gb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_gb.value) + if am_gb.id is not None: + if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, am_gb.id) != 0: + is_gen_bare = 1 + if is_gen_bare != 0: + template_cd_bare: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(func_name) + if template_cd_bare is not None and template_cd_bare.type_params is not None: + tp_bare: list[str] | t.CPtr = template_cd_bare.type_params + tp_count_bare: t.CSizeT = len(tp_bare) + if tp_count_bare > 0: + inferred_args_bare: list[str] | t.CPtr = list[str](pool, 8) + if inferred_args_bare is not None: + # 对齐 TPC _HandleGenericClassNewLlvm:先收集全部实参 + # 推断类型,过滤纯 int(排除 pool 等非 T 参数), + # hint_types 优先填充(Vector(pool,8,CFloat64T(0.0)) + # → Vector[double];Vector(pool,10,CInt(0)) → Vector[int]) + hint_bare: list[str] | t.CPtr = list[str](pool, 8) + if hint_bare is not None: + hi_bare: t.CSizeT + for hi_bare in range(can): + hty_bare: str = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(hi_bare), func_name) + if string.strcmp(hty_bare, "int") != 0: + hint_bare.append(hty_bare) + tpi_bare: t.CSizeT = 0 + for tpi_bare in range(tp_count_bare): + ta_bare: str = "int" + if tpi_bare < len(hint_bare): + ta_bare = hint_bare.get(tpi_bare) + elif tpi_bare < can: + ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(tpi_bare), func_name) + inferred_args_bare.append(ta_bare) + spec_name_bare: str = self.Gen.ClassGenGeneric._specialize_generic_class( + trans, func_name, inferred_args_bare) + if spec_name_bare is not None: + return self.Gen.CallGenCtor._translate_generic_ctor_call( + pool, builder, mod, spec_name_bare, cl, trans) + + # REnum 变体构造检测: ClassName.VariantName(args...) + # 如 LLVMType.Int(1) → 构造 LLVMType 的 Int 变体 + # 必须在跨模块调用查找之前,否则 ClassName 会被误判为模块名 + # 对应 TPC HandlesExprCall.py:585-644 的三条检测路径之一 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + rc_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name: + rc_cls_node: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rc_at.value) + if rc_cls_node.id is not None: + rc_cls_name: str = rc_cls_node.id + # 检查 ClassName 是否是已注册的 REnum 类 + if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0: + # 在该 REnum 类中查找变体名 func_name + rc_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( + rc_cls_name, func_name) + if rc_tag >= 0: + return self.Gen.CallGenCtor._translate_renum_construct( + pool, builder, mod, rc_cls_name, func_name, + rc_tag, cargs, can, trans) + elif rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Attribute: + # 模块限定 REnum 变体构造: mod.Class.Variant(args...) + # 如 llvmlite.LLVMType.Struct(...) → 构造 llvmlite 模块 LLVMType 的 Struct 变体。 + # 根因:此前只支持单层 Name(ClassName.Variant),嵌套属性漏检 → 误走跨模块 + # 函数路径 → _lookup_module_sha1("llvmlite.LLVMType") 查无 → 报"跨模块调用 + # 'llvmlite.LLVMType.Struct' 找不到模块 SHA1"。跨模块任意同名可接受(REnum + # 表按类名全局注册):仅要求最左模块已导入 + 末段类名在全局 REnum 表中命中, + # 不做"类必须属于该模块"校验(未命中则按后续函数路径 fast-fail)。 + rc_mq_path: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, rc_at.value) + if rc_mq_path is not None: + rc_mq_dot: str = string.strrchr(rc_mq_path, 46) # 46 = ord('.') + if rc_mq_dot is not None and rc_mq_dot[1] != '\0': + # 模块路径 = 末段之前(支持多级: w32.fileio.Class.Variant)。 + # 注意:不能用 rc_mq_dot - rc_mq_path 指针减法(TPC 直接发 + # sub i8*, i8* 非法 IR),用 strlen 整数差扣除末段与分隔点。 + rc_mq_len: t.CSizeT = string.strlen(rc_mq_path) - string.strlen(rc_mq_dot + 1) - 1 + rc_mq_mod: str = pool.alloc(rc_mq_len + 1) + if rc_mq_mod is not None: + string.memcpy(rc_mq_mod, rc_mq_path, rc_mq_len) + rc_mq_mod[rc_mq_len] = '\0' + rc_mq_imp: int = 1 + if trans is not None and trans._imported_modules is not None: + rc_mq_imp = self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, rc_mq_mod) + if rc_mq_imp != 0: + rc_mq_cls: str = rc_mq_dot + 1 + # 检查类名是否是已注册的 REnum 类 + if self.Gen.ClassGenEnum._is_renum_class_name(rc_mq_cls) != 0: + rc_mq_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( + rc_mq_cls, func_name) + if rc_mq_tag >= 0: + return self.Gen.CallGenCtor._translate_renum_construct( + pool, builder, mod, rc_mq_cls, func_name, + rc_mq_tag, cargs, can, trans) + + # async 裸名调用(TPV-M1/M2):普通函数调用 async 函数(如 task(5, 6))→ + # 生成 create_(args...) 调用(返回 Ptr(帧),bitcast 成 i8* 供变量存储)。 + # 对齐 TPC HandlesAsyncFunctions._HandleAsyncCreateCallLlvm。必须在裸名 + # 函数查找之前拦截——async 函数只注册了 create_/resume_,裸名 task 不在 + # 函数表,走普通路径会报"裸名调用未定义且未导入"。 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + if func_name is not None and trans is not None: + if self.Gen.AsyncGen.is_async_func(trans, func_name) != 0: + can_buf: str = pool.alloc(64) + if can_buf is not None: + viperlib.snprintf(can_buf, 64, "create_%s", func_name) + create_an: str = self.Gen.FuncGenBase._mangle_func_name( + trans, can_buf, 0) + create_fn_an: llvmlite.Function | t.CPtr = self.find_func_in_module( + mod, create_an) + if create_fn_an is not None: + frame_ty_an: llvmlite.LLVMType | t.CPtr = self.Gen.AsyncGen._lookup_coro_frame( + trans, func_name) + if frame_ty_an is not None: + ret_ptr_ty_an: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, frame_ty_an) + i8_ptr_ty_an: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + # M2:async 调用参数(task(5, 6) → create_task(5, 6)) + arg_head_an: llvmlite.Value | t.CPtr = None + arg_tail_an: llvmlite.Value | t.CPtr = None + arg_cnt_an: int = 0 + if cl.args is not None: + nb_an: t.CSizeT = len(cl.args) + j_an: t.CSizeT = 0 + for j_an in range(nb_an): + av_an: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, cl.args.get(j_an), + None, 0, trans) + if av_an is None: + av_an = llvmlite.const_int32(pool, 0) + av_an.Next = None + if arg_head_an is None: + arg_head_an = av_an + arg_tail_an = av_an + else: + arg_tail_an.Next = av_an + arg_tail_an = av_an + arg_cnt_an = arg_cnt_an + 1 + child_an: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, create_an, arg_head_an, arg_cnt_an, + ret_ptr_ty_an, 0) + if child_an is not None: + return llvmlite.build_bitcast(builder, child_an, i8_ptr_ty_an) + return llvmlite.const_int32(pool, 0) + + # 查找函数:优先从函数表按裸名精确匹配(同模块函数), + # 再从模块函数链表按后缀匹配(SHA1 前缀名)。 + # 注意:模块限定/属性调用(如 stdlib.free)不能做后缀匹配—— + # "free" 会误命中 viperio.Buf.free 等方法,导致普通 malloc 指针被 + # 当作 Buf 对象释放(Buf.free 读 self 字段/写 self.data), + # 传入句柄数组等指针时把句柄值当地址写入直接崩溃。 + # 后缀匹配仅用于裸名调用(cl.func 是 Name,如 _thread_alloc_free)。 + # stubs 在翻译后才注入,翻译时跨模块函数 found_func 通常为 None, + # 此时根据函数名推断 includes 库函数的返回类型(如 strchr 返回 i8*) + # 如果调用方未传 funcs_ptr/func_count(如 AnnAssign 路径),从 trans 获取 + actual_funcs: GenTypes.FuncEntry | t.CPtr = funcs_ptr + actual_func_count: int = func_count + if actual_funcs is None and trans is not None: + actual_funcs = trans._funcs + actual_func_count = trans._func_count + found_func: llvmlite.Function | t.CPtr = None + if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + # 裸名调用:允许精确 + 后缀匹配 + # 自定义装饰器 wrapper 重写:原函数名 → __decor_wrap_{name} + # (对齐 TPC DecoratorPass.run 的 _redirect_calls;wrapper 在函数 + # 翻译末尾生成,同 pass 内 main 调用点晚于被装饰函数定义,可达) + decor_wrap_nm: str = self.Gen.ClassGenBase.lookup_decor_wrapper(func_name) + if decor_wrap_nm is not None: + found_func = self.find_func_in_module(mod, decor_wrap_nm) + else: + found_func = self.find_func_in_module(mod, func_name) + # 治本修复(对齐 Attribute 路径 L6579-6613):后缀匹配误命中类方法 + # (SHA1.Class.method,第 2 段是已注册结构体类)必须拒绝——否则方法体内 + # 与 from-import 模块函数同名的裸名调用(如 LLVMModuleCore.SSAValue 内 + # 的 SSAValue(self.Pool, ...))绑定到本类方法 define → 无限递归 + # (栈溢出 0xC0000005)。裸名调用应优先解析为模块级函数 + # (同模块函数表 / from_imports),方法调用需显式 obj.method。 + if found_func is not None: + bn_name: str = llvmlite.function_get_name(found_func) + if bn_name is not None: + bn_dots: int = 0 + bni2: t.CSizeT = 0 + while bn_name[bni2] != '\0': + if bn_name[bni2] == '.': + bn_dots += 1 + bni2 += 1 + if bn_dots >= 2: + # 提取第 2 段(SHA1.Class.method → Class;SHA1.submod.func → submod) + bn_seg2: str = pool.alloc(64) + if bn_seg2 is not None: + bn_seg2[0] = '\0' + bsi2: t.CSizeT = 0 + while bn_name[bsi2] != '\0' and bsi2 < 17: + bsi2 += 1 + bse2: t.CSizeT = bsi2 + while bn_name[bse2] != '\0' and bn_name[bse2] != '.': + bse2 += 1 + bsl2: t.CSizeT = bse2 - bsi2 + if bsl2 > 0 and bsl2 < 63: + string.strncpy(bn_seg2, bn_name + bsi2, bsl2) + bn_seg2[bsl2] = '\0' + # 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径) + if self.Gen.StructGen.get_struct_type(bn_seg2) is not None: + found_func = None + else: + # 模块限定/属性调用:精确匹配优先,后缀匹配仅接受模块函数。 + # 后缀匹配命中的"方法"(SHA1.Class.method,Class 是已注册类)必须拒绝—— + # 短方法名(free/reset/close 等)会误命中其他模块的方法 + # (如 stdlib.free 命中 viperio.Buf.free,普通指针被当 Buf 释放崩溃; + # 且 atom.__atomic_test_and_set 若被误拒,返回类型从 i1 推断成 i32, + # 调用按 i32 读 i1 返回值读到垃圾高位,spinlock 恒非 0 卡死)。 + # 模块函数(SHA1.func)或子模块函数(SHA1.submod.func)的后缀匹配保留。 + found_func = self.find_func_in_module(mod, func_name) + if found_func is not None: + # 模块限定调用(mod.func,value 是已导入模块名):校验 found_func + # 的 SHA1 前缀必须等于该模块。否则 find_func_in_module 第 3 遍对 + # stub declare 的后缀匹配会误命中其他模块的同名函数(如 main 模块 + # 里 subprocess 的 declare 先注入,syslibtest_impl.run() 后缀匹配 + # 误命中 subprocess.run → call 到错误符号 → 参数错位解引用崩溃, + # SysLibTest 0xC0000005 根因)。误命中时拒绝(found_func=None), + # 落到下方跨模块模块限定解析用模块自身 SHA1 构造正确符号。 + xm_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if xm_at is not None and xm_at.value is not None and trans is not None \ + and trans._imported_modules is not None: + # 模块路径:单层 Name(syslibtest_impl.run)或嵌套 Attribute + # (os._win32.exists)。后者此前不校验 → os/__init__.vp 内部 + # 调 os._win32.exists 后缀匹配命中本模块同名 define → + # 无限递归栈溢出(SysLibTest 0xC00000FD 根因)。 + xm_mod_path: str = None + if xm_at.value.kind() == ast.ASTKind.Name: + xm_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xm_at.value) + if xm_nm.id is not None: + xm_mod_path = xm_nm.id + elif xm_at.value.kind() == ast.ASTKind.Attribute: + xm_mod_path = self.Gen.ExprGenAttr._attr_module_path_str( + pool, xm_at.value) + if xm_mod_path is not None: + if self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, xm_mod_path) != 0: + mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path) + if mod_sha1_chk is not None: + ff_nm2: str = llvmlite.function_get_name(found_func) + if ff_nm2 is not None: + if string.strncmp(ff_nm2, mod_sha1_chk, 16) != 0: + found_func = None + ff_name: str = llvmlite.function_get_name(found_func) + if ff_name is not None: + ff_dots: int = 0 + ffi2: t.CSizeT = 0 + while ff_name[ffi2] != '\0': + if ff_name[ffi2] == '.': + ff_dots += 1 + ffi2 += 1 + if ff_dots >= 2: + # 提取第 2 段(SHA1.Class.method → Class;SHA1.submod.func → submod) + seg2: str = pool.alloc(64) + if seg2 is not None: + seg2[0] = '\0' + si: t.CSizeT = 0 + while ff_name[si] != '\0' and si < 17: + si += 1 + seg_start: t.CSizeT = si + seg_end: t.CSizeT = si + while ff_name[seg_end] != '\0' and ff_name[seg_end] != '.': + seg_end += 1 + seg_len: t.CSizeT = seg_end - seg_start + if seg_len > 0 and seg_len < 63: + string.strncpy(seg2, ff_name + seg_start, seg_len) + seg2[seg_len] = '\0' + # 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径) + if self.Gen.StructGen.get_struct_type(seg2) is not None: + found_func = None + # C 库函数裸名调用支持:若模块中找不到且是已知 C 库函数,自动创建 declare + # 对应 TPC 的 _C_LIB_FUNCS 机制,支持 memset/memmove/memcmp/strcmp 等裸名调用 + if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + found_func = self.Gen.CallGenLib._ensure_c_lib_declare(pool, mod, func_name) + # builtins 自动关联:C 库未命中时,自动关联 builtins 模块的内置函数(ord/chr 等)。 + # builtins 已由编译链预加入(StubMerger 预加入列表)。PhaseA 期 builtins 尚未编译, + # 因此按约定签名创建 {builtins_sha1}.{name} 的 mangled declare(链接时解析到 + # builtins.vp 的真实定义);签名与 builtins.vp 保持一致:ord(i8)->i32, chr(i32)->i8。 + if found_func is None: + bi_sha1: str = self._lookup_module_sha1("builtins") + if bi_sha1 is not None: + bi_ret: llvmlite.LLVMType | t.CPtr = None + bi_p1: llvmlite.LLVMType | t.CPtr = None + if string.strcmp(func_name, "ord") == 0: + bi_ret = llvmlite.Int32(pool) + bi_p1 = llvmlite.Int8(pool) + elif string.strcmp(func_name, "chr") == 0: + bi_ret = llvmlite.Int8(pool) + bi_p1 = llvmlite.Int32(pool) + if bi_ret is not None: + bi_sym: str = func_name + if self.Gen.CallGenBase.is_cexport_func(bi_sha1, func_name) == 0: + bi_sym_len: t.CSizeT = string.strlen(bi_sha1) + string.strlen(func_name) + 2 + bi_sym_buf: str = pool.alloc(bi_sym_len) + if bi_sym_buf is not None: + viperlib.snprintf(bi_sym_buf, bi_sym_len, "%s.%s", bi_sha1, func_name) + bi_sym = bi_sym_buf + bi_decl: llvmlite.Function | t.CPtr = llvmlite.create_declare( + pool, mod, bi_sym, bi_ret) + if bi_decl is not None: + llvmlite.add_param(pool, bi_decl, bi_p1, "v") + found_func = bi_decl + # 使用访问器绕过 stub 类型限制 + found_ret_ty: llvmlite.LLVMType | t.CPtr = None + found_params: t.CPtr = None + if found_func is not None: + found_ret_ty = llvmlite.function_get_ret_ty(found_func) + found_params = llvmlite.function_get_params(found_func) + if found_ret_ty is not None: + call_ret_ty: llvmlite.LLVMType | t.CPtr = found_ret_ty + else: + # found_func=None(跨模块 stub 未注入):先从 vpi stub 返回注解 + # 缓存精确获取返回类型(含 tuple[T1,T2] → 匿名结构体,如 + # b2a_hqx -> tuple[str, t.CUnsignedShort] → {i8*, i16}), + # 未命中才回退 _infer_external_func_ret_ty 白名单推断。 + # 根因:_infer 白名单无 tuple 概念,未知函数默认 i32 会截断 + # 指针/i64;b2a_hqx 被推断为单值 i8*/i32 后调用侧返回值类型 + # 与定义侧 {i8*, i16} 不匹配 → HandlesAssign 的 tuple 解包 + # (依赖 rhs_val.Ty 是 Struct)静默跳过 → 目标变量从未赋值 → + # free 未初始化栈垃圾指针崩溃(HashLibTest hqx 段)。 + vpi_ret_ty: llvmlite.LLVMType | t.CPtr = None + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name: + mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value) + if mod_ret_nm.id is not None: + mod_ret_sha1: str = self._lookup_module_sha1(mod_ret_nm.id) + if mod_ret_sha1 is not None: + # 函数内懒 import:VpiReader imports HandlesExprCall, + # 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致) + VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir) + ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot( + mod_ret_sha1, None, func_name) + if ret_annot_fc is not None: + vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str( + pool, ret_annot_fc, + llvmlite.Int32(pool), + llvmlite.Ptr(pool, llvmlite.Int8(pool))) + if vpi_ret_ty is not None: + call_ret_ty = vpi_ret_ty + else: + call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name) + + # 如果模块中找不到函数,仅裸名调用(Name)才回退查局部变量(函数指针/ + # 闭包调用);模块属性调用(VLogger.fmt_buf())的目标是模块符号,绝不 + # 回退局部变量——跨模块任意同名可接受(CallGenLeafKit 局部 fmt_buf + # 曾劫持 VLogger.fmt_buf → 误走函数指针调用报 t.Callable 缺失), + # 仅 from-import 的裸名 yyy 与局部重名才可能冲突(该场景 func 是 Name)。 + if found_func is None: + fp_alloca: llvmlite.Value | t.CPtr = None + if cl.func is None or cl.func.kind() == ast.ASTKind.Name: + fp_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, func_name) + if fp_alloca is not None: + # 闭包变量(嵌套函数提升,已注册到闭包名集合):提取 fn_ptr/env_ptr 间接调用 + if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0: + return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, + cargs, can, trans) + # 函数指针变量(t.Callable 注解 → i8** alloca):按实参构造签名间接调用 + if self.Gen.CallGenLeafKit._is_func_ptr_alloca(fp_alloca) != 0: + # 区分 t.Callable(真函数指针)vs t.CPtr(闭包指针): + # t.CPtr 变量(如 f: t.CPtr = make_counter())存的是闭包结构 + # {fn_ptr, env_ptr},调用必须提取 env 传参;若按函数指针直接 + # call,跳转到闭包结构数据地址崩溃(ClosureCallTest 运行时崩溃)。 + ve_fp: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( + trans.SymTab, func_name) + is_cptr_annot_fp: int = 0 + if ve_fp is not None and ve_fp.AnnotClassName is not None: + if string.strstr(ve_fp.AnnotClassName, "CPtr") is not None: + is_cptr_annot_fp = 1 + if is_cptr_annot_fp == 0: + return self.Gen.CallGenLeafKit._translate_func_ptr_call(pool, builder, mod, fp_alloca, + func_name, cargs, can, trans) + return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, + cargs, can, trans) + # 其他 i8* 变量按闭包调用路径处理 + return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, + cargs, can, trans) + + # 翻译参数并链接 + # 有关键字参数且找到函数定义时,按参数名匹配 + if kw_count > 0 and found_func is not None and found_params is not None: + return self.Gen.CallGenLib._translate_call_with_kwargs( + pool, builder, mod, func_name, found_func, + cargs, can, keywords, kw_count, trans) + + head: llvmlite.Value | t.CPtr = None + tail: llvmlite.Value | t.CPtr = None + # memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染) + mc_dst2: llvmlite.Value | t.CPtr = None + mc_src2: llvmlite.Value | t.CPtr = None + mc_num2: llvmlite.Value | t.CPtr = None + for ai in range(can): + arg: ast.AST | t.CPtr = cargs.get(ai) + if arg is not None: + # 先取形参类型:单引号单字符字面量(CONST_CHAR)传给 str/指针 + # 形参时需物化 1 字符字符串(translate_constant 依据 target_ty + # 判定),否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→ + # strlen 访问低地址 ASan 崩溃(二代编 StringTest PyI 生成根因)。 + fp: llvmlite.Param | t.CPtr = None + fpty: llvmlite.LLVMType | t.CPtr = None + if found_func is not None: + fp = llvmlite.function_get_param_head(found_func) + fpi: int = 0 + while fp is not None and fpi < ai: + fp = llvmlite.param_get_next(fp) + fpi += 1 + if fp is not None: + fpty = llvmlite.param_get_ty(fp) + arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, arg, None, 0, trans, fpty) + if arg_val is not None: + # 参数类型提升:当 found_func 为 None(stub 未注入)时, + # 根据外部函数签名推断期望类型,提升字面量(如 i32→i64) + # 避免 declare(i64) 与 call(i32) 类型不匹配 + if found_func is None and arg_val.Ty is not None: + # 数组实参按 C 语义衰减为元素指针(对齐 TPC + # _decay_array_to_ptr),与 _translate_method_call 一致。 + if self.Gen.ExprGenBase.is_ptr_type(arg_val.Ty) != 0: + arr_pointee_fc: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee + if arr_pointee_fc is not None: + match arr_pointee_fc: + case llvmlite.LLVMType.Array(arr_et_fc, _): + decayed_fc: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_fc, arr_et_fc, arg_val, + llvmlite.const_int32(pool, 0)) + if decayed_fc is not None: + arg_val = decayed_fc + case _: + pass + # 已知变参 C 库函数(snprintf/sprintf/vsprintf)固定参数 + # 按签名 coerce:stub 未注入时跨模块签名不可见,但固定 + # 参数类型是 C ABI 硬约束(snprintf size 为 i64),不提升 + # 则 call(i32 1024) 与 declare(i64 size) 不匹配,且 Win64 + # 变参槽高 4 字节残留栈垃圾(clean 日志 0x7ff700000052 根因)。 + kvc_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenVa._get_known_vararg_cfunc_param_ty( + pool, func_name, ai) + if kvc_ty is not None: + arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, kvc_ty, arg) + ext_param_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._get_external_func_param_ty( + pool, func_name, ai) + if ext_param_ty is not None: + val_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty) + tgt_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(ext_param_ty) + if val_bits_ap != 0 and tgt_bits_ap != 0 and val_bits_ap != tgt_bits_ap: + if val_bits_ap == 1 and val_bits_ap < tgt_bits_ap: + arg_val = llvmlite.build_zext(builder, arg_val, ext_param_ty) + else: + arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, ext_param_ty, arg) + elif found_func is not None: + # found_func 存在:按形参类型 coerce(如 stdlib.malloc(size: t.CSizeT)) + # 修复: CDefine/i32 常量传给 i64 形参未提升,导致 call(i32) 与 define(i64) 不匹配 + if fp is not None: + if fpty is not None: + arg_val = self.Gen.ExprGenBase.coerce_to_type( + builder, arg_val, fpty, arg) + elif found_func.IsVarArg == 1: + # Win64 变参 8 字节槽位:<64 位整数按 i64 传(sext 符号扩展), + # f32 提升为 f64。若按原窄类型传,调用点只写 4 字节进 8 字节槽, + # 高 4 字节残留旧栈数据,va_arg(i64) 读到垃圾(如 DefineTest + # PAGE=%d 读到 0x400921FB00001000,高位是 PI 残留)。 + # 对齐 TPC FuncGen/Python-llvmlite 的 ABI 自动提升。 + ab_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty) + af_va: int = self.Gen.ExprGenBase.get_llvm_float_bits(arg_val.Ty) + i64_va: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + if ab_va != 0 and ab_va < 64: + arg_val = llvmlite.build_sext(builder, arg_val, i64_va) + elif af_va == 32: + arg_val = llvmlite.build_fpext( + builder, arg_val, llvmlite.Double(pool)) + arg_val.Next = None + if head is None: + head = arg_val + else: + tail.Next = arg_val + tail = arg_val + # memcpy 专用: 保存前三个参数的独立引用 + if ai == 0: + mc_dst2 = arg_val + elif ai == 1: + mc_src2 = arg_val + elif ai == 2: + mc_num2 = arg_val + + # 默认参数填充:当实参数量 < 形参数量时,用默认值填充缺失参数 + # defaults 数组右对齐到 args:defaults[0] 对应 args[offset],offset = param_count - default_count + if can > 0 or (can == 0 and cargs is not None): + df_defaults: t.CVoid | t.CPtr = None + df_default_count: int = 0 + df_param_count: int = 0 + # 先从当前模块函数表查找 FuncEntry + if trans is not None and trans._funcs is not None: + df_entry: GenTypes.FuncEntry | t.CPtr = ExprCallTable.find_func_entry_in_table( + trans._funcs, trans._func_count, func_name) + if df_entry is not None: + df_defaults = df_entry.Defaults + df_default_count = df_entry.DefaultCount + df_param_count = df_entry.ParamCount + # 无条件查询全局默认参数表(跨模块/同模块都要用 stdlib 常量快照 + # 重建默认值:模块表 Defaults 指向 pool 内 AST,跨 1b reset 后悬空; + # 快照才是跨文件永久存活。同模块命中时 gd_entry 也必须已赋值, + # 否则下方 _build_const_from_snapshot 读到未初始化栈垃圾指针崩溃。 + # 与 _translate_method_call 的 mdf_gd 无条件查询(L2677)保持一致。 + gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(func_name) + if df_defaults is None and gd_entry is not None: + df_defaults = gd_entry.Defaults + df_default_count = gd_entry.DefaultCount + df_param_count = gd_entry.ParamCount + # 填充缺失参数 + if df_defaults is not None and df_default_count > 0 and can < df_param_count: + df_offset: int = df_param_count - df_default_count + for df_di in range(df_default_count): + df_param_idx: int = df_offset + df_di + if df_param_idx >= can: + # 优先用 stdlib 常量快照重建(gd_entry.Defaults 的 pool 内 AST 可能已被 reset 回收) + df_arg_val: llvmlite.Value | t.CPtr = None + if gd_entry is not None: + df_arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot( + pool, mod, gd_entry, df_di, trans, builder) + if df_arg_val is None: + df_defaults_list: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(df_defaults), t.CPtr)) + df_default_node: ast.AST | t.CPtr = df_defaults_list.get(df_di) + if df_default_node is not None: + df_arg_val = self.Gen.ExprGen.translate_value( + builder, pool, mod, df_default_node, None, 0, trans) + if df_arg_val is not None: + df_arg_val.Next = None + if head is None: + head = df_arg_val + else: + tail.Next = df_arg_val + tail = df_arg_val + elif found_func is not None: + # 默认参数缺失/表信息不全时的零值兜底: + # 1) df_param_count>0 但 df_defaults 为空(函数表有参数数、无默认值记录) + # → 现有 if 不满足(要求 df_defaults 非 None 且 default_count>0), + # 缺参调用按 x64 约定读 [rsp+0x28] 栈参数区垃圾 → ASan 崩溃。 + # 2) 调用方文件先于定义文件编译(编译顺序问题,如 Phase1Main 调 + # StubMergerBuild._BuildReachableSha1Set 缺 app_deps), + # find_func_entry_in_table/find_func_defaults 均查不到(df_param_count=0) + # → 同样缺参崩溃。 + # 两种情况都用 found_func 实际参数数兜底,缺参填零值 + # (对齐 TPC _fill_default_args 的零值兜底语义;found_func 是 stub + # declare,参数数与 define 一致,按它填充安全)。 + # 注意排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__): + # 含 raise 的函数定义尾部注入 2 个 EH 形参,若计入 fc_cnt 会为 + # raise_disk() 这类无参调用补 2 个 zeroinitializer 占位,随后 + # _emit_eh_guarded_call 又追加真实 EH 实参 → call 4 参 vs define + # 2 参 → x64 ABI 错位 → 函数写 null 输出指针 → 0xC0000005 + # (TestFBProject raise_disk 崩溃根因)。 + fc_cnt: int = 0 + fc_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + while fc_p is not None: + fp_nm_z: str = llvmlite.param_get_name(fc_p) + if fp_nm_z is None or (string.strcmp(fp_nm_z, "%__eh_msg_out__") != 0 + and string.strcmp(fp_nm_z, "%__eh_code_out__") != 0): + fc_cnt += 1 + fc_p = llvmlite.param_get_next(fc_p) + if fc_cnt == 0: + fc_cnt = df_param_count if df_param_count > 0 else 0 + if can < fc_cnt: + fp_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + fpi_z: int = 0 + while fp_z is not None and fpi_z < can: + fp_z = llvmlite.param_get_next(fp_z) + fpi_z += 1 + while fp_z is not None and fpi_z < fc_cnt: + zero_ty_z: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(fp_z) + zero_val_z: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, zero_ty_z) + if zero_val_z is not None: + zero_val_z.Next = None + if head is None: + head = zero_val_z + else: + tail.Next = zero_val_z + tail = zero_val_z + fp_z = llvmlite.param_get_next(fp_z) + fpi_z += 1 + + # 使用模块中的实际函数名(可能带 SHA1 前缀,如 "83210a72e829a086.atoi") + # t.CExtern/t.State/t.CExport 函数不加 SHA1 前缀,使用裸名 + # declare 和 define 统一用 found_func.Name: + # - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name) + # - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名 + # 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀, + # 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference + call_name: str = func_name + # 原始函数名(别名导入时记录,如 from X import parse as json_parse → orig_func_name="parse") + # 用于在别名处理后重新推断返回类型,避免 _infer_external_func_ret_ty 用别名匹配失败 + orig_func_name: str = None + if found_func is not None: + found_name: str = llvmlite.function_get_name(found_func) + if found_name is not None: + call_name = found_name + else: + # 跨模块函数调用:found_func 为 None 时,检查是否是模块限定调用 + # 如 string.strchr(...) → @{sha1_of_string}.strchr + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + xmod_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if xmod_at.value is not None and (xmod_at.value.kind() == ast.ASTKind.Name + or xmod_at.value.kind() == ast.ASTKind.Attribute): + # 模块路径:单层 Name(string.strchr)或嵌套 Attribute + # (os._win32.exists)。嵌套路径此前不支持 → os/__init__.vp + # 内部调 os._win32.exists 只能被 find_func_in_module 后缀匹配 + # 命中本模块同名 define(无限递归),或找不到模块路径而走 + # 错误 fallback;此处用完整模块路径解析正确 SHA1(与校验段 + # L7344 对齐,SysLibTest 0xC00000FD 根因)。 + xmod_path: str = None + if xmod_at.value.kind() == ast.ASTKind.Name: + xmod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xmod_at.value) + if xmod_nm.id is not None: + xmod_path = xmod_nm.id + else: + xmod_path = self.Gen.ExprGenAttr._attr_module_path_str(pool, xmod_at.value) + if xmod_path is not None: + mod_sha1: str = self._lookup_module_sha1(xmod_path) + if mod_sha1 is not None: + # fast-fail: 模块限定调用要求模块已被当前文件 import。 + # 全局 SHA1 存储包含所有被扫描的 includes,未导入的模块 + # (如 this.this() 但 import this 被注释)也会命中存储, + # 静默生成 @{sha1}.func 调用而非报错——违反"找不到 fast-fail"原则。 + is_xmod_imported: int = 1 + if trans is not None and trans._imported_modules is not None: + is_xmod_imported = self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, xmod_path) + if is_xmod_imported == 0: + err_ni: str = pool.alloc(256) + if err_ni is not None: + viperlib.snprintf(err_ni, 256, + "跨模块调用 '%s.%s':模块 '%s' 未导入(请先 import %s)", + xmod_path, func_name, xmod_path, xmod_path) + self.Gen.TypeGenErr.fatal_error(xmod_at, err_ni) + self.Gen.TypeGenErr.fatal_error(xmod_at, "跨模块调用:模块未导入") + # 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer) + reexport_src: str = self.Gen.ImportsGen.lookup_reexport(mod_sha1, func_name) + if reexport_src is None: + # re-export 表未命中(includes 模块 stub/text 已存在时 + # 不被按需全量翻译,表只在 declare_only=0 时注册,可能为空): + # 按需解析该模块 .vpi 的 from-import 定位定义模块 + reexport_src = self._lookup_reexport_from_vpi(pool, mod_sha1, func_name) + if reexport_src is not None: + reexport_sha1: str = self._lookup_module_sha1(reexport_src) + if reexport_sha1 is not None: + mod_sha1 = reexport_sha1 + # CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func + if self.Gen.CallGenBase.is_cexport_func(mod_sha1, func_name) != 0: + call_name = func_name + elif self._is_state_func_from_vpi(pool, mod_sha1, func_name) != 0: + # t.State 标记的外部 C 函数(如 CPython PyList_Size): + # 定义端用裸名 @PyList_Size(链接外部库),跨模块调用 + # 必须用裸名——带 sha1 前缀 → ld.lld undefined symbol + call_name = func_name + else: + mangled_len: t.CSizeT = string.strlen(mod_sha1) + string.strlen(func_name) + 2 + mangled: str = pool.alloc(mangled_len) + if mangled is not None: + viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name) + call_name = mangled + else: + # t 和 c 是内部库,豁免 SHA1 查找:使用裸名调用 + if string.strcmp(xmod_path, "t") == 0 or string.strcmp(xmod_path, "c") == 0: + call_name = func_name + else: + # Bug 修复: 先检查是否是已导入的模块名(如 BuildPipeline.run_pipeline) + # 如果是已导入模块但 SHA1 未注册,说明该模块未被编译(如 Phase2 未扫描到), + # 应该立即报错,而不是错误地走 from_imports 查找(会被 star import 回退误导) + is_imported_mod: int = 0 + if trans is not None and trans._imported_modules is not None: + is_imported_mod = self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, xmod_path) + if is_imported_mod != 0: + # 别名导入(import X as Y):Y 在 _imported_modules 中, + # 但 _lookup_module_sha1("Y") 精确匹配失败(mod_arr 只存文件名)。 + # 先尝试 from_imports 查找别名→完整模块名→后缀匹配 SHA1。 + # allow_star_fallback=0 避免被 star import 回退误导。 + alias_sha1: str = None + if trans is not None and trans._from_imports is not None: + alias_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0) + if alias_sha1 is not None: + if self.Gen.CallGenBase.is_cexport_func(alias_sha1, func_name) != 0: + call_name = func_name + elif self._is_state_func_from_vpi(pool, alias_sha1, func_name) != 0: + # t.State 外部 C 函数:裸名(同主路径 L7918) + call_name = func_name + else: + AliasMangledLen: t.CSizeT = string.strlen(alias_sha1) + string.strlen(func_name) + 2 + alias_mangled: str = pool.alloc(AliasMangledLen) + if alias_mangled is not None: + viperlib.snprintf(alias_mangled, AliasMangledLen, "%s.%s", alias_sha1, func_name) + call_name = alias_mangled + else: + # 已导入模块但 SHA1 未注册:该模块未被编译,立即报错 + err_msg_imp: str = pool.alloc(256) + if err_msg_imp is not None: + viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_path) + self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg_imp) + self.Gen.TypeGenErr.fatal_error(xmod_at, "已导入模块 SHA1 未注册") + else: + # 检查是否是通过 from import 导入的名称(如 from .__types import LLVMType) + # LLVMType.Ptr(ty) 中 LLVMType 是类名不是模块名, + # 但 Ptr 是 __types 模块的模块级函数 + # 禁用 star import 回退:xmod_path 是明确名称,不应回退到 star import 模块 + from_sha1: str = None + if trans is not None and trans._from_imports is not None: + from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0) + if from_sha1 is not None: + # CExport 函数定义用裸名,跨模块调用需用裸名 + if self.Gen.CallGenBase.is_cexport_func(from_sha1, func_name) != 0: + call_name = func_name + else: + fmangled_len: t.CSizeT = string.strlen(from_sha1) + string.strlen(func_name) + 2 + fmangled: str = pool.alloc(fmangled_len) + if fmangled is not None: + viperlib.snprintf(fmangled, fmangled_len, "%s.%s", from_sha1, func_name) + call_name = fmangled + else: + err_msg2: str = pool.alloc(256) + if err_msg2 is not None: + viperlib.snprintf(err_msg2, 256, "跨模块调用 '%s.%s' 找不到模块 SHA1", xmod_path, func_name) + self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg2) + self.Gen.TypeGenErr.fatal_error(xmod_at, "跨模块调用找不到模块 SHA1") + else: + # 裸名调用(非 Attribute):检查 from_imports + # 治本修复:函数调用禁用 star import 回退(allow_star_fallback=0)。 + # star import(from stdint import *)本意是导入类型/常量(如 BYTEPTR),而非函数; + # 函数应显式 from import。漏导函数若走 star 回退会被错归到 star 模块,生成错误 SHA1 符号 + # (如 block_insert_text_before_terminator 漏导后被错归 stdint,生成 + # f5522571bcce7bcb.block_insert... 而非 874bac141c97bae9.block_insert...)。 + # 禁用回退后,漏导函数编译期即报"未定义且未导入"(fast-fail),优于链接期 undefined reference。 + if trans is not None and trans._from_imports is not None: + bare_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) + if bare_sha1 is not None: + # 别名处理: from X import Y as Z → 调用 Z 时需用 Y 作为源模块函数名 + # 例如 from json.__parser import parse as json_parse → 修饰为 SHA1.parse 而非 SHA1.json_parse + bare_orig: str = self.Gen.ImportsGen.lookup_from_import_original( + trans._from_imports, func_name) + bare_func_name: str = func_name + if bare_orig is not None: + # bare_orig 指向 from_imports 内部缓冲区,未 null 终止 + # 需复制到独立缓冲区(截断于空格或 null) + bo_len: t.CSizeT = 0 + while bare_orig[bo_len] != ' ' and bare_orig[bo_len] != '\0': + bo_len += 1 + bo_buf: str = pool.alloc(bo_len + 1) + if bo_buf is not None: + for bo_i in range(bo_len): + bo_buf[bo_i] = bare_orig[bo_i] + bo_buf[bo_len] = '\0' + bare_func_name = bo_buf + # 记录原始函数名,供后续重新推断返回类型 + # 例如 from json.__parser import parse as json_parse + # func_name="json_parse", bare_func_name="parse" + # _infer_external_func_ret_ty 用 "parse" 能正确匹配,用 "json_parse" 则返回默认 i32 + orig_func_name = bare_func_name + # re-export 解析(对齐模块限定路径 L6598): + # 裸名 from import 的函数可能实际定义在子模块(re-export)。 + # 例:from ast import dump → dump 定义在 ast.base(sha1=71b2), + # ast/__init__(sha1=876e)只是 re-export。若沿用包 sha1 构造 + # call_name="876e.dump",text.ll/vpi 返回类型解析均未命中, + # fail-fast 报"返回类型无法精确获取";需重定向到定义模块 SHA1。 + reexport_src2: str = self.Gen.ImportsGen.lookup_reexport(bare_sha1, bare_func_name) + if reexport_src2 is None: + # re-export 表未命中:按需解析该模块 .vpi 的 from-import + reexport_src2 = self._lookup_reexport_from_vpi(pool, bare_sha1, bare_func_name) + if reexport_src2 is not None: + reexport_sha1_2: str = self._lookup_module_sha1(reexport_src2) + if reexport_sha1_2 is not None: + bare_sha1 = reexport_sha1_2 + if self.Gen.CallGenBase.is_cexport_func(bare_sha1, bare_func_name) != 0: + call_name = bare_func_name + else: + bare_mangled_len: t.CSizeT = string.strlen(bare_sha1) + string.strlen(bare_func_name) + 2 + bare_mangled: str = pool.alloc(bare_mangled_len) + if bare_mangled is not None: + viperlib.snprintf(bare_mangled, bare_mangled_len, "%s.%s", bare_sha1, bare_func_name) + call_name = bare_mangled + else: + err_msg3: str = pool.alloc(256) + if err_msg3 is not None: + viperlib.snprintf(err_msg3, 256, "裸名调用 '%s' 未定义且未导入", func_name) + self.Gen.TypeGenErr.fatal_error(node, err_msg3) + self.Gen.TypeGenErr.fatal_error(node, "裸名调用未定义且未导入") + else: + err_msg4: str = pool.alloc(256) + if err_msg4 is not None: + viperlib.snprintf(err_msg4, 256, "裸名调用 '%s' 未定义且未导入", func_name) + self.Gen.TypeGenErr.fatal_error(node, err_msg4) + self.Gen.TypeGenErr.fatal_error(node, "裸名调用未定义且未导入") + + # 直接调用 + # memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失) + if string.strcmp(func_name, "memcpy") == 0 and can >= 3: + return self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2) + # 别名导入重新推断返回类型: + # from X import parse as json_parse → func_name="json_parse" 时 _infer_external_func_ret_ty 返回默认 i32 + # 用 orig_func_name="parse" 重新推断,能正确匹配返回 i8*(JsonValue*) + # 避免指针被截断为 i32 后 inttoptr 丢失高 32 位导致崩溃 + if orig_func_name is not None and orig_func_name != func_name: + re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name) + if re_inferred_ty is not None: + call_ret_ty = re_inferred_ty + # ============================================================ + # 跨模块 Viper 函数返回类型精确解析(对齐方法路径,fail-fast) + # 根因:_infer_external_func_ret_ty 白名单未覆盖返回指针的模块函数 + # (new_lexer→Lexer* / tokenize→Token* / parse_tokens→AST*),兜底 i32 + # 导致 64 位指针被截断高 32 位 → inttoptr 无效指针 → 解引用崩溃 + # (AstTest 的 _lexer_init 崩溃链)。declare 也可能是错误 i32(_ensure_method_declare + # 曾用兜底类型创建),因此不信任 found_func 的 declare 返回类型。 + # 权威来源:text.ll define;次选:vpi stub 返回注解;均未命中立即终止。 + # 仅对 mangled("{16位hex}.func")调用生效;C 库/Win32 裸名走原白名单。 + # Phase 1a(declare_only)期间依赖 text.ll 未生成,保持原白名单行为不 fail-fast。 + # ============================================================ + if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) != 0: + if trans is None or trans._declare_only == 0: + cf_dot: str = string.strchr(call_name, '.') + cf_last: str = string.strrchr(call_name, '.') + if cf_dot is not None and cf_last is not None: + cf_sha1: str = stdlib.malloc(17) + cf_bare_len: t.CSizeT = string.strlen(call_name) - \ + (t.CSizeT(t.CUInt64T(cf_last) - t.CUInt64T(call_name))) - 1 + cf_bare: str = stdlib.malloc(cf_bare_len + 1) + if cf_sha1 is not None and cf_bare is not None: + string.strncpy(cf_sha1, call_name, 16) + cf_sha1[16] = '\0' + string.strcpy(cf_bare, cf_last + 1) + # 本模块自身函数(泛型特化等):从当前 IR 已声明的函数 + # 取返回类型;不走跨模块 text.ll 查找——自己的 text.ll + # 在 Phase A 翻译中尚未生成,直接查会误报"依赖未生成" + # 并保持默认类型(GenericTest 泛型 add/combine 等警告 + # 根因)。未命中(特化尚未声明)保持默认推断。 + is_local_mod: int = 0 + if trans is not None and trans.ModuleSha1 is not None: + if string.strncmp(cf_sha1, trans.ModuleSha1, 16) == 0: + is_local_mod = 1 + if is_local_mod != 0: + local_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name) + if local_fn is not None and local_fn.RetTy is not None: + call_ret_ty = local_fn.RetTy + else: + cf_ret: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + pool, cf_sha1, None, cf_bare, 1) + if cf_ret is not None: + call_ret_ty = cf_ret + else: + cf_vpi: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( + pool, cf_sha1, None, cf_bare) + if cf_vpi is not None: + call_ret_ty = cf_vpi + else: + # 区分两种失败场景: + # 1. 依赖模块 text.ll 存在但函数未命中 → 真错误,fail-fast + # 2. 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按 + # 文件顺序翻译,main.vp 先于其 import 的 mbuddytest.vp + # (同项目多源文件)。此时标记 trans._deps_pending=1, + # 保持白名单类型继续,Phase A 多遍循环下遍重译—— + # 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。 + if self._dep_text_ll_exists(pool, cf_sha1) != 0: + cf_err: str = VLogger.fmt_buf() + if cf_err is not None: + viperlib.snprintf( + cf_err, 1024, + "跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中)," + "默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name) + VLogger.error(cf_err, "HandlesExprCall") + self.Gen.TypeGenErr.fatal_error( + node, + f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}", + "TypeError") + else: + if trans is not None: + trans._deps_pending = 1 + # [DEPDBG] 临时诊断:跨模块函数依赖未就绪即 pending 的根因(用后移除) + dbg_ms5: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" + stdio.printf("[DEPDBG] pending(cross_fn): mod=%s call=%s sha1=%s text=%d\n", + dbg_ms5, call_name, + cf_sha1 if cf_sha1 is not None else "none", + self._dep_text_ll_exists(pool, cf_sha1) if cf_sha1 is not None else -1) + cf_dbg: str = VLogger.fmt_buf() + if cf_dbg is not None: + viperlib.snprintf( + cf_dbg, 1024, + "跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序)," + "标记重试,本遍保持默认类型", call_name) + if VLogger.nowarn_register() == 0: + VLogger.warning(cf_dbg, "HandlesExprCall") + stdlib.free(cf_sha1) + stdlib.free(cf_bare) + # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value + self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can) + # 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升 + # (cargs 为本函数参数 AST 链表,self_offset=0 普通函数调用) + is_variadic_call: int = self.Gen.CallGenVa._check_and_promote_variadic_call( + pool, builder, mod, call_name, head, can, cargs, 0, trans) + # try-except:被调函数带 EH 输出参数时,追加输出实参 + 调用后检测异常 + return self.Gen.CallGenLeafKit._emit_eh_guarded_call( + builder, pool, mod, trans, found_func, call_name, head, can, + call_ret_ty, is_variadic_call) + + + + + + + + + + + + +