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