From 0a28df0864ccd78abda91e6396e3520a45207f54 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 26 Aug 2026 17:29:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20=E6=A8=A1=E5=9D=97=E9=99=90?= =?UTF-8?q?=E5=AE=9A=E6=9E=84=E9=80=A0=E6=8C=89=E6=A8=A1=E5=9D=97=E5=90=8D?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=20SHA1=EF=BC=8C=E4=BF=AE=E5=A4=8D=20ctor=20?= =?UTF-8?q?=E5=BD=92=E5=B1=9E=E5=86=85=E6=A0=B8=E5=90=8C=E5=90=8D=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gfx.Scene3D(...) 的模块限定构造路径(Name 模块基)误用 trans.ModuleSha1 (当前文件 sha1,类在 gfx 模块)→ find_struct_by_module 落空 → 裸名回退 find_struct("Scene3D") 命中跨模块同名类中先注册的 entry(VKernel gfx 与 App gfx 都定义 Scene3D,0ae 先经 includes 预注册)→ ctor 生成 @0ae.Scene3D.__before_init__ → 链接 undefined symbol(Scene3D 根因)。 改为按模块名 _lookup_module_sha1("gfx") 精确解析 SHA1 后 find_struct_by_module 命中 App 版(9aab),与注解解析路径一致。 --- App/lib/core/Gen/CallGenMixin.vp | 85 +++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index fa14a5d..76f8172 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -85,14 +85,6 @@ class CallGenMixin(CallGenBase.CallGenBase): can: t.CSizeT = 0 if cargs is not None: can = len(cargs) - # [DET] len() 结果核对(SlotsBugTest/StringIterTest can 垃圾高位定位: - # can=0x229_0000_0000=[cargs高32][count低32],怀疑 len() 生成代码 4 字节 - # 错位。门控:真实 count 远小于 2^32,can>=2^32 即异常,打印定位)。 - if (t.CUInt64T(can) & (t.CUInt64T(1) << 32)) != 0: - lh_l: t.CUInt64T | t.CPtr = (t.CUInt64T | t.CPtr)(t.CVoid(cargs, t.CPtr)) - stdio.printf("[DET] len结果异常: can=%llu cargs=%llu data=%llu count=%llu cap=%llu\n", - t.CUInt64T(can), t.CUInt64T(cargs), lh_l[0], lh_l[1], lh_l[2]) - # super()/ctraits 检测:void 方法(如 __init__)命中后 build_call 返回 # None 属预期(void call 无 SSA 值),必须用 hit_box 区分"命中已处理" # 与"未命中",否则 None 被误判继续主路径 → 内层 Call(super) 当裸名 @@ -248,8 +240,18 @@ class CallGenMixin(CallGenBase.CallGenBase): 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) + # 模块限定构造 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: @@ -347,12 +349,6 @@ class CallGenMixin(CallGenBase.CallGenBase): return self.Gen.CallGenCtor._translate_struct_ctor_kw( pool, builder, mod, struct_ty, func_name, cargs, can, keywords, kw_count, trans) - # 仅位置参数:按字段顺序构建 - # [DET] 调用前 can 核对(与 len结果/callee 收到的 can 三级对照, - # 定位 4 字节错位发生在 len 计算/调用传参/callee 读取哪一级) - if (t.CUInt64T(can) & (t.CUInt64T(1) << 32)) != 0: - stdio.printf("[DET] 调用前 can=%llu cargs=%llu\n", - t.CUInt64T(can), t.CUInt64T(cargs)) return self.Gen.CallGenCtor._translate_struct_ctor( pool, builder, mod, struct_ty, func_name, cargs, can, trans) @@ -607,6 +603,16 @@ class CallGenMixin(CallGenBase.CallGenBase): 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 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: @@ -652,12 +658,19 @@ class CallGenMixin(CallGenBase.CallGenBase): if bi_sha1 is not None: bi_ret: llvmlite.LLVMType | t.CPtr = None bi_p1: llvmlite.LLVMType | t.CPtr = None + bi_p2: 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) + 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: @@ -670,6 +683,8 @@ class CallGenMixin(CallGenBase.CallGenBase): 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 | t.CPtr = None @@ -720,7 +735,25 @@ class CallGenMixin(CallGenBase.CallGenBase): 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) + # 闭包内调用捕获的闭包变量(如 _cmd_cd 调 _build_path): + # 从 env 读闭包对象指针,不能 lookup_var 跨函数命中父函数 + # 闭包 alloca(SSA 错乱 → llc "instruction expected to be + # numbered",Terminal _cmd_cd 根因)。 + if trans is not None and HT.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 | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_nl: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_nl) + tmp_alloca_nl: llvmlite.Value | t.CPtr = llvmlite.build_alloca( + 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: @@ -962,6 +995,24 @@ class CallGenMixin(CallGenBase.CallGenBase): 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: + dot_pos_cx: str = string.strchr(found_name, 46) + if dot_pos_cx is not None: + cx_pre_len: t.CSizeT = string.strlen(found_name) - string.strlen(dot_pos_cx) + if cx_pre_len == 16: + 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