- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport / ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份 口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本 - 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1): _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute 分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增 _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争 - VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty 原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot register Translator) - 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、 [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin) 根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec (TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结 (CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
1595 lines
119 KiB
Plaintext
1595 lines
119 KiB
Plaintext
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 viperlib
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||
|
||
from . import CallGenBase
|
||
import lib.core.Gen.GenApi as GenApi
|
||
|
||
|
||
# ============================================================
|
||
# CallGenMixin - 调用分派 Mixin(拆分后顶层,阶段 A:底层已拆至
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
|
||
# ============================================================
|
||
define _CEXPORT_MAX = 256
|
||
define ASM_MAX_OPERANDS = 16
|
||
define ASM_TEXT_BUF_SIZE = 2048
|
||
define ASM_CONST_BUF_SIZE = 512
|
||
define ASM_ARGS_BUF_SIZE = 512
|
||
define ASM_LINE_BUF_SIZE = 4096
|
||
define LLVMIR_MAX_OPS = 16
|
||
define LLVMIR_TEMPLATE_BUF = 512
|
||
|
||
|
||
# ============================================================
|
||
# CallGenMixin - 调用分派中枢(顶层,继承 LeafKit)
|
||
# ============================================================
|
||
novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||
"""调用分派 Mixin(拆分后顶层,直接继承 CallGenBase——TPV self 调用仅支持 1 级父类)"""
|
||
|
||
# ============================================================
|
||
# _CheckStaticCallCrossModule - static 符号跨模块**调用**拦截(`mod.fn(...)`)
|
||
#
|
||
# 命中别的模块登记的 static 函数即 fail-fast。放在 translate_call 入口按 AST
|
||
# 形态统一拦截:后续分派有 found_func 命中 / vpi 解析 / 裸名兜底多条分支,
|
||
# 只在某一分支检查会漏。基座不是已导入模块(self.method / obj.method /
|
||
# Class.static_method)时放行;同模块自引用(sha1 相同)放行。
|
||
# 独立成函数:translate_call 极大,内联该检查会干扰其类型推断。
|
||
# ============================================================
|
||
def _CheckStaticCallCrossModule(self, pool: memhub.MemBuddy*,
|
||
trans: HT.Translator*,
|
||
cl: ast.Call*) -> int:
|
||
if cl is None or cl.func is None or trans is None:
|
||
return 0
|
||
if cl.func.kind() != ast.ASTKind.Attribute:
|
||
return 0
|
||
if trans.ModuleSha1 is None or trans._imported_modules is None:
|
||
return 0
|
||
# 快速短路:项目内无 static 符号时零开销(不查 sha1、不扫表)
|
||
if self.Gen.CallGenBase.StaticCount <= 0:
|
||
return 0
|
||
sc_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if sc_at.value is None or sc_at.attr is None:
|
||
return 0
|
||
sc_mod: str = None
|
||
if sc_at.value.kind() == ast.ASTKind.Name:
|
||
sc_nm: ast.Name* = ast.Name*(sc_at.value)
|
||
sc_mod = sc_nm.id
|
||
elif sc_at.value.kind() == ast.ASTKind.Attribute:
|
||
sc_mod = self.Gen.ExprGenAttr._attr_module_path_str(pool, sc_at.value)
|
||
if sc_mod is None:
|
||
return 0
|
||
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, sc_mod) == 0:
|
||
return 0
|
||
sc_sha1: str = self.Gen.StructGen._resolve_module_sha1(sc_mod, trans._from_imports)
|
||
if sc_sha1 is None:
|
||
return 0
|
||
if string.strncmp(sc_sha1, trans.ModuleSha1, 16) == 0:
|
||
return 0
|
||
if self.Gen.CallGenBase.IsStaticSymbol(sc_sha1, sc_at.attr) == 0:
|
||
return 0
|
||
self.Gen.TypeGenErr.fatal_error(cl,
|
||
f"'{sc_mod}.{sc_at.attr}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||
"NameError")
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 翻译函数调用 Call(func, args) — 模块级版本
|
||
# ============================================================
|
||
def translate_call(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译函数调用,返回 call 结果 Value"""
|
||
cl: ast.Call* = ast.Call*(node)
|
||
# static 符号(禁止跨模块使用):`mod.fn(...)` 命中别的模块登记的 static 函数
|
||
# 即 fail-fast。逻辑收敛在 _CheckStaticCallCrossModule 内——本函数极大,
|
||
# 内联该检查(新增分支 + 跨函数调用)会干扰其类型推断。
|
||
self._CheckStaticCallCrossModule(pool, trans, cl)
|
||
if cl is None: return None
|
||
|
||
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
|
||
|
||
cargs: list[ast.AST*]* = cl.args
|
||
can: sizet = 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* = 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* = ast.Subscript*(cl.func)
|
||
if sub_node.value is not None and sub_node.value.kind() == ast.ASTKind.Name:
|
||
gen_nm_node: ast.Name* = ast.Name*(sub_node.value)
|
||
gen_class_name: str = gen_nm_node.id
|
||
# 检查是否是泛型类模板
|
||
template_cd: ast.ClassDef* = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name)
|
||
if template_cd is not None:
|
||
# 提取类型实参
|
||
gen_type_args: list[str]* = 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__ 方法名后缀)
|
||
# 末参传模板名:关键字实参需按 __init__ 形参名定位槽位
|
||
return self.Gen.CallGenCtor._translate_generic_ctor_call(pool, builder, mod, spec_name, cl, trans, gen_class_name)
|
||
else:
|
||
# 泛型类模板未找到:可能是泛型函数手动传类型参数调用 xxx[T](x)
|
||
# (对应 TPC _HandleGenericCallExplicitLlvm;TPV 无泛型函数特化,
|
||
# 泛型函数按普通函数编译,类型参数 [T] 仅在函数定义处消费)。
|
||
# 检查当前模块是否已定义同名函数(def double[T] 编译后即普通函数
|
||
# double),若是则丢弃 [int] 显式类型参数,退回普通函数调用。
|
||
if self.find_func_in_module(mod, gen_class_name) is not None:
|
||
func_name = gen_class_name
|
||
else:
|
||
# 内建指针/类型下标 cast:(rawptr[list[int]])(lst)、
|
||
# (t.CPtr[T])(x)、(t.CArray[i8, N])(x) —— rawptr/CPtr/
|
||
# CArray 不是泛型类模板,是类型注解下标。先按注解解析出
|
||
# 类型再单参转换(ast/base.vp L392 ops_list 行根因)。
|
||
if cl.args is not None and len(cl.args) == 1:
|
||
sub_cast_ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
|
||
if sub_cast_ty is not None:
|
||
sub_cast_arg: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), funcs_ptr, func_count, trans)
|
||
if sub_cast_arg is not None:
|
||
return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, sub_cast_arg, sub_cast_ty,
|
||
cl.args.get(0), trans)
|
||
# 泛型类模板未找到:可能未 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* = 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*]* = cl.args
|
||
if binop_args is not None and len(binop_args) == 1:
|
||
binop_arg_val: llvmlite.Value* = 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* = 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* = 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* = 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* = 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* = 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*]* = cl.keywords
|
||
kw_count: sizet = 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* = None
|
||
gen_ctor_entry: GenTypes.StructEntry* = None
|
||
# 模块限定构造 Module.Class(...) 优先:按模块名解析 SHA1 精确命中类。
|
||
# 必须在裸名可见性分支之前——跨模块同名类(VKernel gfx 与 App gfx 都
|
||
# 定义 Scene3D)存在时,裸名 find_struct 返回先注册的错误 entry
|
||
# (0ae → ctor 生成 @0ae.Scene3D.__before_init__ → 链接 undefined
|
||
# symbol,Scene3D 根因)。模块解析成功即锁定,跳过下方裸名回退。
|
||
mod_qual_done: int = 0
|
||
if trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
at_qc0: ast.Attribute* = ast.Attribute*(cl.func)
|
||
mod_path0: str = None
|
||
if at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Name:
|
||
mod_nm0: ast.Name* = ast.Name*(at_qc0.value)
|
||
if mod_nm0.id is not None:
|
||
mod_path0 = mod_nm0.id
|
||
elif at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Attribute:
|
||
mod_path0 = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc0.value)
|
||
if mod_path0 is not None:
|
||
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path0) != 0:
|
||
mod_sha1_0: str = self._lookup_module_sha1(mod_path0)
|
||
if mod_sha1_0 is not None:
|
||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_0)
|
||
mod_qual_done = 1
|
||
vis: int = self.Gen.StructGen.is_struct_visible(func_name)
|
||
if mod_qual_done == 0 and 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* = ast.Attribute*(cl.func)
|
||
if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name:
|
||
mod_nm_qc: ast.Name* = ast.Name*(at_qc.value)
|
||
imp_ok: int = self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_nm_qc.id)
|
||
if imp_ok != 0:
|
||
# 模块限定构造 Module.Class(...):按模块名解析 SHA1 精确命中类。
|
||
# 不能用 trans.ModuleSha1(那是当前文件 sha1,类在 gfx 模块)——
|
||
# find_struct_by_module 落空后裸名回退 find_struct 会命中跨模块
|
||
# 同名类中先注册的 entry(VKernel gfx 与 App gfx 都定义 Scene3D,
|
||
# 0ae 先注册 → ctor 生成 @0ae.Scene3D.__before_init__ → 链接
|
||
# undefined symbol,Scene3D 根因)。
|
||
mod_sha1_qc2: str = None
|
||
if mod_nm_qc.id is not None:
|
||
mod_sha1_qc2 = self._lookup_module_sha1(mod_nm_qc.id)
|
||
if mod_sha1_qc2 is not None:
|
||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_qc2)
|
||
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: sizet = 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:
|
||
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, sizet(0), trans, cl.IsVolatile)
|
||
# @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, sizet(0), trans, cl.IsVolatile)
|
||
# 普通结构体:检查是否有默认值(用 entry 直接访问)
|
||
gen_has_defaults: int = 0
|
||
if gen_ctor_entry is not None:
|
||
for gdi in range(gen_ctor_entry.FieldCount):
|
||
gdf: GenTypes.FieldEntry* = 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, sizet(0), trans, cl.IsVolatile)
|
||
# 无默认值:返回 zeroinitializer
|
||
ret: llvmlite.Value* = 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, cl.IsVolatile)
|
||
return self.Gen.CallGenCtor._translate_struct_ctor(pool, builder, mod, struct_ty, func_name, cargs, can, trans, cl.IsVolatile)
|
||
|
||
# 裸名泛型类模板构造: 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* = ast.Attribute*(cl.func)
|
||
if at_gb.value is not None and at_gb.value.kind() == ast.ASTKind.Name:
|
||
am_gb: ast.Name* = ast.Name*(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* = 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]* = template_cd_bare.type_params
|
||
tp_count_bare: sizet = len(tp_bare)
|
||
if tp_count_bare > 0:
|
||
inferred_args_bare: list[str]* = list[str](pool, 8)
|
||
if inferred_args_bare is not None:
|
||
# 类型形参 → 位置实参对齐:取 __init__ 签名中注解为该类型形参的形参位次(Vector[T].__init__(self, pool, capacity, _hint: T) ⇒ T 对齐第 3 个位置实参)。不能按「类型形参序号 = 实参序号」硬对齐(第 1 个实参常是 pool/size 等非 T 参数);签名不可得(无 __init__/无 T 注解形参)时退化为同一序号。[C①] 注解上下文提供的类型实参优先级:显式 [...] > 位置/关键字实参推断 > 注解上下文 > fail-fast,仅当本次构造就是注解赋值/return 的直接值(identity 判定)时才返回非 None
|
||
ctx_args_bare: list[str]* = self.Gen.CallGenCtor._infer_ctor_type_args_from_ctx(pool, trans, func_name, int(tp_count_bare), cl)
|
||
tpi_bare: sizet = 0
|
||
for tpi_bare in range(tp_count_bare):
|
||
aip_bare: int = self.Gen.CallGenCtor._find_ctor_type_param_arg_index(func_name, tp_bare.get(tpi_bare))
|
||
if aip_bare < 0:
|
||
aip_bare = int(tpi_bare)
|
||
ais_bare: sizet = sizet(aip_bare)
|
||
# [D25 fail-fast] 禁止静默默认 "int":此前 ta_bare 初值为
|
||
# "int",在「hint 为空且位置实参也不足」时会凭空特化成
|
||
# list[int]/Box[int] 等错特化 —— 元素 4 字节、方法返回 i32,
|
||
# 与真实的指针元素不匹配 ⇒ 后续 inttoptr 截断(D21/D25 崩溃族)。
|
||
# 与 _infer_generic_ctor_type_arg 的注释「绝不默认 int」对齐,
|
||
# 改为 fail-fast 并指明显式标注写法。
|
||
ta_bare: str = None
|
||
if ais_bare < can:
|
||
ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(ais_bare), func_name, trans)
|
||
if ta_bare is None:
|
||
# [C②] 仅关键字实参(Box(v=42)):按 __init__ 形参名
|
||
# 把该类型形参对应的关键字实参找出来再推断(关键字乱序
|
||
# 也能对齐;该形参位已被位置实参占用时返回 -1)。
|
||
kwi_bare: int = self.Gen.CallGenCtor._find_ctor_type_param_kw_arg_index(func_name, tp_bare.get(tpi_bare), keywords, kw_count, int(can))
|
||
if kwi_bare >= 0:
|
||
kwn_bare: rawptr[ast.Keyword] = rawptr[ast.Keyword](keywords.get(sizet(kwi_bare)))
|
||
if kwn_bare is not None and kwn_bare.value is not None:
|
||
ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, kwn_bare.value, func_name, trans)
|
||
if ta_bare is None and ctx_args_bare is not None:
|
||
# [C①] 注解已显式给出类型实参(如
|
||
# s: Stack[int] = Stack())⇒ 用它特化,而非 fail-fast
|
||
ta_bare = ctx_args_bare.get(tpi_bare)
|
||
if ta_bare is None:
|
||
self.Gen.TypeGenErr.fatal_error(
|
||
cl,
|
||
f"无法从实参推断泛型类 '{func_name}' 的第 {tpi_bare + 1} 个类型实参,"
|
||
f"禁止静默默认 int(会产生 list[int] 等错误特化 → 元素按 4 字节/i32 处理 → 指针截断崩溃);"
|
||
f"请为该实参标注类型或显式标注如 {func_name}[类型](...)",
|
||
"TypeError")
|
||
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, func_name)
|
||
|
||
# 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* = ast.Attribute*(cl.func)
|
||
if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name:
|
||
rc_cls_node: ast.Name* = ast.Name*(rc_at.value)
|
||
if rc_cls_node.id is not None:
|
||
rc_cls_name: str = rc_cls_node.id
|
||
# 检查 ClassName 是否是已注册的 REnum 类
|
||
# [D32] 并上 _is_renum_by_entry(struct 带 __tag 即判 REnum):
|
||
# 变体表因任何原因缺失时,入口至少不被表的空状态挡住;
|
||
# 若 tag 仍查不到则下方 fail-fast 报出表状态(不再落到
|
||
# "外部函数返回类型未登记 ... Metadata" 这种误导报错)。
|
||
if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0 \
|
||
or self.Gen.ClassGenEnum._is_renum_by_entry(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:
|
||
# [D32 fail-fast] 是 REnum 却查不到变体名 ⇒ 变体表不完整,
|
||
# 继续走外部函数路径会报无关的 "外部函数返回类型未登记"。
|
||
self.Gen.TypeGenErr.fatal_error(
|
||
cl,
|
||
f"REnum 类 '{rc_cls_name}' 的变体 '{func_name}' 未在变体表中找到"
|
||
f"({self.Gen.ClassGenEnum.RenumTableDump()});"
|
||
f"变体表未注册或变体名不匹配",
|
||
"NameError")
|
||
if rc_tag >= 0:
|
||
return self.Gen.CallGenCtor._translate_renum_construct(pool, builder, mod, rc_cls_name, func_name, rc_tag, cargs, can, trans, cl.IsVolatile)
|
||
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: sizet = 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, cl.IsVolatile)
|
||
|
||
# async 裸名调用(TPV-M1/M2):普通函数调用 async 函数(如 task(5, 6))→
|
||
# 生成 create_<name>(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* = self.find_func_in_module(mod, create_an)
|
||
if create_fn_an is not None:
|
||
frame_ty_an: llvmlite.LLVMType* = self.Gen.AsyncGen._lookup_coro_frame(trans, func_name)
|
||
if frame_ty_an is not None:
|
||
ret_ptr_ty_an: llvmlite.LLVMType* = llvmlite.Ptr(pool, frame_ty_an)
|
||
i8_ptr_ty_an: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
# M2:async 调用参数(task(5, 6) → create_task(5, 6))
|
||
arg_head_an: llvmlite.Value* = None
|
||
arg_tail_an: llvmlite.Value* = None
|
||
arg_cnt_an: int = 0
|
||
if cl.args is not None:
|
||
nb_an: sizet = len(cl.args)
|
||
j_an: sizet = 0
|
||
for j_an in range(nb_an):
|
||
av_an: llvmlite.Value* = 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* = 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* = 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* = 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:
|
||
# 本模块已定义函数名的裸名调用:先按裸名在模块函数表(FuncEntry 数组,forward_declare_functions 已把所有模块级函数以 Name=裸名、Func=其 declare/define 登记进 trans._funcs)精确命中,再回落 find_func_in_module 的 LLVM 模块链表扫描。函数表才是同模块函数名的权威注册表(链表扫描漏掉本模块定义时会误走 C 库自动 declare / from_imports 回退)。模块表只含模块级函数(无方法),CExport 条目 Func 名即裸名,复用不改变既有符号语义
|
||
mod_fn_tb: llvmlite.Function* = None
|
||
if actual_funcs is not None:
|
||
# 同模块函数优先:_funcs 表在 forward_declare_functions 之前
|
||
# 已被 LoadDepVpiIntoModule 注入的依赖模块 stub 污染——外来
|
||
# 同名函数(如 lexer.vp 的 _advance)以裸名注册在表前部,
|
||
# 普通按名查找从 0 号槽先命中它 → 本模块 _advance 的 163 处
|
||
# 裸名调用全被绑成 lexer 版(AstTest 根因)。传 trans.ModuleSha1
|
||
# 使表查找优先返回 mangled 名为 "{ModuleSha1}.{name}" 的本模块
|
||
# 条目(forward_declare 已把本模块全部模块级函数登记进表)。
|
||
tbl_owner_sha1: str = None
|
||
if trans is not None:
|
||
tbl_owner_sha1 = trans.ModuleSha1
|
||
mod_fn_tb = self.Gen.CallGenLeafKit.find_func_in_table(actual_funcs, actual_func_count, func_name, tbl_owner_sha1)
|
||
if mod_fn_tb is not None:
|
||
found_func = mod_fn_tb
|
||
else:
|
||
# 表内无同名条目(如本模块函数未登记/表被清空)时回退扫
|
||
# FuncHead。同样传 owner_sha1:本模块同名前向 declare 尚未
|
||
# 激活(IsDeclared==1)时 pass2 会跳过、pass3 会按链表顺序
|
||
# 误绑排在前面的跨模块 stub declare,前缀优先扫描兜住该时序。
|
||
ff_owner_sha1: str = None
|
||
if trans is not None:
|
||
ff_owner_sha1 = trans.ModuleSha1
|
||
found_func = self.find_func_in_module(mod, func_name, ff_owner_sha1)
|
||
# 治本修复(对齐 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: sizet = 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: sizet = 0
|
||
while bn_name[bsi2] != '\0' and bsi2 < 17:
|
||
bsi2 += 1
|
||
bse2: sizet = bsi2
|
||
while bn_name[bse2] != '\0' and bn_name[bse2] != '.':
|
||
bse2 += 1
|
||
bsl2: sizet = bse2 - bsi2
|
||
if bsl2 > 0 and bsl2 < 63:
|
||
string.strncpy(bn_seg2, bn_name + bsi2, bsl2)
|
||
bn_seg2[bsl2] = '\0'
|
||
# 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径)。
|
||
# 类未注册时(Phase A 顺序)同样拒绝:{sha1}.X.Y 的 X
|
||
# 既非已注册类又非已导入模块 → 一定是方法(对齐下方
|
||
# Attribute 分支 L660 修复,防裸名误绑未注册类方法)。
|
||
bn_seg2_cls: int = 0
|
||
if self.Gen.StructGen.get_struct_type(bn_seg2) is not None:
|
||
bn_seg2_cls = 1
|
||
bn_seg2_mod: int = 0
|
||
if trans is not None and trans._imported_modules is not None:
|
||
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, bn_seg2) != 0:
|
||
bn_seg2_mod = 1
|
||
if bn_seg2_cls != 0 or bn_seg2_mod == 0:
|
||
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* = ast.Attribute*(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* = ast.Name*(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_or_known(trans._imported_modules, xm_mod_path) != 0:
|
||
mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path)
|
||
if mod_sha1_chk is None and trans._from_imports is not None:
|
||
# import X as Y(如 import fat32_diskio as diskio):
|
||
# 精确查不到别名,必须按别名还原后再查(精确→后缀→
|
||
# LookupModNameByAlias 三级)。否则本校验被跳过 →
|
||
# find_func_in_module 后缀匹配误命中本模块同名函数
|
||
# (diskio.read 命中 fat32 自身 read)→ call 到错误
|
||
# 符号 + 参数错位 + define 被 4 参调用污染(VKernel
|
||
# llc "instruction expected to be numbered" 根因)。
|
||
mod_sha1_chk = self.Gen.StructGen._resolve_module_sha1(xm_mod_path, trans._from_imports)
|
||
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: sizet = 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: sizet = 0
|
||
while ff_name[si] != '\0' and si < 17:
|
||
si += 1
|
||
seg_start: sizet = si
|
||
seg_end: sizet = si
|
||
while ff_name[seg_end] != '\0' and ff_name[seg_end] != '.':
|
||
seg_end += 1
|
||
seg_len: sizet = 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 路径)。
|
||
# 类未注册时(Phase A 顺序:定义模块尚未翻译,如
|
||
# ClassGenEnum 编译时 VLogger.Logger 未注册)也必须拒绝:
|
||
# 3 段名 {sha1}.X.Y 中 X 只可能是类名或子模块名,
|
||
# 既非已注册类又非已导入模块 → 一定是方法。否则模块级
|
||
# 函数调用 VLogger.info(...) 被 find_func_in_module 后缀
|
||
# 匹配误绑到 Logger.info → self 错位 NULL + 参数补零 →
|
||
# 日志退化为 `├ INFO: ENUM`(二代自举日志退化根因)。
|
||
is_seg2_cls: int = 0
|
||
if self.Gen.StructGen.get_struct_type(seg2) is not None:
|
||
is_seg2_cls = 1
|
||
is_seg2_mod: int = 0
|
||
if trans is not None and trans._imported_modules is not None:
|
||
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, seg2) != 0:
|
||
is_seg2_mod = 1
|
||
if is_seg2_cls != 0 or is_seg2_mod == 0:
|
||
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* = None
|
||
bi_p1: llvmlite.LLVMType* = None
|
||
bi_p2: llvmlite.LLVMType* = 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)
|
||
elif string.strcmp(func_name, "min") == 0 or string.strcmp(func_name, "max") == 0:
|
||
# min/max 数值比较(builtins.vp 定义 double 参数/返回,
|
||
# int/float 实参经 coerce 提升;两参数)
|
||
bi_ret = llvmlite.Double(pool)
|
||
bi_p1 = llvmlite.Double(pool)
|
||
bi_p2 = llvmlite.Double(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: sizet = 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* = llvmlite.create_declare(pool, mod, bi_sym, bi_ret)
|
||
if bi_decl is not None:
|
||
llvmlite.add_param(pool, bi_decl, bi_p1, "v")
|
||
if bi_p2 is not None:
|
||
llvmlite.add_param(pool, bi_decl, bi_p2, "w")
|
||
found_func = bi_decl
|
||
# 使用访问器绕过 stub 类型限制
|
||
found_ret_ty: llvmlite.LLVMType* = 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* = 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* = None
|
||
# 跨模块返回类型解析的来源模块 SHA1:仅在下方 cl.func 是
|
||
# Attribute(模块.函数) 分支赋值。必须显式初始化 None——此前声明在
|
||
# if 块内、elif mod_ret_sha1 is not None 在外层读取,裸名调用
|
||
# (cl.func 是 Name,如 LLVMModuleCore 方法内裸名 create_global_int)
|
||
# 时该 alloca 从未 store,读到栈残留垃圾(0x1)→
|
||
# _LookupMethodRetTyFromTextLL 的 strlen(0x1) 零页崩溃
|
||
# (llvmlite/__init__.vp CreateGlobalInt 翻译时 0xC0000005 根因)。
|
||
mod_ret_sha1: str = None
|
||
# 模块路径(cl.func 为 Attribute 时赋值,否则保持 None)。声明在 if 外,
|
||
# 使下方裸名兜底分支也能安全读取(读未声明的局部 = 栈垃圾)。
|
||
mod_path_ret: str = None
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
at_ret: ast.Attribute* = ast.Attribute*(cl.func)
|
||
# 解析调用模块路径:单层(stdlib.free,value 是 Name)与多层
|
||
# (w32.win32file.CreatePipe,value 是嵌套 Attribute)都支持。
|
||
# 缺失多层路径 → mod_ret_sha1=None → 误走裸名外部函数路径,
|
||
# fail-fast 误杀可收敛的模块限定调用(CreatePipe 根因)。
|
||
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
|
||
mod_ret_nm: ast.Name* = ast.Name*(at_ret.value)
|
||
mod_path_ret = mod_ret_nm.id
|
||
elif at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Attribute:
|
||
mod_path_ret = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_ret.value)
|
||
if mod_path_ret is not None:
|
||
# import X as Y 别名(如 import lib.core.Gen.GenApi as GenApi):
|
||
# 精确模块名查找(LookupSha1ByModName)查不到别名 → None → 误走
|
||
# 裸名外部函数路径 fail-fast 误杀可收敛的模块限定调用
|
||
# (CallGenLeafKit L1327 GenApi.translate_printf_call 根因,
|
||
# 同 CreatePipe 模式)。改经 _resolve_module_sha1(别名还原
|
||
# LookupModNameByAlias → 精确/后缀)解析。
|
||
fi_from_rt: str = None
|
||
if trans is not None:
|
||
fi_from_rt = trans._from_imports
|
||
mod_ret_sha1 = self.Gen.StructGen._resolve_module_sha1(mod_path_ret, fi_from_rt)
|
||
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 None:
|
||
# 包模块 vpi 未命中(函数定义在子模块,如
|
||
# fltk.Fl_Input_value 实际在 fltk._cfl_input):
|
||
# 经 reexport 定位定义模块(star import 多候选
|
||
# 逐个查返回注解)再查 vpi。
|
||
rr_src: str = self.Gen.ImportsGen.lookup_reexport(mod_ret_sha1, func_name)
|
||
if rr_src is None:
|
||
rr_src = self._lookup_reexport_from_vpi(pool, mod_ret_sha1, func_name)
|
||
if rr_src is not None:
|
||
rr_total: sizet = string.strlen(rr_src)
|
||
rr_i: sizet = 0
|
||
while rr_i < rr_total and ret_annot_fc is None:
|
||
while rr_i < rr_total and rr_src[rr_i] == ' ':
|
||
rr_i += 1
|
||
if rr_i >= rr_total:
|
||
break
|
||
rr_start: sizet = rr_i
|
||
while rr_i < rr_total and rr_src[rr_i] != ' ':
|
||
rr_i += 1
|
||
rr_len: sizet = rr_i - rr_start
|
||
rr_buf: str = pool.alloc(rr_len + 1)
|
||
if rr_buf is not None:
|
||
string.strncpy(rr_buf, rr_src + rr_start, rr_len)
|
||
rr_buf[rr_len] = '\0'
|
||
rr_sha1: str = self._lookup_module_sha1(rr_buf)
|
||
if rr_sha1 is not None:
|
||
VpiReader._ensure_vpi_ret_annot_loaded(rr_sha1, self.Gen.CallGenBase.TempDir)
|
||
ret_annot_fc = VpiReader._lookup_vpi_ret_annot(rr_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
|
||
elif mod_ret_sha1 is not None:
|
||
# 跨模块裸名 CExport 函数(如 mm.malloc → @malloc):found_func=None
|
||
# 且 vpi 返回注解缓存未命中时,仍从依赖模块 text.ll/vpi 精确解析
|
||
# 返回类型(对齐 mangled 路径),避免静默默认 i32 截断指针/i64
|
||
# (uhci.vp mm.malloc 根因:call i32 @malloc 截断 0xbb69000 → null)。
|
||
cross_ret: llvmlite.LLVMType* = None
|
||
if trans is not None and trans._declare_only == 0:
|
||
cross_ret = self._LookupMethodRetTyFromTextLL(pool, mod_ret_sha1, None, func_name, 1)
|
||
if cross_ret is None:
|
||
cross_ret = self._lookup_method_ret_ty_via_vpi(pool, mod_ret_sha1, None, func_name)
|
||
if cross_ret is not None:
|
||
call_ret_ty = cross_ret
|
||
else:
|
||
# 依赖模块 text.ll 未生成(Phase A 中间遍)→ fatal_if_unknown=0,
|
||
# 本遍仅临时占位。
|
||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0)
|
||
else:
|
||
# 裸名调用(cl.func 是 Name,mod_ret_sha1=None)两类来源:from-import 的函数(后续解析模块归属构造 mangled call_name 并经 text.ll/vpi 精确解析返回类型,可收敛)→ fatal_if_unknown=0 仅临时占位,不 fail-fast;纯外部 C 函数(无登记、无模块归属,白名单未命中)→ fail-fast(fatal_if_unknown=1)。闭包变量与持有函数指针/闭包的局部变量同样按可解析占位——返回类型由 translate_closure_call/lookup_var 路径固定处理,不依赖外部返回类型推断,否则被 fail-fast 误杀
|
||
bare_resolvable: int = 0
|
||
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
|
||
bare_resolvable = 1
|
||
if trans is not None and trans.SymTab is not None:
|
||
if self.Gen.VarGen.lookup_var(trans.SymTab, func_name) is not None:
|
||
bare_resolvable = 1
|
||
if trans is not None and trans._from_imports is not None:
|
||
if self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) is not None:
|
||
bare_resolvable = 1
|
||
# M3 会话函数(跨片段):名字命中会话函数表 ⇒ 同样按"可解析"占位,
|
||
# 不 fail-fast。返回类型由下方 found_func==None 分支的
|
||
# repl_try_session_func_call 按表内签名(宿主从定义片段 IR 解析)构造,
|
||
# 与 _infer_external_func_ret_ty 的白名单无关;此处的 call_ret_ty 只是
|
||
# 占位(命中后立即 return,不会用到)。
|
||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 不经过;且限定裸名(Name)——
|
||
# 会话函数只以裸名导出,模块限定调用(mod.f)不走本表。
|
||
if trans is not None and trans.ReplCtx is not None \
|
||
and cl.func is not None and cl.func.kind() == ast.ASTKind.Name \
|
||
and self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name) is not None:
|
||
bare_resolvable = 1
|
||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 1 - bare_resolvable)
|
||
|
||
if found_func is None:
|
||
fp_alloca: llvmlite.Value* = None
|
||
if cl.func is None or cl.func.kind() == ast.ASTKind.Name:
|
||
# 闭包内调用捕获的闭包变量(如 _cmd_cd 调 _build_path):
|
||
# 从 env 读闭包对象指针,不能 lookup_var 跨函数命中父函数
|
||
# 闭包 alloca(SSA 错乱 → llc "instruction expected to be
|
||
# numbered",Terminal _cmd_cd 根因)。
|
||
if trans is not None and GenApi.is_nonlocal_name(trans, func_name) != 0:
|
||
fp_alloca = self.Gen.StmtGenDecl.load_nonlocal_var(trans, func_name)
|
||
if fp_alloca is not None:
|
||
# translate_closure_call 期望 i8** 存储(load 一次得闭包
|
||
# 对象指针);load_nonlocal_var 已返回对象指针 → 临时
|
||
# alloca 暂存
|
||
i8_ty_nl: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||
i8_ptr_ty_nl: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_nl)
|
||
tmp_alloca_nl: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_nl)
|
||
if tmp_alloca_nl is not None:
|
||
llvmlite.build_store(builder, fp_alloca, tmp_alloca_nl)
|
||
fp_alloca = tmp_alloca_nl
|
||
else:
|
||
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* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, func_name)
|
||
is_cptr_annot_fp: int = 0
|
||
fp_annot: str = self.Gen.VarGen.var_annot_class_name(ve_fp)
|
||
if fp_annot is not None:
|
||
# [S4] CPtr 标记语义 = **注解基名精确等于 "CPtr"**
|
||
# (裸 t.CPtr / from t import CPtr 后的 CPtr;两者都只在
|
||
# 注解里写了指针标记本身,没有承载具体型别)。
|
||
# 为何不能用 Spec 判定:t.Callable[[A], R] 解析结果同样是
|
||
# Ptr(i8)(TypeGenResolve.vp:2525-2531),与 t.CPtr 的
|
||
# Spec 完全同型 ⇒ 类型层无区分度,标记只能由注解名承载。
|
||
# 原 strstr 子串匹配会误命中含 "CPtr" 的类名(如 CPtrHolder)。
|
||
# 已知局限(与改造前一致,非回归):`Foo | t.CPtr` 的
|
||
# SourceName 只留 "Foo"(取非 CPtr 侧类名)⇒ 此处判为非标记。
|
||
if TSpec.SpecTypeNameBaseIs(fp_annot, "CPtr") != 0:
|
||
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)
|
||
# ---- M3:会话函数表命中 → ctx 函数指针槽取址 + 间接调用 ----
|
||
# 位置(为什么在这里):本步位于 `if found_func is None:` 块**末尾**——
|
||
# ① found_func 为 None ⇒ 本片段的 LLVM 模块里没有该函数的定义/前向声明
|
||
# (会话函数定义在**先前片段**的 DLL 里,代码不在本模块内);
|
||
# ② 放在闭包名/局部函数指针变量分支**之后** ⇒ 局部名与闭包正确地**遮蔽**
|
||
# 会话函数(Python 的局部优先),只有"既非本模块函数/声明、又非局部/
|
||
# 闭包/函数指针变量"的裸名才查会话函数表;
|
||
# ③ 仍先于 from_imports 解析与"裸名调用未定义且未导入"的 fail-fast。
|
||
# 命中后从 ctx 的函数指针槽 load 地址、按表内签名间接调用:函数代码只在
|
||
# 定义片段里一份(不复制/不重编译),调用点只持指针。
|
||
sfn_box: t.CPtr = None
|
||
if trans is not None and trans.ReplCtx is not None \
|
||
and (cl.func is None or cl.func.kind() == ast.ASTKind.Name):
|
||
sfn_box = stdlib.malloc(4)
|
||
if sfn_box is not None:
|
||
sfn_box[0] = 0
|
||
sfn_val: llvmlite.Value* = self.Gen.VarGen.repl_try_session_func_call(trans, pool, builder, mod, func_name, cargs, int(can), int(kw_count), node, sfn_box)
|
||
sfn_handled: int = int(sfn_box[0])
|
||
stdlib.free(sfn_box)
|
||
if sfn_handled != 0:
|
||
# 命中(含命中但按 M3 规则明确报错的情形):不再走任何
|
||
# "未定义/未导入"路径。void 会话函数返回 None 属预期。
|
||
return sfn_val
|
||
|
||
# 翻译参数并链接
|
||
# 有关键字参数且找到函数定义时,按参数名匹配
|
||
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* = None
|
||
tail: llvmlite.Value* = None
|
||
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
|
||
mc_dst2: llvmlite.Value* = None
|
||
mc_src2: llvmlite.Value* = None
|
||
mc_num2: llvmlite.Value* = None
|
||
for ai in range(can):
|
||
arg: ast.AST* = 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* = None
|
||
fpty: llvmlite.LLVMType* = 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* = 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* = 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* = 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* = 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* = 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: sizet))
|
||
# 修复: 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, trans)
|
||
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* = 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.CPtr = None
|
||
df_default_count: int = 0
|
||
df_param_count: int = 0
|
||
# 计算被调模块 SHA1 作限定键(消除跨模块同名函数串味):
|
||
# module.func(...) → 该 module 的 SHA1;裸名调用 → 当前模块 SHA1。
|
||
# 模块表与全局默认参数表都按「名 + 定义模块 SHA1」精确匹配,避免
|
||
# usb.uhci.init(1 参) 误命中 mm.init(5 参/2 默认值) 而补出多余实参。
|
||
# 必须先算(模块表查找也要用它限定定义模块 → 见下)。
|
||
dfx_callee_sha: str = None
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
dfx_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if dfx_at.value is not None and dfx_at.value.kind() == ast.ASTKind.Name:
|
||
dfx_mn: ast.Name* = ast.Name*(dfx_at.value)
|
||
if dfx_mn.id is not None:
|
||
dfx_callee_sha = self._lookup_module_sha1(dfx_mn.id)
|
||
if dfx_callee_sha is None and self.Gen.TypeGenBase is not None:
|
||
dfx_callee_sha = self.Gen.TypeGenBase.CurrentModuleSha1
|
||
# 从当前模块函数表查找 FuncEntry —— 必须按**定义模块 SHA1 限定**:
|
||
# 表内含 LoadDepVpiIntoModule 以裸名注入的依赖 stub(槽位在前),
|
||
# 裸名首命中会让 usb 的 `uhci.init(io_base)`(1 形参)拿到 mm.init 的
|
||
# 5 形参/2 默认值 → 调用点被补 2 个 i32 0(N17)。
|
||
# 注:仅做「精确到定义模块」这一项改动;下方交叉校验守卫(dfx_cnt)
|
||
# 保持原样(`dfx_cnt > 0 and ...`)——收紧它会让 dfx_cnt==0 的场景
|
||
# 落入 `elif found_func is not None` 零值兜底分支,改动面更大,
|
||
# 且本项改动已足以修掉 N17(受限查找命中本模块/同定义模块条目,
|
||
# Defaults 为 None 或本模块池内 AST)。
|
||
if trans is not None and trans._funcs is not None:
|
||
df_entry: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_in_table_owned(trans._funcs, trans._func_count, func_name, dfx_callee_sha)
|
||
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* = self.Gen.CallGenLeafKit.find_func_defaults(func_name, dfx_callee_sha)
|
||
# 包重导出补查(如 json.write 实际定义在 includes/json/__writer.vp):
|
||
# AST 侧模块名 'json' 只能解析到包 __init__ 的 SHA1,而默认参数表按
|
||
# **定义该函数的模块 SHA1** 登记(_register_global_defaults),于是直接
|
||
# 查不到 → 缺参填充整体跳过 → call 实参数 < declare/define 形参数 →
|
||
# 被调函数读寄存器/栈残留垃圾(VET `json.write(pool, val, False)` 的
|
||
# 缺省 cap 读成指针量级 → pool.alloc(巨量) → [OOM] 根因)。
|
||
# 与下方 L1473-1480 调用名重定向同一套 re-export 解析:命中后用
|
||
# **定义模块 SHA1** 复查默认参数表(SHA1 已校验,不会跨模块串味)。
|
||
if gd_entry is None and dfx_callee_sha is not None:
|
||
rr_src_dx: str = self.Gen.ImportsGen.lookup_reexport(dfx_callee_sha, func_name)
|
||
if rr_src_dx is None:
|
||
rr_src_dx = self._lookup_reexport_from_vpi(pool, dfx_callee_sha, func_name)
|
||
if rr_src_dx is not None:
|
||
rr_sha_dx: str = self._lookup_module_sha1(rr_src_dx)
|
||
if rr_sha_dx is not None:
|
||
gd_entry = self.Gen.CallGenLeafKit.find_func_defaults(func_name, rr_sha_dx)
|
||
# 覆盖条件含 df_default_count<=0:调用方函数表可能命中一条同名条目但
|
||
# Defaults 为空/默认值为 0 条(VpiReader 注入的 declare 条目),
|
||
# 此时必须用全局默认参数表的真实条目覆盖,否则缺参填充整体不触发。
|
||
if gd_entry is not None and (df_defaults is None or df_default_count <= 0):
|
||
df_defaults = gd_entry.Defaults
|
||
df_default_count = gd_entry.DefaultCount
|
||
df_param_count = gd_entry.ParamCount
|
||
# 交叉校验:全局默认参数表按「裸函数名」登记/查询,跨模块同名函数会串味
|
||
# (如 usb.uhci.init 只有 1 形参,却命中 mm.init 的 5 形参/2 默认值记录
|
||
# → 被补 2 个 i32 0 → call 3 实参 vs declare 1 形参 → ABI 错位)。
|
||
# 若被调声明(found_func)的实际形参数与表记录 ParamCount 不符,则丢弃该表,
|
||
# 交由下方 `elif found_func is not None` 分支按真实形参数零值兜底。
|
||
if found_func is not None and df_default_count > 0:
|
||
dfx_cnt: int = 0
|
||
dfx_p: llvmlite.Param* = llvmlite.function_get_param_head(found_func)
|
||
while dfx_p is not None:
|
||
dfx_nm: str = llvmlite.param_get_name(dfx_p)
|
||
if dfx_nm is None or (string.strcmp(dfx_nm, "%__eh_msg_out__") != 0
|
||
and string.strcmp(dfx_nm, "%__eh_code_out__") != 0):
|
||
dfx_cnt += 1
|
||
dfx_p = llvmlite.param_get_next(dfx_p)
|
||
if dfx_cnt > 0 and dfx_cnt != df_param_count:
|
||
df_defaults = None
|
||
df_default_count = 0
|
||
df_param_count = 0
|
||
# 填充缺失参数
|
||
if df_default_count > 0 and can < df_param_count and (df_defaults is not None or gd_entry is not None):
|
||
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* = 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 and df_defaults is not None:
|
||
df_defaults_list: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(df_defaults))
|
||
df_default_node: ast.AST* = 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
|
||
# 追加的默认实参必须计入实参数:下方
|
||
# _coerce_args_to_def_params/_ensure_method_declare 等
|
||
# 均以 can 为界重建/校验实参链,count 不增会把补齐的
|
||
# 尾随默认实参整体截断丢弃(VET json.write 缺省 cap
|
||
# 丢失 → 被调方读寄存器垃圾 → pool.alloc 巨量 OOM 根因)。
|
||
can += 1
|
||
elif found_func is not None:
|
||
# 默认参数缺失/表信息不全时的零值兜底:函数表有参数数但无默认值记录、或调用方先于定义文件编译导致查不到默认值时,用 found_func 实际参数数(stub declare,与 define 一致)兜底并缺参填零值(对齐 TPC _fill_default_args 语义)。注意排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__),否则会与 _emit_eh_guarded_call 追加的真实 EH 实参重复导致 ABI 错位
|
||
fc_cnt: int = 0
|
||
fc_p: llvmlite.Param* = 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* = 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* = llvmlite.param_get_ty(fp_z)
|
||
zero_val_z: llvmlite.Value* = 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
|
||
can += 1 # 零值兜底实参同样计入实参数(同默认实参)
|
||
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
|
||
# CExport 裸名兜底:跨模块 stub declare 可能带 SHA1 前缀
|
||
# (@"1fdad1c76fb116bc.malloc"),但定义端 CExport 函数
|
||
# (mm.vp malloc/free/malloc_direct,返回注解含 t.CExport)
|
||
# 用裸名 @malloc → 链接 undefined symbol: 1fdad1c76fb116bc.malloc。
|
||
# found_func 非 None 时下方 is_cexport_func 裸名分支不执行
|
||
# (该分支仅 found_func None 的模块限定路径),这里对带前缀
|
||
# 的 declare 提取 sha1 再查 CExport 注册表,命中则改用裸名。
|
||
if found_func.IsDeclared != 0:
|
||
# 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:此前只判
|
||
# 首个 '.' 前恰 16 字节、不校验 hex 形态)。
|
||
if GenApi.sha1_prefix_len(found_name) != 0:
|
||
cx_sha1_buf: str = pool.alloc(17)
|
||
if cx_sha1_buf is not None:
|
||
string.memcpy(cx_sha1_buf, found_name, 16)
|
||
cx_sha1_buf[16] = '\0'
|
||
if self.Gen.CallGenBase.is_cexport_func(cx_sha1_buf, func_name) != 0:
|
||
call_name = func_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* = ast.Attribute*(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* = ast.Name*(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_or_known(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)
|
||
rs_found: int = 0
|
||
if reexport_src is not None:
|
||
# star import 多候选(空格分隔,如 fltk/__init__ 依次
|
||
# from _types/_enums/_cfl import *)逐个验证定义模块:
|
||
# is_cexport_func/_is_state_func_from_vpi 按 vpi 返回注解
|
||
# 确认 func_name 真实定义处(Fl_init_all → _cfl)。
|
||
cs_total: sizet = string.strlen(reexport_src)
|
||
cs_i: sizet = 0
|
||
while cs_i < cs_total:
|
||
while cs_i < cs_total and reexport_src[cs_i] == ' ':
|
||
cs_i += 1
|
||
if cs_i >= cs_total:
|
||
break
|
||
cs_start: sizet = cs_i
|
||
while cs_i < cs_total and reexport_src[cs_i] != ' ':
|
||
cs_i += 1
|
||
cs_seg: sizet = cs_i - cs_start
|
||
cs_buf: str = pool.alloc(cs_seg + 1)
|
||
if cs_buf is not None:
|
||
string.strncpy(cs_buf, reexport_src + cs_start, cs_seg)
|
||
cs_buf[cs_seg] = '\0'
|
||
cand_sha1: str = self._lookup_module_sha1(cs_buf)
|
||
if cand_sha1 is not None:
|
||
if self.Gen.CallGenBase.is_cexport_func(cand_sha1, func_name) != 0 or \
|
||
self._is_state_func_from_vpi(pool, cand_sha1, func_name) != 0:
|
||
mod_sha1 = cand_sha1
|
||
call_name = func_name
|
||
rs_found = 1
|
||
break
|
||
if rs_found == 0:
|
||
# star import 候选验证全部失败:func_name 可能定义在
|
||
# 原模块自身(extern def 如 stdlib.malloc 定义在 stdlib,
|
||
# 而 stdlib 又 `from stdint import *` 产生误候选 stdint)。
|
||
# 先验证原模块:extern/State → 裸名(rs_found=1 下方置裸名)。
|
||
if self.Gen.CallGenBase.is_cexport_func(mod_sha1, func_name) != 0 or \
|
||
self._is_state_func_from_vpi(pool, mod_sha1, func_name) != 0:
|
||
rs_found = 1
|
||
call_name = func_name
|
||
else:
|
||
# 原模块 vpi 自含普通 def(非 CExport/State):如
|
||
# string.vp 自身定义 strncmp,而其 `from stdint
|
||
# import *` 产生的 star 候选 stdint 会劫持解析。
|
||
# vpi 命中 → 保持原模块 sha(生成 @{mod_sha1}.func,
|
||
# 普通跨模块函数带 sha1 前缀),不走候选回退。
|
||
orig_has_def: int = 0
|
||
if self.Gen.CallGenBase.TempDir is not None \
|
||
and self.Gen.CallGenBase.TempDirLen > 0:
|
||
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1, self.Gen.CallGenBase.TempDir)
|
||
if VpiReader._lookup_vpi_ret_annot(mod_sha1, None, func_name) is not None:
|
||
orig_has_def = 1
|
||
if orig_has_def == 0:
|
||
# 原模块未定义:回退首个候选(旧行为,普通跨模块
|
||
# 函数带 sha1 前缀,如 ast._lexer_init 定义在 ast.lexer)
|
||
reexport_sha1: str = self._lookup_module_sha1(reexport_src)
|
||
if reexport_sha1 is not None:
|
||
mod_sha1 = reexport_sha1
|
||
# CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func
|
||
if rs_found != 0:
|
||
pass # 已验证 reexport 定义模块并置裸名
|
||
elif 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: sizet = 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: sizet = 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: sizet = 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: sizet = 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: sizet = 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:
|
||
# 别名附加尝试:仅作为 mangled 精确解析前的临时推断(fatal_if_unknown=0)。
|
||
# 若 call_name 是 mangled,L1272 起的 text.ll/vpi 精确解析会覆盖并 fail-fast;
|
||
# 未命中时保持白名单/默认 i32 由后续路径兜底。
|
||
re_inferred_ty: llvmlite.LLVMType* = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name, node, 0)
|
||
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: sizet = string.strlen(call_name) - \
|
||
(sizet(uint64(cf_last) - uint64(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* = 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* = 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* = 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:
|
||
# 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按文件
|
||
# 顺序翻译,main.vp 先于其 import 的 mbuddytest.vp(同项目
|
||
# 多源文件):本遍保持默认类型,不 fail-fast。
|
||
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
|
||
self.Gen.TypeGenErr.fatal_error(node, f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}", "TypeError")
|
||
stdlib.free(cf_sha1)
|
||
stdlib.free(cf_bare)
|
||
# 裸名 C 库函数(string.memset → @memset):实参按标准签名提升
|
||
# (第三参 i32 字面量 → i64),再建 declare,保证 call 实参与
|
||
# declare/定义宽度一致(B3:process 固化 i32 vs 定义 sizet=i64)。
|
||
head = self.Gen.CallGenLib._promote_clib_call_args(pool, builder, mod, call_name, head, can)
|
||
# 跨模块模块级调用({sha1}.{func}):实参按 vpi 定义侧形参类型 coerce
|
||
# (整型宽度精确对齐,i32 字面量 → 定义侧 i8/i16),再建 declare →
|
||
# call 实参 / declare / define 三者宽度一致(B3 同族通用化;反例:
|
||
# su8250.putchar 定义 (i16,i8) 而调用点字面量 1016=i32 固化宽)。
|
||
head = self.Gen.CallGenLib._coerce_args_to_def_params(pool, builder, call_name, head, can, cargs, 0, trans)
|
||
# 跨模块调用:创建 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, sizet(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)
|