diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index bb8674b..24a5dbb 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -157,6 +157,9 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, # P2-P5: 注入各 Handles 模块级单例(委托函数经本地 _g_gen 访问, # 避免函数体 import 包模块与类/模块名歧义) GenApi._g_gen = gen + HandlesTranslator._g_gen = gen + HandlesExpr._g_gen = gen + HandlesExprCall._g_gen = gen # P6: 注入语句/赋值/变量/导入/翻译驱动模块的单例 # 设置当前文件名(供报错使用) GenApi.set_current_file_type(file_path) @@ -836,6 +839,63 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f return 0 +def asm_to_obj(asm_path: str, sha1_val: str, output_dir: str, + triple: str) -> int: + """用 llvm-mc 汇编 .s 源文件为 .obj(切片路径) + + 裸机项目(VKernel 等)含 .s 汇编文件(如 Kernel/intr/isr.s 定义 + isr0/syscall_entry/_per_cpu 等符号)。.s 不经过 TPV 翻译/llc, + 直接用配套的 llvm-mc 汇编为 .obj,obj 路径与其他模块一致 + (output_dir/{sha1前缀}/{sha1}.obj),参与 Phase C 链接。 + + Args: + asm_path: .s 源文件完整路径 + sha1_val: 文件内容 SHA1 前16字符(切片路径用) + output_dir: 输出目录 + triple: 目标 triple(如 x86_64-none-elf) + + Returns: + 0 成功,非 0 失败 + """ + if asm_path is None or sha1_val is None or output_dir is None: + return 1 + obj_path: str = build_sliced_path(output_dir, sha1_val, "obj") + if obj_path is None: + return 1 + trip: str = triple + if trip is None or trip[0] == '\0': + trip = "x86_64-none-elf" + cmd_len: t.CSizeT = len("llvm-mc -triple=") + len(trip) \ + + len(" -filetype=obj -o ") + len(obj_path) + len(asm_path) + 64 + cmd: bytes = _mbuddy.alloc(cmd_len) + if cmd is None: + return 1 + viperlib.snprintf(cmd, cmd_len, "llvm-mc -triple=%s -filetype=obj -o %s %s", + trip, obj_path, asm_path) + + # --phase2command 模式:命令写入 bat(与 llc/链接一致) + if _phase2_cmd_path is not None: + _phase2_write_cmd(cmd) + return 0 + + result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) + if result is None: + VLogger.error("llvm-mc subprocess.run 返回 None", "asm") + return 1 + if result.returncode != 0: + if result.stdout is not None: + stdio.printf("%s\n", result.stdout) + if result.stderr is not None: + stdio.printf("%s\n", result.stderr) + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "llvm-mc 汇编失败,返回码: %d, 命令: %s", + result.returncode, cmd) + VLogger.error(fb, "asm") + return 1 + return 0 + + def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT, temp_dir: str, output_dir: str, module_name: str, cc_cmd: str, cc_flags: str) -> int: @@ -899,6 +959,61 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, if obj_paths is None or obj_paths_len == 0 or linker_cmd is None: return 1 + # 去重 .obj 路径:同一模块的 obj 可能被多个收集环节加入(Phase B+ + # 新编译 obj_paths 与 Phase C reachable 收集指向 includes.binary 中 + # 同一文件)。App 项目 linker flags 含 --allow-multiple-definition 可 + # 容忍;VKernel 无此 flag → ld.lld "duplicate symbol"。链接命令中同一 + # 路径出现两次即重复符号,保留首次出现的路径,后续重复跳过。 + if obj_paths_len > 0: + dedup: bytes = _mbuddy.alloc(obj_paths_len + 1) + if dedup is not None: + dedup[0] = '\0' + dpos: t.CSizeT = 0 + dsrc_i: t.CSizeT = 0 + while dsrc_i < obj_paths_len: + # 跳过前导空格 + while dsrc_i < obj_paths_len and obj_paths[dsrc_i] == ' ': + dsrc_i += 1 + if dsrc_i >= obj_paths_len: + break + dstart: t.CSizeT = dsrc_i + while dsrc_i < obj_paths_len and obj_paths[dsrc_i] != ' ': + dsrc_i += 1 + dtlen: t.CSizeT = dsrc_i - dstart + # 检查 dedup 中是否已有相同 token + ddup: int = 0 + dj: t.CSizeT = 0 + while dj < dpos: + while dj < dpos and dedup[dj] == ' ': + dj += 1 + if dj >= dpos: + break + ds2: t.CSizeT = dj + while dj < dpos and dedup[dj] != ' ': + dj += 1 + dt2len: t.CSizeT = dj - ds2 + if dt2len == dtlen: + deq: int = 1 + dk: t.CSizeT = 0 + while dk < dtlen: + if dedup[ds2 + dk] != obj_paths[dstart + dk]: + deq = 0 + break + dk += 1 + if deq != 0: + ddup = 1 + break + if ddup == 0: + if dpos > 0: + dedup[dpos] = ' ' + dpos += 1 + string.memcpy(dedup + dpos, obj_paths + dstart, dtlen) + dpos += dtlen + dedup[dpos] = '\0' + if dpos > 0 and dpos != obj_paths_len: + obj_paths = dedup + obj_paths_len = dpos + # 收集 includes.binary 的 .obj 文件路径 EXTRA_BUF_SIZE: t.CSizeT = 32768 extra_objs: bytes = _mbuddy.alloc(EXTRA_BUF_SIZE) diff --git a/App/lib/Phase/Phase1/Phase1Main.vp b/App/lib/Phase/Phase1/Phase1Main.vp index 1f4accb..4be4167 100644 --- a/App/lib/Phase/Phase1/Phase1Main.vp +++ b/App/lib/Phase/Phase1/Phase1Main.vp @@ -74,8 +74,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if log is not None: log.banner("Phase1: 扫描 includes(按需翻译)") - # 扫描 includes 目录 + # 扫描 includes 目录(含每文件 SHA1 计算计时) + t_scan0: t.CUInt64T = w32.win32base.GetTickCount64() result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) + t_scan1: t.CUInt64T = w32.win32base.GetTickCount64() + fb_scan: str = VLogger.fmt_buf() + if fb_scan is not None: + viperlib.snprintf(fb_scan, 1024, + "扫描 includes 总耗时: %llu ms (%d 文件, 含 SHA1 计算)", + t_scan1 - t_scan0, result.Count if result is not None else 0) + VLogger.info(fb_scan, "Phase1") if result is None: VLogger.error("扫描失败", "Phase1") return 1 @@ -126,6 +134,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_registered: int = 0 p1a_skipped: int = 0 p1a_failed: int = 0 + # 1a-pre 阶段总耗时起点(GetTickCount64,ms;对齐 Phase2 计时插桩) + t_pre0: t.CUInt64T = w32.win32base.GetTickCount64() for i in range(result.Count): # 文件级 mark:import 依赖收集完(deps 写盘 + PopulateIncludesDeps @@ -259,6 +269,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 回收本文件 AST/lexer/token(deps 已写盘、依赖存储器已深拷贝) mb.reset_to(scan_mark_a) + # 1a-pre 阶段总耗时(全部 includes 文件读+parse+import 依赖扫描) + t_pre1: t.CUInt64T = w32.win32base.GetTickCount64() + fb_pre: str = VLogger.fmt_buf() + if fb_pre is not None: + viperlib.snprintf(fb_pre, 1024, "1a-pre 总耗时: %llu ms (%d 文件扫描 import 依赖)", + t_pre1 - t_pre0, result.Count) + VLogger.info(fb_pre, "Phase1") + if p1a_failed > 0: fb_p1apre: str = VLogger.fmt_buf() if fb_p1apre is not None: @@ -397,6 +415,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 遍历文件:优先使用拓扑顺序,回退到字母序 iter_count: int = topo_count if topo_count > 0 else result.Count + # 1a 阶段总耗时起点(struct/enum/union 注册) + t_a0: t.CUInt64T = w32.win32base.GetTickCount64() for i in range(iter_count): # 每文件级 mark:翻译完回收该文件 AST(注册数据在 perm_pool) p1a_mark: t.CSizeT = mb.mark() @@ -430,6 +450,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_skp += 1 continue + # 单文件计时起点(读文件 + parse 段,定位 1a 卡点) + t_fa0: t.CUInt64T = w32.win32base.GetTickCount64() + # 读取文件内容 file_path_r: str = entry_r.Path f_r: fileio.File | t.CPtr = fileio.File(file_path_r, fileio.MODE.R) @@ -468,6 +491,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_fl += 1 continue + # 计时点 1:读文件 + parse 完成 + t_fa1: t.CUInt64T = w32.win32base.GetTickCount64() + # 创建 Translator,设置 _declare_only=1(注册 struct/enum/union) tr_r: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() if tr_r is None: @@ -484,11 +510,22 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, GenApi.clear_ctypedefs() GenApi.reset_visible_structs(mb, 0) ret_r: int = tr_r.translate(tree_r) + # 计时点 2:translate(declare=1) 返回 + t_fa2: t.CUInt64T = w32.win32base.GetTickCount64() if ret_r != 0: p1a_fl += 1 else: p1a_reg += 1 + # 逐文件进度日志(parse/translate 两段,定位 1a 卡点) + fb_1a: str = VLogger.fmt_buf() + if fb_1a is not None: + viperlib.snprintf(fb_1a, 1024, + "[%d/%d] 1a: %s (parse=%llu ms, translate=%llu ms, total=%llu ms)", + i + 1, iter_count, file_path_r, + t_fa1 - t_fa0, t_fa2 - t_fa1, t_fa2 - t_fa0) + VLogger.info(fb_1a, "Phase1") + # 释放 Translator 的 C malloc 资源 if tr_r._global_names is not None: stdlib.free(tr_r._global_names) @@ -500,6 +537,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # reset 不影响;IncludesScanner result 等在 p1a_mark 前分配同样保留) mb.reset_to(p1a_mark) + # 1a 阶段总耗时(struct/enum/union 注册,含每文件读+parse+翻译声明) + t_a1: t.CUInt64T = w32.win32base.GetTickCount64() + fb_a: str = VLogger.fmt_buf() + if fb_a is not None: + viperlib.snprintf(fb_a, 1024, "1a 总耗时: %llu ms (注册 struct/enum, reg=%d skp=%d)", + t_a1 - t_a0, p1a_reg, p1a_skp) + VLogger.info(fb_a, "Phase1") + if p1a_fl > 0: fb_p1ar: str = VLogger.fmt_buf() if fb_p1ar is not None: @@ -544,6 +589,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # ============================================================ p1bpre_failed: int = 0 dir_len_pv: t.CSizeT = string.strlen(temp_dir) + # 1b-pre 阶段总耗时起点(vpi 预生成) + t_pv0: t.CUInt64T = w32.win32base.GetTickCount64() for i_pv in range(result.Count): entry_addr_pv: t.CUInt64T = t.CUInt64T(result.Entries) + i_pv * entry_size entry_pv: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_pv, t.CPtr)) @@ -620,6 +667,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 回收本文件 AST(vpi 已写盘,AST 不再需要;mark1 后无其他持久数据) mb.reset_to(mark1) + # 1b-pre 阶段总耗时(vpi 预生成) + t_pv1: t.CUInt64T = w32.win32base.GetTickCount64() + fb_pv: str = VLogger.fmt_buf() + if fb_pv is not None: + viperlib.snprintf(fb_pv, 1024, "1b-pre 总耗时: %llu ms (vpi 预生成)", + t_pv1 - t_pv0) + VLogger.info(fb_pv, "Phase1") + if p1bpre_failed > 0: fb_p1bpre: str = VLogger.fmt_buf() if fb_p1bpre is not None: @@ -632,6 +687,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, translated: int = 0 skipped: int = 0 failed: int = 0 + # 1b 阶段总耗时起点(全量翻译) + t_b0: t.CUInt64T = w32.win32base.GetTickCount64() loop_n_b: int = result.Count if topo_count > 0: @@ -705,6 +762,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(stub_path) + # 单文件计时起点:读文件(parse/translate/dump 三段,对齐 Phase2 插桩) + t_f0: t.CUInt64T = w32.win32base.GetTickCount64() + # 读取文件内容 file_path: str = entry.Path f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R) @@ -762,6 +822,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, failed += 1 continue + # 计时点 1:读文件 + lexer/tokenize/parse 完成 + t_f1: t.CUInt64T = w32.win32base.GetTickCount64() + # 文本扫描注册 CTypedef(stdint.vp 崩溃根因修复):AST 字符串字段 # (attr/id)token 零拷贝指向 src_buf,跨阶段内容不稳定(悬空), # ScanCTypedefDeclarations 读 attr 会 0xC0000005。改用源码文本逐行 @@ -769,18 +832,27 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, GenApi.RegisterCTypedefFromSource(mb, src_buf) # 生成 .vpi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter) - pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE) - if pyi_buf is not None: - pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE) - if pyi_pos > 0: - pyi_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi") - if pyi_path is not None: - pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.W) - if not pf.closed: - pf.write(pyi_buf, pyi_pos) - pf.close() - stdlib.free(pyi_path) - stdlib.free(pyi_buf) + # 1b-pre 已为所有待翻译文件生成 vpi,此处仅兜底:vpi 缺失才生成 + # (消除 1b-pre 与 1b 对同一文件的重复 _GeneratePyiFromAst 工作) + vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi") + if vpi_path_1b is not None: + vf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.R) + vpi_exists_1b: int = 0 + if not vf_1b.closed: + vf_1b.close() + vpi_exists_1b = 1 + if vpi_exists_1b == 0: + pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE) + if pyi_buf is not None: + pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst( + mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE) + if pyi_pos > 0: + pf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.W) + if not pf_1b.closed: + pf_1b.write(pyi_buf, pyi_pos) + pf_1b.close() + stdlib.free(pyi_buf) + stdlib.free(vpi_path_1b) # 翻译 AST → LLVM IR tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() @@ -810,6 +882,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.exit(1) ret: int = tr.translate(tree) + # 计时点 2:translate 返回 + t_f2: t.CUInt64T = w32.win32base.GetTickCount64() # 诊断:translate 正常返回(若挂起在 translate 内部,此行不会打印, # 用于区分"translate 内部挂起"与"后续 dump 挂起") if ret == 0: @@ -891,6 +965,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(text_path_p1) stdlib.free(text_buf) + # 计时点 3:dump(stub+text)完成;输出单文件三段耗时 + t_f3: t.CUInt64T = w32.win32base.GetTickCount64() + fb_t: str = VLogger.fmt_buf() + if fb_t is not None: + viperlib.snprintf(fb_t, 1024, + "[%d/%d] 翻译: %s (parse=%llu ms, translate=%llu ms, dump=%llu ms, total=%llu ms)", + i + 1, loop_n_b, file_path, + t_f1 - t_f0, t_f2 - t_f1, t_f3 - t_f2, t_f3 - t_f0) + VLogger.info(fb_t, "Phase1") + # save dependencies(_imported_modules) for Phase B deps_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt") if deps_path_p1 is not None: @@ -945,6 +1029,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, VLogger.error(fb, "Phase1") stdlib.exit(1) + # 1b 阶段总耗时(全量翻译 + dump) + t_b1: t.CUInt64T = w32.win32base.GetTickCount64() + fb_b: str = VLogger.fmt_buf() + if fb_b is not None: + viperlib.snprintf(fb_b, 1024, "1b 总耗时: %llu ms (全量翻译, trans=%d skip=%d fail=%d)", + t_b1 - t_b0, translated, skipped, failed) + VLogger.info(fb_b, "Phase1") + # 释放 Phase 1b 使用的拓扑排序资源 if topo_order is not None: mb.free(topo_order) diff --git a/App/lib/Phase/Phase1/Phase1Scan.vp b/App/lib/Phase/Phase1/Phase1Scan.vp index ef137bd..033cb15 100644 --- a/App/lib/Phase/Phase1/Phase1Scan.vp +++ b/App/lib/Phase/Phase1/Phase1Scan.vp @@ -4,6 +4,7 @@ import string import stdlib import memhub import viperlib +import sys import w32.fileio as fileio import w32.win32file import w32.win32base @@ -379,8 +380,17 @@ def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, # ============================================================ def _ScanTreeForContainers(node: ast.AST | t.CPtr, need_list_box: t.CPtr, - need_dict_box: t.CPtr): + need_dict_box: t.CPtr, + depth: int = 0): """递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用""" + # 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限 + # 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。 + if depth > 128: + fb_stc: str = VLogger.fmt_buf() + if fb_stc is not None: + viperlib.snprintf(fb_stc, 256, "容器扫描递归过深(AST 环或嵌套超过 128 层)") + VLogger.error(fb_stc, "Phase1") + sys.exit(1) if node is None: return k_s: int = node.kind() @@ -393,21 +403,21 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, need_dict_box[0] = 1 elif k_s == ast.ASTKind.Subscript: sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) - _ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box) - _ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box) + _ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1) + _ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.BinOp: bop_s: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) - _ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box) - _ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box) + _ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box, depth + 1) + _ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Call: cl_s2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) - _ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box) + _ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1) cl_args_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.args if cl_args_s is not None: na_s: t.CSizeT = len(cl_args_s) i_a2: t.CSizeT for i_a2 in range(na_s): - _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box) + _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1) cl_kws_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.keywords if cl_kws_s is not None: nk_s: t.CSizeT = len(cl_kws_s) @@ -415,23 +425,23 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, for i_k2 in range(nk_s): kw_s: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(cl_kws_s.get(i_k2)) if kw_s is not None: - _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box) + _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Assign: asn_s: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node) - _ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box) + _ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1) tgts_s: list[ast.AST | t.CPtr] | t.CPtr = asn_s.targets if tgts_s is not None: nt_s: t.CSizeT = len(tgts_s) i_t2: t.CSizeT for i_t2 in range(nt_s): - _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box) + _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.AnnAssign: aa_s2: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) - _ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box) - _ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box) + _ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1) + _ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.FunctionDef: fn_s: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node) - _ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box) + _ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1) fn_args_s: ast.Arguments | t.CPtr = fn_s.args if fn_args_s is not None: fa_s: list[ast.AST | t.CPtr] | t.CPtr = fn_args_s.args @@ -441,7 +451,7 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, for i_f2 in range(nf_s): arg_s: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(fa_s.get(i_f2)) if arg_s is not None: - _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box) + _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.ClassDef: cd_s: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node) bases_s: list[ast.AST | t.CPtr] | t.CPtr = cd_s.bases @@ -449,10 +459,10 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, nb_s: t.CSizeT = len(bases_s) i_b2: t.CSizeT for i_b2 in range(nb_s): - _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box) + _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Attribute: at_s: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) - _ScanTreeForContainers(at_s.value, need_list_box, need_dict_box) + _ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Tuple: tu_s: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node) elts_s: list[ast.AST | t.CPtr] | t.CPtr = tu_s.elts @@ -460,25 +470,25 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, ne_s: t.CSizeT = len(elts_s) i_e2: t.CSizeT for i_e2 in range(ne_s): - _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box) + _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.UnaryOp: uop_s: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) - _ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box) + _ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Expr: ex_s: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node) - _ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box) + _ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Return: rt_s: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node) - _ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box) + _ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.Compare: cp_s: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node) - _ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box) + _ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1) cps_s: list[ast.AST | t.CPtr] | t.CPtr = cp_s.comparators if cps_s is not None: nc_s: t.CSizeT = len(cps_s) i_c2: t.CSizeT for i_c2 in range(nc_s): - _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box) + _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.BoolOp: bo_s: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node) bvs_s: list[ast.AST | t.CPtr] | t.CPtr = bo_s.values @@ -486,7 +496,7 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, nv_s: t.CSizeT = len(bvs_s) i_v2: t.CSizeT for i_v2 in range(nv_s): - _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box) + _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1) elif k_s == ast.ASTKind.List: # [] 字面量 need_list_box[0] = 1 @@ -499,4 +509,4 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, n_s: t.CSizeT = len(ch_s) ci_s: t.CSizeT for ci_s in range(n_s): - _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box) + _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1) diff --git a/App/lib/Projectrans/Config.vp b/App/lib/Projectrans/Config.vp index f2c61c4..959bc9c 100644 --- a/App/lib/Projectrans/Config.vp +++ b/App/lib/Projectrans/Config.vp @@ -35,6 +35,7 @@ LinkerFlags: str = None # 链接器参数(空格分隔,从 linker. LinkerOutput: str = None TargetTriple: str = None TargetDataLayout: str = None +ProjectDir: str = None # project.vpj 所在目录(resolve_paths 设置) Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级) StrictMode: int = 0 IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景) @@ -146,7 +147,7 @@ def Load_project_config(path: str) -> int: # 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs) includes_val: JsonValue | t.CPtr = root["includes"] if includes_val is not None and includes_val.is_array(): - arr_len: t.CSizeT = includes_val.__len__() + arr_len: t.CSizeT = len(includes_val) inc_buf: bytes = stdlib.malloc(2048) if inc_buf is not None: inc_buf[0] = '\0' @@ -186,12 +187,10 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str: Returns: 空格分隔的字符串,或 None 如果 flags_val 为 None 或非数组或空数组 """ - if flags_val is None: - return None - if not flags_val.is_array(): - return None + if flags_val is None: return None + if not flags_val.is_array(): return None - arr_len: t.CSizeT = flags_val.__len__() + arr_len: t.CSizeT = len(flags_val) if arr_len == 0: return None @@ -385,11 +384,14 @@ def resolve_paths(project_dir: str) -> int: 0 表示成功,非 0 表示失败 """ global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs + global ProjectDir if project_dir is None: return 1 if _mbuddy is None: return 1 + ProjectDir = project_dir + SourceDir = _join_path(SourceDir, project_dir) BuildDir = _join_path(BuildDir, project_dir) @@ -441,9 +443,103 @@ def resolve_paths(project_dir: str) -> int: IncludesDirs = id_out if IncludesDir is not None: IncludesDir = _join_path(IncludesDir, project_dir) + # linker.flags 中 `-T <脚本>` 的链接脚本路径同样基于 project_dir 解析, + # 否则 subprocess 按进程 cwd(TransPyC/)找 linker.ld → ld.lld 报 + # "cannot find linker script linker.ld" + _resolve_linker_t_paths(project_dir) return 0 +def _join_path_abs(rel: str, project_dir: str) -> str: + """将相对路径基于 project_dir 拼接为绝对路径(用于 -T 链接脚本等) + + 已为绝对路径(/ \\ 开头)或盘符形式(x:)时原样返回。 + """ + if rel is None: + return None + rel_len: t.CSizeT = string.strlen(rel) + if rel_len == 0: + return rel + if rel[0] == '/' or rel[0] == '\\': + return rel + if rel_len >= 2 and rel[1] == ':': + return rel + dir_len: t.CSizeT = string.strlen(project_dir) + if dir_len == 0: + return rel + total_len: t.CSizeT = dir_len + 1 + rel_len + 1 + buf: str = _mbuddy.alloc(total_len) + if buf is None: + return rel + viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel) + return buf + + +def _resolve_linker_t_paths(project_dir: str) -> None: + """将 LinkerFlags 中 `-T <相对路径>` 的链接脚本路径解析为绝对路径 + + ld.lld 的 -T 参数若是相对路径,将按 subprocess 进程 cwd 解析而找不到 + 脚本。逐 token 扫描 LinkerFlags,遇 -T 其后 token 若非绝对路径则基于 + project_dir 拼接(与 resolve_paths 对其他相对路径的处理保持一致)。 + """ + global LinkerFlags + if LinkerFlags is None or project_dir is None: + return + fl_total: t.CSizeT = string.strlen(LinkerFlags) + if fl_total == 0: + return + out: str = _mbuddy.alloc(fl_total * 2 + 512) + if out is None: + return + out[0] = '\0' + out_pos: t.CSizeT = 0 + i: t.CSizeT = 0 + prev_is_t: int = 0 # 前一 token 是否为 -T + while i < fl_total: + # 跳过前导空格 + while i < fl_total and LinkerFlags[i] == ' ': + i += 1 + if i >= fl_total: + break + start: t.CSizeT = i + while i < fl_total and LinkerFlags[i] != ' ': + i += 1 + tok_len: t.CSizeT = i - start + is_t_flag: int = 0 + if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T': + is_t_flag = 1 + # 输出分隔符(前一 token 之后) + if out_pos > 0: + out[out_pos] = ' ' + out_pos += 1 + if is_t_flag: + string.strcpy(out + out_pos, "-T") + out_pos += 2 + elif prev_is_t != 0: + # -T 后的脚本路径:非绝对路径则基于 project_dir 拼接 + tok_buf: str = _mbuddy.alloc(tok_len + 1) + if tok_buf is not None: + string.memcpy(tok_buf, LinkerFlags + start, tok_len) + tok_buf[tok_len] = '\0' + abs_path: str = _join_path_abs(tok_buf, project_dir) + if abs_path is not None: + p_len: t.CSizeT = string.strlen(abs_path) + string.strcpy(out + out_pos, abs_path) + out_pos += p_len + else: + string.memcpy(out + out_pos, LinkerFlags + start, tok_len) + out_pos += tok_len + else: + string.memcpy(out + out_pos, LinkerFlags + start, tok_len) + out_pos += tok_len + else: + string.memcpy(out + out_pos, LinkerFlags + start, tok_len) + out_pos += tok_len + prev_is_t = is_t_flag + out[out_pos] = '\0' + LinkerFlags = out + + def get_includes_binary_dir() -> str: """获取 includes.binary 目录路径(includes 目录 + 缓存后缀) diff --git a/App/lib/Projectrans/Utils.vp b/App/lib/Projectrans/Utils.vp index 7327866..dddc676 100644 --- a/App/lib/Projectrans/Utils.vp +++ b/App/lib/Projectrans/Utils.vp @@ -89,16 +89,18 @@ def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str: # ============================================================ -# is_vp_or_py_at - 检查路径段是否以 .vp/.py 后缀结尾 +# is_vp_at - 检查路径段是否以 .vp 后缀结尾 # ============================================================ -def is_vp_or_py_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int: - """检查 p[start, start+plen) 是否以 .vp/.py 后缀结尾(1=是,0=否)""" +def is_vp_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int: + """检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。 + + 不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。 + """ if p is None or plen <= 3: return 0 base: t.CSizeT = start + plen - 3 - if p[base] == '.': - if p[base + 1] == 'v' and p[base + 2] == 'p': return 1 - if p[base + 1] == 'p' and p[base + 2] == 'y': return 1 + if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p': + return 1 return 0 @@ -167,8 +169,8 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str: if psep_ch == '/' or psep_ch == '\\': path_start = prefix_len plen = plen - prefix_len - # 去掉 .vp 或 .py 后缀(.vp 为 Viper 源,.py 向后兼容) - if is_vp_or_py_at(path, path_start, plen) != 0: + # 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py) + if is_vp_at(path, path_start, plen) != 0: plen -= 3 if plen == 0: return None diff --git a/App/lib/Stub/StubGen/Converter.vp b/App/lib/Stub/StubGen/Converter.vp index 1543ac9..1d94d06 100644 --- a/App/lib/Stub/StubGen/Converter.vp +++ b/App/lib/Stub/StubGen/Converter.vp @@ -1,4 +1,5 @@ import lib.core.Gen.GenApi as GenApi +import lib.core.VLogger as VLogger import t, c from stdint import * import ast @@ -6,6 +7,7 @@ import string import memhub import viperlib import stdlib +import sys # ============================================================ # StubGen.Converter - vpi 存根文件生成器 @@ -184,7 +186,7 @@ def _eval_cdefine_value_cache(node: ast.AST | t.CPtr) -> t.CInt64T: return _eval_binop_int(node) if k == ast.ASTKind.Call: cl_c: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) - if cl_c.args is not None and cl_c.args.__len__() > 0: + if cl_c.args is not None and len(cl_c.args) > 0: return _eval_cdefine_value_cache(cl_c.args.get(0)) return 0 if k == ast.ASTKind.Name: @@ -244,8 +246,18 @@ def _PyiIndent(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 支持: Name, Attribute, Subscript, BinOp(|), Constant(前向引用), Tuple # ============================================================ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, - annot: ast.AST | t.CPtr) -> t.CSizeT: + annot: ast.AST | t.CPtr, + depth: int = 0) -> t.CSizeT: """递归生成类型注解字符串,返回新的 pos""" + # 递归深度守卫:AST 损坏成自引用环(Attribute.value/Subscript.slice/ + # BinOp.left 等回指祖先)时无限递归直至栈耗尽(Phase1 vpi 生成最先 + # 执行,早于翻译阶段)。fail-fast 终止,不静默回退。 + if depth > 64: + fb_pt: str = VLogger.fmt_buf() + if fb_pt is not None: + viperlib.snprintf(fb_pt, 256, "vpi 类型注解递归过深(AST 环或嵌套超过 64 层)") + VLogger.error(fb_pt, "StubGen") + sys.exit(1) if annot is None: return _PyiEmit(buf, buf_size, pos, "t.CInt") @@ -261,7 +273,7 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # Attribute: 属性引用 (如 t.CInt, memhub.MemBuddy) if k == ast.ASTKind.Attribute: at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot) - pos = _PyiTypeStr(buf, buf_size, pos, at.value) + pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1) pos = _PyiEmit(buf, buf_size, pos, ".") if at.attr is not None: pos = _PyiEmit(buf, buf_size, pos, at.attr) @@ -270,18 +282,18 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # Subscript: 下标类型 (如 list[int], t.CArray[elem, count]) if k == ast.ASTKind.Subscript: sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot) - pos = _PyiTypeStr(buf, buf_size, pos, sub.value) + pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1) pos = _PyiEmit(buf, buf_size, pos, "[") - pos = _PyiTypeStr(buf, buf_size, pos, sub.slice) + pos = _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1) pos = _PyiEmit(buf, buf_size, pos, "]") return pos # BinOp: 联合类型 (如 int | str, File | t.CPtr) if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot) - pos = _PyiTypeStr(buf, buf_size, pos, bop.left) + pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1) pos = _PyiEmit(buf, buf_size, pos, " | ") - pos = _PyiTypeStr(buf, buf_size, pos, bop.right) + pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1) return pos # Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count) @@ -313,30 +325,30 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(annot) if tup.elts is not None: tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts - tn: t.CSizeT = tup_elts.__len__() + tn: t.CSizeT = len(tup_elts) ti: t.CSizeT = 0 while ti < tn: if ti > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") el: ast.AST | t.CPtr = tup_elts.get(ti) - pos = _PyiTypeStr(buf, buf_size, pos, el) + pos = _PyiTypeStr(buf, buf_size, pos, el, depth + 1) ti += 1 return pos # Call: 函数调用表达式 (用于装饰器如 @t.NoVTable()) if k == ast.ASTKind.Call: cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(annot) - pos = _PyiTypeStr(buf, buf_size, pos, cl.func) + pos = _PyiTypeStr(buf, buf_size, pos, cl.func, depth + 1) pos = _PyiEmit(buf, buf_size, pos, "(") if cl.args is not None: cl_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args - can: t.CSizeT = cl_args.__len__() + can: t.CSizeT = len(cl_args) cai: t.CSizeT = 0 while cai < can: if cai > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") ca: ast.AST | t.CPtr = cl_args.get(cai) - pos = _PyiTypeStr(buf, buf_size, pos, ca) + pos = _PyiTypeStr(buf, buf_size, pos, ca, depth + 1) cai += 1 pos = _PyiEmit(buf, buf_size, pos, ")") return pos @@ -345,6 +357,79 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, return _PyiEmit(buf, buf_size, pos, "t.CInt") +# ============================================================ +# _PyiDefaultStr - 生成默认值表达式文本 +# +# 用于 vpi 签名 '= ' 输出(如 depth: int = 0)。 +# vpi stub 必须保留默认值:VpiReader 解析后注册默认参数表, +# 跨模块缺参调用(如 resolve_annotation_type 的 depth=0)才能 +# 用默认值填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。 +# 支持: Constant(int/str/bool/None), Name(CDefine 常量), UnaryOp。 +# ============================================================ +def _PyiDefaultStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, + node: ast.AST | t.CPtr, + depth: int = 0) -> t.CSizeT: + """生成默认值表达式文本到 buf,返回新的 pos""" + # 递归深度守卫:AST 损坏成自引用环时 fail-fast 终止(同 _PyiTypeStr) + if depth > 64: + fb_pd: str = VLogger.fmt_buf() + if fb_pd is not None: + viperlib.snprintf(fb_pd, 256, "vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)") + VLogger.error(fb_pd, "StubGen") + sys.exit(1) + if node is None: + return pos + + k: int = node.kind() + # Constant: 数字/字符串/bool/None 直接输出字面量 + if k == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR: + num_buf_dv: str = stdlib.malloc(24) + if num_buf_dv is not None: + viperlib.snprintf(num_buf_dv, 24, "%d", cn.int_val) + pos = _PyiEmit(buf, buf_size, pos, num_buf_dv) + stdlib.free(num_buf_dv) + return pos + if cn.const_kind == ast.CONST_STR: + pos = _PyiEmit(buf, buf_size, pos, '"') + if cn.str_val is not None: + pos = _PyiEmit(buf, buf_size, pos, cn.str_val) + return _PyiEmit(buf, buf_size, pos, '"') + if cn.const_kind == ast.CONST_BOOL: + if cn.int_val != 0: + return _PyiEmit(buf, buf_size, pos, "True") + return _PyiEmit(buf, buf_size, pos, "False") + if cn.const_kind == ast.CONST_NONE: + return _PyiEmit(buf, buf_size, pos, "None") + return pos + + # Name: CDefine 常量名(如 share: ULONG = SHARE_READ) + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is not None: + return _PyiEmit(buf, buf_size, pos, nm.id) + return pos + + # UnaryOp: 一元运算(如 -1, ~0) + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + if uop.op == ast.OpKind.USub: + pos = _PyiEmit(buf, buf_size, pos, "-") + elif uop.op == ast.OpKind.UAdd: + pos = _PyiEmit(buf, buf_size, pos, "+") + elif uop.op == ast.OpKind.Invert: + pos = _PyiEmit(buf, buf_size, pos, "~") + else: + return pos + if uop.operand is not None: + return _PyiDefaultStr(buf, buf_size, pos, uop.operand, depth + 1) + return pos + + # 其它(BinOp 等复杂表达式):不输出,保持无默认值(fail-fast 由调用侧兜底) + return pos + + # ============================================================ # _PyiFunction - 生成函数签名 # @@ -357,7 +442,7 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 装饰器 if fn.decorator_list is not None: fn_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = fn.decorator_list - dn: t.CSizeT = fn_decorator_list.__len__() + dn: t.CSizeT = len(fn_decorator_list) di: t.CSizeT = 0 while di < dn: dec: ast.AST | t.CPtr = fn_decorator_list.get(di) @@ -383,7 +468,17 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 位置参数 if args.args is not None: args_args: list[ast.AST | t.CPtr] | t.CPtr = args.args - an: t.CSizeT = args_args.__len__() + an: t.CSizeT = len(args_args) + # 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。 + # vpi stub 必须输出 '= ',否则 VpiReader 解析后默认参数 + # 表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。 + # 注意:arg_defaults 必须内联声明(list[ast.AST|CPtr] | t.CPtr 联合 + # 接收者的 .get 会被 TPC/TPV 误分派——预声明 + 重赋值会解析成 + # list[int].get 返回 i32 截断指针崩溃;内联声明走 list[ast.ASTptr].get)。 + arg_defaults: list[ast.AST | t.CPtr] | t.CPtr = args.defaults + dn: t.CSizeT = 0 + if arg_defaults is not None: + dn = len(arg_defaults) ai: t.CSizeT = 0 while ai < an: if first == 0: @@ -395,6 +490,12 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, if ag.annotation is not None: pos = _PyiEmit(buf, buf_size, pos, ": ") pos = _PyiTypeStr(buf, buf_size, pos, ag.annotation) + if arg_defaults is not None and an - dn <= ai: + di: t.CSizeT = ai - (an - dn) + dnode: ast.AST | t.CPtr = arg_defaults.get(di) + if dnode is not None: + pos = _PyiEmit(buf, buf_size, pos, " = ") + pos = _PyiDefaultStr(buf, buf_size, pos, dnode) ai += 1 # *args @@ -442,7 +543,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 装饰器 if cls.decorator_list is not None: cls_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = cls.decorator_list - dcn: t.CSizeT = cls_decorator_list.__len__() + dcn: t.CSizeT = len(cls_decorator_list) dci: t.CSizeT = 0 while dci < dcn: ddec: ast.AST | t.CPtr = cls_decorator_list.get(dci) @@ -461,7 +562,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # PEP 695 类型参数 (class Foo[T]:) if cls.type_params is not None: cls_type_params: list[ast.AST | t.CPtr] | t.CPtr = cls.type_params - tpn: t.CSizeT = cls_type_params.__len__() + tpn: t.CSizeT = len(cls_type_params) if tpn > 0: pos = _PyiEmit(buf, buf_size, pos, "[") tpi: t.CSizeT = 0 @@ -477,7 +578,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 基类 if cls.bases is not None: cls_bases: list[ast.AST | t.CPtr] | t.CPtr = cls.bases - bn: t.CSizeT = cls_bases.__len__() + bn: t.CSizeT = len(cls_bases) if bn > 0: pos = _PyiEmit(buf, buf_size, pos, "(") bi: t.CSizeT = 0 @@ -496,7 +597,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, if cls.children is not None: cls_children: list[ast.AST | t.CPtr] | t.CPtr = cls.children - cn: t.CSizeT = cls_children.__len__() + cn: t.CSizeT = len(cls_children) ci: t.CSizeT = 0 while ci < cn: stmt: ast.AST | t.CPtr = cls_children.get(ci) @@ -510,7 +611,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0: if mfn.children is not None: mfn_children: list[ast.AST | t.CPtr] | t.CPtr = mfn.children - icn: t.CSizeT = mfn_children.__len__() + icn: t.CSizeT = len(mfn_children) ici: t.CSizeT = 0 while ici < icn: ibody: ast.AST | t.CPtr = mfn_children.get(ici) @@ -595,17 +696,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, # = 1<<0 = 1、MASK = 0-1 = -1 → 跨模块读回错误常量 → 索引越界崩溃根因)。 # 必须 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时创建局部 # 变量(_lookup_cdefine_for_pyi 读到的仍是模块级 None → 缓存永不命中, - # 追加58 首版失效根因)。 - global _g_pyi_cdef_names - global _g_pyi_cdef_vals - global _g_pyi_cdef_count + global _g_pyi_cdef_names, _g_pyi_cdef_vals, _g_pyi_cdef_count if tree.children is not None: pre_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children - pcn: t.CSizeT = pre_children.__len__() + pcn: t.CSizeT = len(pre_children) # 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配) _g_pyi_cdef_count = 0 - pci0: t.CSizeT = 0 - while pci0 < pcn: + for pci0 in range(pcn): stmt_pc0: ast.AST | t.CPtr = pre_children.get(pci0) if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign: aa_pc0: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc0) @@ -616,14 +713,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, ann_pc0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(aa_pc0.annotation) if ann_pc0.attr is not None and string.strcmp(ann_pc0.attr, "CDefine") == 0: _g_pyi_cdef_count += 1 - pci0 += 1 if _g_pyi_cdef_count > 0: _g_pyi_cdef_names = (str | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8)) _g_pyi_cdef_vals = (t.CInt64T | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8)) # 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项) pci: t.CSizeT = 0 cdi: t.CInt = 0 - while pci < pcn: + for pci in range(pcn): stmt_pc: ast.AST | t.CPtr = pre_children.get(pci) if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign: aa_pc: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc) @@ -638,7 +734,6 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, slot_pc[0] = tgt_pc.id _g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value) cdi += 1 - pci += 1 # 文件头 pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n") @@ -649,29 +744,25 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, return pos tree_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children - n: t.CSizeT = tree_children.__len__() + n: t.CSizeT = len(tree_children) # 第一遍扫描:检测 import t/c has_import_t: int = 0 has_import_c: int = 0 - i: t.CSizeT = 0 - while i < n: + for i in range(n): stmt: ast.AST | t.CPtr = tree_children.get(i) if stmt is not None and stmt.kind() == ast.ASTKind.Import: imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(stmt) if imp.names is not None: imp_names: list[ast.AST | t.CPtr] | t.CPtr = imp.names - an: t.CSizeT = imp_names.__len__() - ai: t.CSizeT = 0 - while ai < an: + an: t.CSizeT = len(imp_names) + for ai in range(an): al: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp_names.get(ai)) if al is not None and al.name is not None: if string.strcmp(al.name, "t") == 0: has_import_t = 1 elif string.strcmp(al.name, "c") == 0: has_import_c = 1 - ai += 1 - i += 1 # 补充缺失的 import t/c if has_import_t == 0 and has_import_c == 0: @@ -684,8 +775,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, "\n") # 第二遍扫描:生成声明 - i = 0 - while i < n: + for i in range(n): stmt = tree_children.get(i) if stmt is not None: k: int = stmt.kind() @@ -696,7 +786,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, "import ") if imp2.names is not None: imp2_names: list[ast.AST | t.CPtr] | t.CPtr = imp2.names - an2: t.CSizeT = imp2_names.__len__() + an2: t.CSizeT = len(imp2_names) ai2: t.CSizeT = 0 while ai2 < an2: if ai2 > 0: @@ -723,7 +813,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, " import ") if ifr.names is not None: ifr_names: list[ast.AST | t.CPtr] | t.CPtr = ifr.names - an3: t.CSizeT = ifr_names.__len__() + an3: t.CSizeT = len(ifr_names) ai3: t.CSizeT = 0 while ai3 < an3: if ai3 > 0: @@ -822,7 +912,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, call_cv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aa.value) if call_cv.args is not None: call_args_cv: list[ast.AST | t.CPtr] | t.CPtr = call_cv.args - if call_args_cv.__len__() > 0: + if len(call_args_cv) > 0: first_arg: ast.AST | t.CPtr = call_args_cv.get(0) if first_arg is not None: arg_kind: int = first_arg.kind() @@ -874,7 +964,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, asg: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(stmt) if asg.targets is not None and asg.value is not None: asg_targets: list[ast.AST | t.CPtr] | t.CPtr = asg.targets - atn: t.CSizeT = asg_targets.__len__() + atn: t.CSizeT = len(asg_targets) if atn == 1: atg: ast.AST | t.CPtr = asg_targets.get(0) if atg is not None and atg.kind() == ast.ASTKind.Name: @@ -909,8 +999,6 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, "None") pos = _PyiEmit(buf, buf_size, pos, "\n") - i += 1 - # NUL 终止 if pos < buf_size: buf[pos] = 0 diff --git a/App/lib/Stub/StubMerger/StubMergerBuild.vp b/App/lib/Stub/StubMerger/StubMergerBuild.vp index 6e34a0f..01747fa 100644 --- a/App/lib/Stub/StubMerger/StubMergerBuild.vp +++ b/App/lib/Stub/StubMerger/StubMergerBuild.vp @@ -25,6 +25,244 @@ import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo import lib.Stub.StubMerger.StubMergerIR as StubMergerIR import lib.Stub.StubMerger as StubMerger +# ============================================================ +# 已见符号表(BuildCombinedIR 去重 O(n²)→O(1)) +# +# Phase B 对每个源文件调用 BuildCombinedIR 组合 IR:原实现对每行 +# declare/global/type 都在不断增长的 out_buf 全量扫描去重 +# (_IsFuncDeclaredOrDefined 逐字节 strncmp、strstr 等),依赖最多时 +# (CallGenCall)单文件 merge 达 12s。改用定长槽位开放寻址哈希表 +# 记录 out_buf 中已活跃的行:去重查找 O(1)、注释行 O(1) 定位。 +# +# slot 布局(步长 _SEEN_SLOT): +# {u64 hash} {u64 pos} {u8 flags} {name[_SEEN_NAME_MAX]} {u8 nul} +# 空槽判定:hash==0(memset 全零初始化;FNV-1a 保证 >=2)。 +# flags:类型表 bit0=1 表示活跃行为完整定义(= type { / = type <), +# bit0=0 表示仅 opaque 声明;函数/全局表恒为 0。 +# pos:活跃行在 out_buf 中的起始位置(注释操作直接 out_buf[pos]=';')。 +# 注释行不从表中删除:注释后必然立即追加新行(更新 pos),表保持 +# "有活跃行"不变量。超长名(>= _SEEN_NAME_MAX)不登记,防御性回退。 +# 表满(count >= cap)fail-fast 报错:静默丢弃登记会致去重失效, +# 重复行追加 → llc redefinition(88410e59e3e5f98b .str 常量根因, +# 该模块合并 IR 实测 4486 个全局行 > 旧容量 4096,故全局表设 16384)。 +# ============================================================ +_SEEN_NAME_MAX: t.CDefine = 256 +_SEEN_SLOT: t.CDefine = 274 # 8(hash)+8(pos)+1(flags)+256(name)+1(nul) +_SEEN_FUNC_CAP: t.CDefine = 16384 +_SEEN_GLOBAL_CAP: t.CDefine = 16384 +_SEEN_TYPE_CAP: t.CDefine = 4096 +_SEEN_FNV_OFFSET: t.CUInt64T = 0xcbf29ce484222325 +_SEEN_FNV_PRIME: t.CUInt64T = 0x100000001b3 + +_seen_funcs: bytes # 已见函数签名表(@"sha1.name"( 等) +_seen_funcs_count: int +_seen_globals: bytes # 已见全局变量名表(@name) +_seen_globals_count: int +_seen_types: bytes # 已见类型名表(%name) +_seen_types_count: int +_seen_key_scratch: bytes # 注册键暂存(跨 truncation 窗口复制) + +def _SeenHash(key: str) -> t.CUInt64T: + """FNV-1a 64 位哈希(对齐 includes/hashtable.vp)""" + h: t.CUInt64T = _SEEN_FNV_OFFSET + p: str = key + while p[0] != 0: + h = h ^ t.CUInt64T(p[0]) + h = h * _SEEN_FNV_PRIME + p += 1 + if h < 2: + h = 2 + return h + +def _SeenFind(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT: + """已见表槽位查找:命中返回槽位下标;未命中返回 cap(哨兵)""" + if tbl is None or key is None: + return cap + mask: t.CSizeT = cap - 1 + idx: t.CSizeT = t.CSizeT(key_hash) & mask + i: t.CSizeT = 0 + while i < cap: + off: t.CSizeT = idx * _SEEN_SLOT + hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off) + sh: t.CUInt64T = hp[0] + if sh == 0: + return cap + if sh == key_hash: + nm: str = tbl + off + 17 + if string.strcmp(nm, key) == 0: + return idx + idx = (idx + 1) & mask + i += 1 + return cap + +def _SeenPresent(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> int: + """已见表查找:返回 0=不存在;>0=存在(返回值 = flags+1)""" + si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash) + if si >= cap: + return 0 + return t.CInt(tbl[si * _SEEN_SLOT + 16]) + 1 + +def _SeenInsert(tbl: bytes, cap: t.CSizeT, count: int, key: str, key_hash: t.CUInt64T, + flags: int, pos: t.CSizeT) -> int: + """已见表插入/更新:key 不存在则新登记(返回 count+1);已存在则 OR flags、更新 pos""" + if tbl is None or key is None: + return count + kl: t.CSizeT = string.strlen(key) + if kl >= _SEEN_NAME_MAX: + return count + if count >= cap: + fb_full: str = VLogger.fmt_buf() + if fb_full is not None: + viperlib.snprintf(fb_full, 1024, "已见符号表满(容量 %d),新增符号登记被丢弃——去重失效,必须增大容量", cap) + VLogger.error(fb_full, "StubMerger") + return count + mask: t.CSizeT = cap - 1 + idx: t.CSizeT = t.CSizeT(key_hash) & mask + i: t.CSizeT = 0 + while i < cap: + off: t.CSizeT = idx * _SEEN_SLOT + hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off) + sh: t.CUInt64T = hp[0] + if sh == 0: + hp[0] = key_hash + pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8) + pp[0] = t.CUInt64T(pos) + tbl[off + 16] = t.CChar(flags) + string.strcpy(tbl + off + 17, key) + return count + 1 + if sh == key_hash: + nm: str = tbl + off + 17 + if string.strcmp(nm, key) == 0: + pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8) + pp[0] = t.CUInt64T(pos) + tbl[off + 16] = t.CChar(t.CInt(tbl[off + 16]) | flags) + return count + idx = (idx + 1) & mask + i += 1 + return count + +def _SeenCommentOut(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T, + out_buf: bytes) -> int: + """注释 out_buf 中该符号的活跃行(行首改 ';'),返回 1=已注释 0=无""" + si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash) + if si >= cap: + return 0 + if t.CInt(tbl[si * _SEEN_SLOT + 16]) != 0: + return 0 # 类型表 flags=1:活跃行为完整定义,无可注释的 opaque + pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8) + p: t.CUInt64T = pp[0] + if out_buf is not None: + out_buf[t.CSizeT(p)] = ';' + return 1 + +def _SeenGetPos(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT: + """读取已见表条目记录的活跃行位置;不存在返回 cap(哨兵)""" + si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash) + if si >= cap: + return cap + pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8) + return t.CSizeT(pp[0]) + +def _RegisterStubLines(buf: bytes, buf_len: t.CSizeT, base_pos: t.CSizeT): + """把本地 stub(整体复制进 out_buf)中的 declare/global/type 全部登记到已见表 + + 本地 stub.ll 在 BuildCombinedIR 第 1 步整体 strcpy 进 out_buf,不做逐行 + 去重处理,若不同步登记,后续依赖的同名行查表未命中会重复追加 → + llc "redefinition of type/global"(7871cc1a... ll:758 根因)。 + 行位置 = base_pos + 行内偏移(复制是 1:1 保序),供注释操作定位。 + 同名重复行(如 61d41cf89...stub.ll 的 _g_gen 出现两次)在复制前将 + 行首改为 ';'(注释掉),表只指向首个活跃行(61d41cf89... ll:9853 根因); + 若先 opaque 后完整定义,则注释旧行、表升级指向完整定义行。 + """ + global _seen_funcs_count, _seen_globals_count, _seen_types_count + if buf is None: + return + pos: t.CSizeT = 0 + while pos < buf_len: + ls: t.CSizeT = pos + while pos < buf_len: + if buf[pos] == '\n': + break + pos += 1 + ll: t.CSizeT = pos - ls + if pos < buf_len: + pos += 1 + line_pos: t.CSizeT = base_pos + ls + # declare → 函数签名 @"name"( + if ll >= 8 and string.strncmp(buf + ls, "declare ", 8) == 0: + at: t.CSizeT = ls + 8 + while at < ls + ll: + if buf[at] == '@': + break + at += 1 + if at < ls + ll: + lp: t.CSizeT = at + 1 + while lp < ls + ll: + if buf[lp] == '(': + break + lp += 1 + if lp < ls + ll: + sv_f: t.CChar = buf[lp + 1] + buf[lp + 1] = '\0' + kh_f: t.CUInt64T = _SeenHash(buf + at) + if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, buf + at, kh_f) == 0: + _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, buf + at, kh_f, 0, line_pos) + else: + buf[ls] = ';' + buf[lp + 1] = sv_f + continue + # 全局 @name =(键 @name,截断于 '=' 前) + if ll > 0 and buf[ls] == '@': + # 字符串常量 @.str.{sha1}.{N}:本地 stub 的 external 声明是死代码 + # (本地 text.ll 有 internal constant 定义),注释掉避免与定义重复 + if ll >= 5 and buf[ls + 1] == '.' and buf[ls + 2] == 's' and buf[ls + 3] == 't' and buf[ls + 4] == 'r': + buf[ls] = ';' + continue + eq: t.CSizeT = ls + 1 + while eq < ls + ll: + if buf[eq] == '=': + break + eq += 1 + if eq < ls + ll: + sv_g: t.CChar = buf[eq] + buf[eq] = '\0' + kh_g: t.CUInt64T = _SeenHash(buf + ls) + if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, buf + ls, kh_g) == 0: + _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, buf + ls, kh_g, 0, line_pos) + else: + buf[ls] = ';' + buf[eq] = sv_g + continue + # 类型 %name = type ...(完整定义 flags=1,opaque flags=0) + if ll > 0 and buf[ls] == '%': + eq: t.CSizeT = ls + 1 + while eq < ls + ll: + if buf[eq] == '=': + break + eq += 1 + if eq < ls + ll: + sv_t: t.CChar = buf[eq] + buf[eq] = '\0' + kh_t: t.CUInt64T = _SeenHash(buf + ls) + is_full: int = 0 + if eq + 8 <= ls + ll: + if buf[eq + 2] == 't' and buf[eq + 3] == 'y' and buf[eq + 4] == 'p' and buf[eq + 5] == 'e' and buf[eq + 6] == ' ': + if buf[eq + 7] == '{' or buf[eq + 7] == '<': + is_full = 1 + prev_t: int = _SeenPresent(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t) + if prev_t == 0: + _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, is_full, line_pos) + elif is_full == 1 and prev_t == 1: + # 先 opaque 后完整定义:注释旧行,表升级指向新完整定义行 + old_pos: t.CSizeT = _SeenGetPos(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t) + if old_pos >= base_pos and old_pos < base_pos + buf_len: + buf[old_pos - base_pos] = ';' + _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, 1, line_pos) + else: + buf[ls] = ';' + buf[eq] = sv_t + continue + # ============================================================ # StubMergerBuild - StubMerger 拆分(阶段 E):StubMergerBuild # @@ -43,6 +281,7 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, out_pos: t.CSizeT) -> t.CSizeT: """加载依赖 stub 并追加(跳过 header,去重)""" + global _seen_funcs_count, _seen_globals_count, _seen_types_count if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: return out_pos @@ -91,7 +330,7 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, dep_buf[ls] = ';' cfs_pos = ext_off2 + 30 - # 跳过 header,追加内容(去重 declare/global) + # 跳过 header,追加内容(去重 declare/global/type,已见符号表 O(1) 去重) dep_pos: t.CSizeT = 0 skipping: int = 1 while dep_pos < dep_br_ls: @@ -116,7 +355,12 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0: continue skipping = 0 - # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) + # 注册暂存(append 成功后才登记;reg_key 指向 scratch,insert 当轮完成) + reg_tbl: int = 0 + reg_key: str = None + reg_hash: t.CUInt64T = 0 + reg_flags: int = 0 + # 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句) if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0: at: t.CSizeT = line_start + 8 while at < line_start + line_len: @@ -132,24 +376,39 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, if lp < line_start + line_len: saved: t.CChar = dep_buf[lp + 1] dep_buf[lp + 1] = '\0' - if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at) != 0: + reg_hash = _SeenHash(dep_buf + at) + if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, dep_buf + at, reg_hash) != 0: dep_buf[lp + 1] = saved continue + string.strcpy(_seen_key_scratch, dep_buf + at) + reg_tbl = 1 + reg_key = _seen_key_scratch + reg_flags = 0 dep_buf[lp + 1] = saved - # 去重:external global + # 去重:external global(键 @name,截断于 '=' 前) if line_len > 0 and dep_buf[line_start] == '@': + # 字符串常量 @.str.{sha1}.{N}:依赖 stub 的 external 声明是死代码 + # (定义在模块自身 text.ll,合并 IR 中依赖方无需声明),跳过 + if line_len >= 5 and dep_buf[line_start + 1] == '.' and dep_buf[line_start + 2] == 's' \ + and dep_buf[line_start + 3] == 't' and dep_buf[line_start + 4] == 'r': + continue eq: t.CSizeT = line_start + 1 while eq < line_start + line_len: if dep_buf[eq] == '=': break eq += 1 if eq < line_start + line_len: - saved: t.CChar = dep_buf[eq + 1] - dep_buf[eq + 1] = '\0' - if string.strstr(out_buf, dep_buf + line_start) is not None: - dep_buf[eq + 1] = saved + saved: t.CChar = dep_buf[eq] + dep_buf[eq] = '\0' + reg_hash = _SeenHash(dep_buf + line_start) + if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + line_start, reg_hash) != 0: + dep_buf[eq] = saved continue - dep_buf[eq + 1] = saved + string.strcpy(_seen_key_scratch, dep_buf + line_start) + reg_tbl = 2 + reg_key = _seen_key_scratch + reg_flags = 0 + dep_buf[eq] = saved # 去重:type 定义(如 %"sha1.Point" = type {i32, i32}) # 区分完整定义(= type { ... } / = type < ... >)和 opaque 声明(= type opaque) # 当本地 stub 有 opaque 声明而依赖 stub 有完整定义时,注释掉 opaque 行, @@ -169,34 +428,48 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ': if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<': dep_is_full_def = 1 + saved_eq: t.CChar = dep_buf[eq2] + dep_buf[eq2] = '\0' + reg_hash = _SeenHash(dep_buf + line_start) if dep_is_full_def == 1: - # 依赖行是完整定义 - # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 - saved_eq: t.CChar = dep_buf[eq2] - dep_buf[eq2] = '\0' # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - commented: int = StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start) - # 2. 检查 out_buf 中是否已有完整定义(避免重复) - has_full: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + line_start) - dep_buf[eq2] = saved_eq - if has_full == 1: + _SeenCommentOut(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash, out_buf) + # 2. 检查是否已有完整定义(避免重复) + if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash) == 2: + dep_buf[eq2] = saved_eq dep_buf[eq2 + 1] = saved2 continue # 3. 追加完整定义 + string.strcpy(_seen_key_scratch, dep_buf + line_start) + reg_tbl = 3 + reg_key = _seen_key_scratch + reg_flags = 1 else: - # 依赖行是 opaque 声明,用原有子串匹配去重 - found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start) - if found_opaque is not None: + # opaque 声明:已存在同名(任意形态)则跳过 + if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash) != 0: + dep_buf[eq2] = saved_eq dep_buf[eq2 + 1] = saved2 continue + string.strcpy(_seen_key_scratch, dep_buf + line_start) + reg_tbl = 3 + reg_key = _seen_key_scratch + reg_flags = 0 + dep_buf[eq2] = saved_eq dep_buf[eq2 + 1] = saved2 - # 追加 + # 追加(append 成功后才登记已见表) if out_pos + line_len + 2 < out_size: + reg_pos: t.CSizeT = out_pos string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len) out_pos += line_len out_buf[out_pos] = '\n' out_pos += 1 out_buf[out_pos] = '\0' + if reg_tbl == 1: + _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, reg_key, reg_hash, reg_flags, reg_pos) + elif reg_tbl == 2: + _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, reg_key, reg_hash, reg_flags, reg_pos) + elif reg_tbl == 3: + _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, reg_key, reg_hash, reg_flags, reg_pos) return out_pos # ============================================================ # _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf @@ -208,6 +481,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, out_pos: t.CSizeT) -> t.CSizeT: """从依赖 text.ll 提取 declare(define 转 declare),追加到 out_buf""" + global _seen_funcs_count, _seen_globals_count, _seen_types_count if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: return out_pos @@ -285,13 +559,15 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, if lp_lt >= ls + ll: in_body = 1 continue - # 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句 + # 去重检查:用已见函数表(键 @"name"(,避免误匹配 call 语句) saved_lt: t.CChar = dep_buf[lp_lt + 1] dep_buf[lp_lt + 1] = '\0' - if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at_lt) != 0: + fh_lt: t.CUInt64T = _SeenHash(dep_buf + at_lt) + if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, dep_buf + at_lt, fh_lt) != 0: dep_buf[lp_lt + 1] = saved_lt in_body = 1 continue + string.strcpy(_seen_key_scratch, dep_buf + at_lt) dep_buf[lp_lt + 1] = saved_lt # 找行尾的 ' {' 并截断 @@ -337,6 +613,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, skipped_lt = 1 copy_len_lt: t.CSizeT = decl_end - c_start_lt if out_pos + 8 + copy_len_lt + 2 < out_size: + decl_pos_lt: t.CSizeT = out_pos string.strncpy(out_buf + out_pos, "declare ", 8) out_pos += 8 string.strncpy(out_buf + out_pos, dep_buf + c_start_lt, copy_len_lt) @@ -344,6 +621,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, out_buf[out_pos] = '\n' out_pos += 1 out_buf[out_pos] = '\0' + _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, _seen_key_scratch, fh_lt, 0, decl_pos_lt) in_body = 1 continue @@ -366,22 +644,27 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<': dep_is_full_td = 1 if dep_is_full_td == 1: - # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 + # 临时截断到 '=' 之前(不含 '='),键统一为 %name saved_eq_td: t.CChar = dep_buf[eq_td] dep_buf[eq_td] = '\0' + th_td: t.CUInt64T = _SeenHash(dep_buf + ls) # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls) - # 2. 检查 out_buf 中是否已有完整定义(避免重复) - has_full_td: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + ls) + _SeenCommentOut(_seen_types, _SEEN_TYPE_CAP, dep_buf + ls, th_td, out_buf) + # 2. 检查是否已有完整定义(避免重复) + if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + ls, th_td) == 2: + dep_buf[eq_td] = saved_eq_td + continue + string.strcpy(_seen_key_scratch, dep_buf + ls) dep_buf[eq_td] = saved_eq_td - if has_full_td == 0: - # 追加完整类型定义 - if out_pos + ll + 2 < out_size: - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' + # 追加完整类型定义 + if out_pos + ll + 2 < out_size: + td_pos: t.CSizeT = out_pos + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, _seen_key_scratch, th_td, 1, td_pos) continue # 提取全局变量定义(@... = ...) @@ -389,31 +672,38 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, # stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义 # 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义 if ll > 0 and dep_buf[ls] == '@': + # 字符串常量 @.str.{sha1}.{N}:依赖 text.ll 中的 internal 定义是死代码 + # (定义模块自身 .obj 已有,合并 IR 中依赖方无需重复),跳过 + if ll >= 5 and dep_buf[ls + 1] == '.' and dep_buf[ls + 2] == 's' and dep_buf[ls + 3] == 't' \ + and dep_buf[ls + 4] == 'r': + continue eq_gv: t.CSizeT = ls + 1 while eq_gv < ls + ll: if dep_buf[eq_gv] == '=': break eq_gv += 1 if eq_gv < ls + ll: - # 截断到 '=' 之前(不含 '='),得到全局变量名 @name + # 截断到 '=' 之前(不含 '='),键统一为 @name saved_gv: t.CChar = dep_buf[eq_gv] dep_buf[eq_gv] = '\0' + gh_gv: t.CUInt64T = _SeenHash(dep_buf + ls) # 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义) # 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份 # 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致 # ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。 # 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。 - if StubMergerIR._HasGlobalInBuf(out_buf, dep_buf + ls) == 0: - # 注释掉 out_buf 中的同名行(external 声明或旧定义) - StubMergerIR._CommentOutGlobalInBuf(out_buf, dep_buf + ls) + if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + ls, gh_gv) == 0: + # 追加完整定义(无同名行时注释旧行是 no-op) + string.strcpy(_seen_key_scratch, dep_buf + ls) dep_buf[eq_gv] = saved_gv - # 追加完整定义 if out_pos + ll + 2 < out_size: + gv_pos: t.CSizeT = out_pos string.strncpy(out_buf + out_pos, dep_buf + ls, ll) out_pos += ll out_buf[out_pos] = '\n' out_pos += 1 out_buf[out_pos] = '\0' + _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, _seen_key_scratch, gh_gv, 0, gv_pos) else: dep_buf[eq_gv] = saved_gv continue @@ -731,19 +1021,12 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6) is_app_src: int = 0 if app_mod_path is not None: - # 优先尝试 .vp,向后兼容 .py + # 仅识别 .vp 源文件(不再做 vp -> py 后缀识别回退) viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf) af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R) if not af.closed: is_app_src = 1 af.close() - else: - # .vp 不存在,回退 .py - viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf) - af = fileio.File(app_mod_path, fileio.MODE.R) - if not af.closed: - is_app_src = 1 - af.close() stdlib.free(app_mod_path) if is_app_src == 0: # fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充 @@ -778,6 +1061,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, def BuildCombinedIR(temp_dir: str, local_sha1: str, out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: """组合本地 stub + 依赖 stubs + 本地 text → out_buf(按需加载)""" + global _seen_funcs, _seen_funcs_count, _seen_globals, _seen_globals_count + global _seen_types, _seen_types_count, _seen_key_scratch if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0: return 0 @@ -785,6 +1070,34 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, out_buf[0] = '\0' out_pos: t.CSizeT = 0 + # 已见符号表重置(跨构建复用缓冲,仅清零计数 + 全槽位清零) + if _seen_funcs is None: + _seen_funcs = stdlib.malloc(_SEEN_FUNC_CAP * _SEEN_SLOT) + if _seen_funcs is None: + VLogger.error("已见函数表分配失败", "StubMerger") + return 0 + if _seen_globals is None: + _seen_globals = stdlib.malloc(_SEEN_GLOBAL_CAP * _SEEN_SLOT) + if _seen_globals is None: + VLogger.error("已见全局表分配失败", "StubMerger") + return 0 + if _seen_types is None: + _seen_types = stdlib.malloc(_SEEN_TYPE_CAP * _SEEN_SLOT) + if _seen_types is None: + VLogger.error("已见类型表分配失败", "StubMerger") + return 0 + if _seen_key_scratch is None: + _seen_key_scratch = stdlib.malloc(_SEEN_NAME_MAX + 1) + if _seen_key_scratch is None: + VLogger.error("已见表键暂存分配失败", "StubMerger") + return 0 + string.memset(_seen_funcs, 0, _SEEN_FUNC_CAP * _SEEN_SLOT) + string.memset(_seen_globals, 0, _SEEN_GLOBAL_CAP * _SEEN_SLOT) + string.memset(_seen_types, 0, _SEEN_TYPE_CAP * _SEEN_SLOT) + _seen_funcs_count = 0 + _seen_globals_count = 0 + _seen_types_count = 0 + # 1. 读取并追加本地 stub.ll(含 header) stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "stub.ll") if stub_path is None: @@ -830,8 +1143,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, stub_content[ext_off + 6] = ' ' stub_content[ext_off + 7] = ' ' fix_pos = ext_off + 8 - # 直接复制修复后的 stub 内容 + # 直接复制修复后的 stub 内容(复制前先登记其 declare/global/type, + # 保持已见表与 out_buf 一致,避免后续依赖重复追加 redefinition) if out_pos + slen + 2 < out_size: + _RegisterStubLines(stub_content, slen, out_pos) string.strcpy(out_buf + out_pos, stub_content) out_pos += slen if out_buf[out_pos - 1] != '\n': @@ -944,6 +1259,19 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, # 检查 dep_mod_name 是否是已导入包的子模块 # (如 ast.lexer 是 ast 的子模块) should_load = 1 + else: + # 基名匹配:store 键是全路径模块名 + # (Kernel.drivers.fs.fat32.fat32_types),而 deps.txt + # 记录 import 短名(fat32_types)→ 精确/子模块均不命中 + # → 依赖 stub/text 不加载 → 跨模块类完整定义缺失 + # (vpi 注入失败的 fat32_types.dirobj 只有 opaque)→ + # 值类型 alloca 报 llc "Cannot allocate unsized type" + # (VKernel example_fs L500 根因)。取最后一个 '.' 之后 + # 的基名与 deps 词边界匹配(多加载无害,去重表兜底)。 + last_dot_bm: str = string.strrchr(dep_mod_name, '.') + if last_dot_bm is not None and last_dot_bm[1] != '\0': + if GenApi.is_module_imported(deps_buf, last_dot_bm + 1) != 0: + should_load = 1 break if is_include == 0: # 非 includes stub(用户文件),总是加载 @@ -1057,7 +1385,12 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0: continue tx_skip = 0 - # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) + # 注册暂存(append 成功后才登记;tx_key 指向 scratch,insert 当轮完成) + tx_reg: int = 0 + tx_key: str = None + tx_hash: t.CUInt64T = 0 + tx_flags: int = 0 + # 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句) if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0: tx_at: t.CSizeT = tx_ls + 8 while tx_at < tx_ls + tx_ll: @@ -1073,11 +1406,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, if tx_lp < tx_ls + tx_ll: tx_sv: t.CChar = text_content[tx_lp + 1] text_content[tx_lp + 1] = '\0' - if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, text_content + tx_at) != 0: + tx_hash = _SeenHash(text_content + tx_at) + if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, text_content + tx_at, tx_hash) != 0: text_content[tx_lp + 1] = tx_sv continue + string.strcpy(_seen_key_scratch, text_content + tx_at) + tx_reg = 1 + tx_key = _seen_key_scratch + tx_flags = 0 text_content[tx_lp + 1] = tx_sv - # 去重:define(注释掉 out_buf 中已有的 declare,避免 declare/define 冲突) + # 去重:define(注释 out_buf 中已有 declare,避免 declare/define 冲突) if tx_ll >= 7 and string.strncmp(text_content + tx_ls, "define ", 7) == 0: tx_at_d: t.CSizeT = tx_ls + 7 while tx_at_d < tx_ls + tx_ll: @@ -1093,11 +1431,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, if tx_lp_d < tx_ls + tx_ll: tx_sv_d: t.CChar = text_content[tx_lp_d + 1] text_content[tx_lp_d + 1] = '\0' - StubMergerIR._CommentOutDeclareInBuf(out_buf, text_content + tx_at_d) + tx_hash = _SeenHash(text_content + tx_at_d) + _SeenCommentOut(_seen_funcs, _SEEN_FUNC_CAP, text_content + tx_at_d, tx_hash, out_buf) + string.strcpy(_seen_key_scratch, text_content + tx_at_d) + tx_reg = 1 + tx_key = _seen_key_scratch + tx_flags = 0 text_content[tx_lp_d + 1] = tx_sv_d # 处理:@global = ... # stub 中可能是 external 声明或完整定义,以 text 中的定义为准 - # 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的 + # 注释 out_buf 中同名全局行,然后追加 text 中的定义(键 @name,截断于 '=' 前) if tx_ll > 0 and text_content[tx_ls] == '@': tx_eq: t.CSizeT = tx_ls + 1 while tx_eq < tx_ls + tx_ll: @@ -1105,11 +1448,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, break tx_eq += 1 if tx_eq < tx_ls + tx_ll: - tx_sv2: t.CChar = text_content[tx_eq + 1] - text_content[tx_eq + 1] = '\0' - StubMergerIR._CommentOutGlobalInBuf(out_buf, text_content + tx_ls) - text_content[tx_eq + 1] = tx_sv2 - # 去重:%type = ... + tx_sv2: t.CChar = text_content[tx_eq] + text_content[tx_eq] = '\0' + tx_hash = _SeenHash(text_content + tx_ls) + _SeenCommentOut(_seen_globals, _SEEN_GLOBAL_CAP, text_content + tx_ls, tx_hash, out_buf) + string.strcpy(_seen_key_scratch, text_content + tx_ls) + tx_reg = 2 + tx_key = _seen_key_scratch + tx_flags = 0 + text_content[tx_eq] = tx_sv2 + # 去重:%type = ...(键 %name,截断于 '=' 前) if tx_ll > 0 and text_content[tx_ls] == '%': tx_eq2: t.CSizeT = tx_ls + 1 while tx_eq2 < tx_ls + tx_ll: @@ -1117,19 +1465,38 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, break tx_eq2 += 1 if tx_eq2 < tx_ls + tx_ll: - tx_sv3: t.CChar = text_content[tx_eq2 + 1] - text_content[tx_eq2 + 1] = '\0' - if string.strstr(out_buf, text_content + tx_ls) is not None: - text_content[tx_eq2 + 1] = tx_sv3 + tx_sv3: t.CChar = text_content[tx_eq2] + text_content[tx_eq2] = '\0' + tx_is_full: int = 0 + if tx_eq2 + 8 <= tx_ls + tx_ll: + if text_content[tx_eq2 + 2] == 't' and text_content[tx_eq2 + 3] == 'y' \ + and text_content[tx_eq2 + 4] == 'p' and text_content[tx_eq2 + 5] == 'e' \ + and text_content[tx_eq2 + 6] == ' ': + if text_content[tx_eq2 + 7] == '{' or text_content[tx_eq2 + 7] == '<': + tx_is_full = 1 + tx_hash = _SeenHash(text_content + tx_ls) + if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, text_content + tx_ls, tx_hash) != 0: + text_content[tx_eq2] = tx_sv3 continue - text_content[tx_eq2 + 1] = tx_sv3 - # 追加 + string.strcpy(_seen_key_scratch, text_content + tx_ls) + tx_reg = 3 + tx_key = _seen_key_scratch + tx_flags = tx_is_full + text_content[tx_eq2] = tx_sv3 + # 追加(append 成功后才登记已见表) if out_pos + tx_ll + 2 < out_size: + tx_reg_pos: t.CSizeT = out_pos string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll) out_pos += tx_ll out_buf[out_pos] = '\n' out_pos += 1 out_buf[out_pos] = '\0' + if tx_reg == 1: + _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, tx_key, tx_hash, tx_flags, tx_reg_pos) + elif tx_reg == 2: + _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, tx_key, tx_hash, tx_flags, tx_reg_pos) + elif tx_reg == 3: + _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, tx_key, tx_hash, tx_flags, tx_reg_pos) stdlib.free(text_content) if deps_buf is not None: diff --git a/App/lib/Stub/StubMerger/StubMergerIR.vp b/App/lib/Stub/StubMerger/StubMergerIR.vp index aa50b6f..c9deee1 100644 --- a/App/lib/Stub/StubMerger/StubMergerIR.vp +++ b/App/lib/Stub/StubMerger/StubMergerIR.vp @@ -19,19 +19,15 @@ import lib.Stub.StubGen.VpiReader as VpiReader import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config - import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild import lib.Stub.StubMerger as StubMerger + # ============================================================ # StubMergerIR - StubMerger 拆分(阶段 E):StubMergerIR -# -# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 -# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 -# StubMerger._xxx 访问(main.vp 先例)。 # ============================================================ # ============================================================ @@ -52,7 +48,7 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int: tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll") if tpath is None: return -1 - tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R) + tf_d: fileio.File = fileio.File(tpath, fileio.MODE.R) stdlib.free(tpath) if tf_d.closed: return -1 @@ -110,7 +106,7 @@ def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int: if sig_len == 0 or out_len == 0 or sig_len > out_len: return 0 pos: t.CSizeT = 0 - while pos + sig_len <= out_len: + for pos in range(0, out_len - sig_len + 1): if string.strncmp(out_buf + pos, func_sig, sig_len) == 0: ls: t.CSizeT = pos while ls > 0 and out_buf[ls - 1] != '\n': @@ -119,7 +115,6 @@ def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int: return 1 if string.strncmp(out_buf + ls, "define ", 7) == 0: return 1 - pos += 1 return 0 # ============================================================ # _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉 diff --git a/App/lib/Stub/StubMerger/StubMergerTopo.vp b/App/lib/Stub/StubMerger/StubMergerTopo.vp index 10b297f..de059b5 100644 --- a/App/lib/Stub/StubMerger/StubMergerTopo.vp +++ b/App/lib/Stub/StubMerger/StubMergerTopo.vp @@ -72,6 +72,12 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, if rel_idx is not None: stdlib.free(rel_idx) return 0 + # 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0) + zi: int = 0 + for zi in range(store_count + 1): + indegree[zi] = 0 + queue[zi] = 0 + rel_idx[zi] = 0 # 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为) rel_count: int = 0 i_t: int @@ -84,6 +90,11 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0: rel_idx[rel_count] = i_t rel_count += 1 + # 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行) + fb_rel: str = VLogger.fmt_buf() + if fb_rel is not None: + viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count) + VLogger.debug(fb_rel, "StubMergerTopo") # 计算入度:m 在可达集合内的直接依赖数 i_r: int for i_r in range(rel_count): @@ -110,7 +121,10 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf) stdlib.free(dname_buf) if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0: - indegree[si] = indegree[si] + 1 + # 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug + indg: int = indegree[si] + indg = indg + 1 + indegree[si] = indg # Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译 q_tail: int = 0 for i_r in range(rel_count): @@ -119,7 +133,11 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, queue[q_tail] = si2 q_tail += 1 out_count: int = 0 - for q_head in range(q_tail): + # Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队 + # (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次, + # 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项 + q_head: int = 0 + while q_head < q_tail: m: int = queue[q_head] sorted_idx[out_count] = m out_count += 1 @@ -148,10 +166,14 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1 if found_dep == 1: - indegree[si3] = indegree[si3] - 1 + # 中间 int 变量递减:绕过联合类型指针算术编译 bug + indg3: int = indegree[si3] + indg3 = indg3 - 1 + indegree[si3] = indg3 if indegree[si3] == 0: queue[q_tail] = si3 q_tail += 1 + q_head += 1 # 有环时剩余条目按 store 顺序补齐(避免漏编译) for i_r in range(rel_count): si4: int = rel_idx[i_r] diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index acf133e..d2c2daa 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -203,7 +203,7 @@ class AssignGenMixin: cl_cd: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node) if cl_cd is not None and cl_cd.args is not None: cl_args_cd: list[ast.AST | t.CPtr] | t.CPtr = cl_cd.args - if cl_args_cd.__len__() > 0: + if len(cl_args_cd) > 0: first_arg_cd: ast.AST | t.CPtr = cl_args_cd.get(0) if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant: val_node = first_arg_cd @@ -946,8 +946,7 @@ class AssignGenMixin: 支持两种书写形式: 1. 模块限定 t.CArray[...](Attribute value) - 2. 裸名 CArray[...](from t import * 后使用,如 Termination4K3 core.vp - 的 `__python_suffix__: CArray[str] = ["py", ...]`) + 2. 裸名 CArray[...](from t import * 后使用) # 存疑 """ if annot is None or annot.kind() != ast.ASTKind.Subscript: return 0 @@ -1076,7 +1075,7 @@ class AssignGenMixin: annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, annot, trans._imported_modules, trans._from_imports, trans) + pool, annot, trans._imported_modules, trans._from_imports, trans, 0) if resolved is None: # 尝试特化泛型类注解(如 list[str]) # resolve_annotation_type 返回 None 可能是因为泛型类未特化 @@ -1094,7 +1093,7 @@ class AssignGenMixin: trans, sub_nm.id, type_args_ps) if spec_name_ps is not None: resolved = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, annot, trans._imported_modules, trans._from_imports, trans) + pool, annot, trans._imported_modules, trans._from_imports, trans, 0) if resolved is not None: var_ty = resolved # 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。 @@ -1189,7 +1188,7 @@ class AssignGenMixin: annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, annot, trans._imported_modules, trans._from_imports, trans) + pool, annot, trans._imported_modules, trans._from_imports, trans, 0) if resolved is not None: var_ty = resolved else: @@ -1207,7 +1206,7 @@ class AssignGenMixin: # t.Callable 函数指针:注册返回类型签名(供函数指针间接调用精确返回类型) callable_ret: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenErr.try_extract_callable_ret( - pool, annot, trans._imported_modules, trans._from_imports, trans) + pool, annot, trans._imported_modules, trans._from_imports, trans, 0) if callable_ret is not None: self.Gen.CallGenLeafKit.register_func_ptr_sig(nm.id, callable_ret) diff --git a/App/lib/core/Gen/CallGenBase.vp b/App/lib/core/Gen/CallGenBase.vp index 694696a..0088120 100644 --- a/App/lib/core/Gen/CallGenBase.vp +++ b/App/lib/core/Gen/CallGenBase.vp @@ -330,17 +330,41 @@ class CallGenBase: def is_cexport_func(self, sha1: str, func_name: str) -> int: - """检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)""" + """检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源) + + 注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp + 先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc + 误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined + symbol: 1fdad1c76fb116bc.malloc)。 + """ if self.Gen is None or sha1 is None or func_name is None: return 0 - if self.Gen.CallGenBase.CexportSha1Arr is None: + if self.Gen.CallGenBase.CexportSha1Arr is not None: + for i in range(self.Gen.CallGenBase.CexportCount): + idx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0: + idx2: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0: + return 1 + return self._is_cexport_func_from_vpi(sha1, func_name) + + + def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int: + """vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头) + + 对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。 + """ + if sha1 is None or func_name is None: return 0 - for i in range(self.Gen.CallGenBase.CexportCount): - idx: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0: - idx2: t.CSizeT = t.CSizeT(i) * 64 - if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0: - return 1 + if self.Gen is None or self.Gen.CallGenBase.TempDir is None \ + or self.Gen.CallGenBase.TempDirLen <= 0: + return 0 + VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) + cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name) + if cexp_annot is None: + return 0 + if string.strstr(cexp_annot, "CExport") is not None: + return 1 return 0 diff --git a/App/lib/core/Gen/CallGenCBuiltin.vp b/App/lib/core/Gen/CallGenCBuiltin.vp index 83a6a6e..cf748d2 100644 --- a/App/lib/core/Gen/CallGenCBuiltin.vp +++ b/App/lib/core/Gen/CallGenCBuiltin.vp @@ -133,7 +133,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): and trans is not None and trans.CurAssignAnnot is not None: dref_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( trans.Pool, trans.CurAssignAnnot, - trans._imported_modules, trans._from_imports, trans) + trans._imported_modules, trans._from_imports, trans, 0) if dref_ty is not None: dref_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dref_ty) dref_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(dref_ty) @@ -161,12 +161,21 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): mod: llvmlite.LLVMModule | t.CPtr, cl: ast.Call | t.CPtr, trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: - """c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store)""" + """c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store) + 单参形式 c.DerefAs(ptr) → 读取 ptr 指向的值(等价 c.Deref(ptr))。 + """ if cl is None: return None cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args - if cargs is None or len(cargs) < 2: + if cargs is None or len(cargs) < 1: return None + # 单参形式:deref 读(paging.vp save_pt_range L263 + # saved = c.DerefAs(save_count)、restore_pt_range L277-278 + # c.DerefAs(entry_ptr) 根因——此前 len<2 直接 return None → + # 跌落跨模块函数调用生成 call @DerefAs → ld.lld undefined symbol: + # DerefAs。复用 translate_c_deref 的类型推断读取逻辑。 + if len(cargs) < 2: + return self.translate_c_deref(pool, builder, mod, cl, trans) ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( builder, pool, mod, cargs.get(0), None, 0, trans) @@ -344,7 +353,69 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): pass case _: pass + # nonlocal 变量:提升函数内从 env 读对象指针(load_nonlocal_var), + # 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱崩溃)。 + # 与普通局部 c.Addr(obj) 返回对象指针(load 一次)语义一致。 + if trans is not None: + if HT.is_nonlocal_name(trans, nm.id) != 0: + nl_addr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.load_nonlocal_var( + trans, nm.id) + if nl_addr is not None: + return nl_addr return_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) + if return_var is None and mod is not None: + # 模块全局变量:c.Addr(模块全局) → 返回全局变量指针。 + # 此前局部符号表查找失败即返回 None → 跌落跨模块函数调用路径 + # 生成 call @Addr(裸名,无 obj 定义)→ ld.lld undefined symbol: + # Addr(VKernel main.vp L87 c.Addr(ncpu_cr) 及 4d/8d/a1/c3/d3 + # 大量模块全局取地址根因)。必须经 make_global_ref 转成带引号的 + # Ptr(Ty) 引用——直接返回 GlobalVariable 节点时其 Ty 是 pointee、 + # Name 无 @ 引号,参与 ptrtoint 会渲染成 "ptrtoint void to i64" + # (llc: void type only allowed for function results)。 + gv_ref_nm: llvmlite.GlobalVariable | t.CPtr = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id) + if gv_ref_nm is not None and gv_ref_nm.Name is not None and gv_ref_nm.Ty is not None: + return_var = self.Gen.ExprGenAttr.make_global_ref(pool, gv_ref_nm.Name, gv_ref_nm.Ty) + # 模块函数地址:c.Addr(_on_int_complete) → 函数符号本身(@sha1.func)。 + # 此前 Name 分支只查局部/模块全局变量,函数取地址失败 return None + # → 跌落跨模块函数调用生成 call @Addr → ld.lld undefined symbol: + # Addr(uhci.vp L130、coroutine.vp L133 c.Addr(_coro_entry)、 + # sched.vp L209 c.Addr(_thread_entry) 根因)。 + if return_var is None and mod is not None: + f_addr_ref: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(mod, nm.id) + if f_addr_ref is not None: + f_addr_name: str = llvmlite.function_get_name(f_addr_ref) + if f_addr_name is not None: + p_head_ca: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(f_addr_ref) + p_cnt_ca: t.CSizeT = 0 + p_cur_ca: llvmlite.Param | t.CPtr = p_head_ca + while p_cur_ca is not None: + p_cnt_ca += 1 + p_cur_ca = llvmlite.param_get_next(p_cur_ca) + fn_ty_ca: llvmlite.LLVMType | t.CPtr = llvmlite.Func( + pool, f_addr_ref.RetTy, p_head_ca, p_cnt_ca) + # 函数引用名需按需加引号(数字开头/含 '.' 的 sha1 前缀名 + # 必须 @\"...\",否则 llc "expected 'to' after cast value", + # uhci.vp c.Addr(_on_int_complete) 根因)。 + fnlen_ca: t.CSizeT = string.strlen(f_addr_name) + needs_q_ca: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(f_addr_name) + faddr_buf_ca: str = pool.alloc(fnlen_ca + 4) + if faddr_buf_ca is not None and fn_ty_ca is not None: + if needs_q_ca != 0: + viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@\"%s\"", f_addr_name) + else: + viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@%s", f_addr_name) + return_var = llvmlite.SSAValue( + pool, llvmlite.Ptr(pool, fn_ty_ca), faddr_buf_ca) + # 闭包变量:c.Addr(闭包名) → 返回闭包对象指针(load 一次)。 + # 闭包 alloca 是 i8**(存 {fn_ptr, env_ptr} 结构地址);不 load 的话 + # 会把 &闭包指针变量(i8**)当对象地址传,回调侧按闭包结构读 + # fn_ptr 会读到指针变量的存储地址而非函数地址(与 Struct 对象 + # 变量 c.Addr(obj) load 一次的语义一致)。 + if return_var is not None and return_var.Ty is not None: + if self.Gen.CallGenLeafKit.is_closure_name(nm.id) != 0: + i8_ty_cb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_cb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_cb) + return llvmlite.build_load(builder, i8_ptr_ty_cb, return_var) if return_var is not None and return_var.Ty is not None: # 对象指针变量(如 lit_tree = zhuff.zhuff_tree(),alloca 是 # Ptr(Ptr(Struct)),存对象指针):c.Addr(对象变量) 应返回对象 @@ -377,52 +448,151 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): at_addr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg0) if at_addr is None or at_addr.attr is None or at_addr.value is None: return None - # 解析 self(Name)→ 查找 alloca - if at_addr.value.kind() != ast.ASTKind.Name: - return None - self_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value) - if self_nm.id is None: - return None - self_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var( - trans.SymTab, self_nm.id) - if self_alloca is None or self_alloca.Ty is None: - return None - # 从 self 类型提取结构体类型(Ptr(Struct) 或 Ptr(Ptr(Struct))) + # 跨模块函数地址:c.Addr(serial.puts) → 函数符号本身(@sha1.puts)。 + # 此前 Attribute 分支只支持 self/cls 字段 GEP;serial.puts 的 value 是 + # 已导入模块名(非局部变量)→ 下方 self_alloca 查找失败 return None → + # 回退 ExprGenAttr 当全局变量处理(创建 @sha1.puts external global i8* + # + load)→ 与 vpi 注入的 puts 函数 declare 同名冲突 → llc + # "redefinition of function '@a6890f1aeecb0aaf.puts'" + # (elf.vp L485 c.Addr(serial.puts) 根因)。 + if at_addr.value.kind() == ast.ASTKind.Name and trans is not None \ + and trans._imported_modules is not None: + mod_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value) + if mod_nm2.id is not None: + if self.Gen.ImportsGen.is_module_imported( + trans._imported_modules, mod_nm2.id) != 0: + f_ref2: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module( + mod, at_addr.attr) + if f_ref2 is not None: + fname2: str = llvmlite.function_get_name(f_ref2) + if fname2 is not None: + # 构造带 @ 名的 SSAValue(Ptr(FuncTy)):直接返回 + # Function 节点会被 IRBuilder 格式化成 RetTy(void) + # → "bitcast void to i8*"(elf.vp L2874 根因)。 + # Function 无 Ty 字段(成员仅 Next/Name/RetTy/ + # Params/Blocks/IsDeclared/IsVarArg/Attrs), + # 用 RetTy+Params 重建函数类型。Params 是 + # GSList(含 Head 字段),必须经 + # function_get_param_head 取链表头——直接 + # 把 Params 当 Param 遍历会读 GSList.Head/ + # Next 当 Name/Ty → 垃圾参数类型 + # "i32 (i1425633744, i8*)"(elf.vp L2874 根因)。 + p_head2: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(f_ref2) + p_cnt2: t.CSizeT = 0 + p_cur2: llvmlite.Param | t.CPtr = p_head2 + while p_cur2 is not None: + p_cnt2 += 1 + p_cur2 = llvmlite.param_get_next(p_cur2) + fn_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Func( + pool, f_ref2.RetTy, p_head2, p_cnt2) + # 函数引用名按需加引号(数字开头/含 '.' 的 + # sha1 前缀名必须 @\"...\")。 + fnlen2: t.CSizeT = string.strlen(fname2) + needs_q2: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(fname2) + faddr_buf2: str = pool.alloc(fnlen2 + 4) + if faddr_buf2 is not None and fn_ty2 is not None: + if needs_q2 != 0: + viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@\"%s\"", fname2) + else: + viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@%s", fname2) + return llvmlite.SSAValue( + pool, llvmlite.Ptr(pool, fn_ty2), faddr_buf2) + else: + # 模块限定全局:c.Addr(gdt.gp) → 全局符号本身(@gp)。 + # 跨模块全局(含 CExport 裸名 @gp)已注入为 external + # global。经 make_global_ref 转成带引号的 Ptr(Ty) 引用 + # (直接返回 GlobalVariable 节点会渲染 "ptrtoint void")。 + # 此前未处理 → return None → 跌落跨模块函数调用路径 + # 生成 call @Addr(裸名,无 obj 定义)→ ld.lld + # undefined symbol: Addr(apic.vp L193 c.Addr(gdt.gp) + # / L199 c.Addr(idt.idt) 根因)。 + g_ref2: llvmlite.GlobalVariable | t.CPtr = self.Gen.ExprGenAttr.find_global_in_module( + mod, at_addr.attr) + if g_ref2 is not None and g_ref2.Name is not None and g_ref2.Ty is not None: + return self.Gen.ExprGenAttr.make_global_ref(pool, g_ref2.Name, g_ref2.Ty) + # 解析 base(Name 变量 / Subscript 数组元素)→ 对象指针 + obj_ptr: llvmlite.Value | t.CPtr = None struct_ty: llvmlite.LLVMType | t.CPtr = None - self_is_ptr_ptr: int = 0 - match self_alloca.Ty: - case llvmlite.LLVMType.Ptr(inner1): - match inner1: - case llvmlite.LLVMType.Ptr(inner2): - struct_ty = inner2 - self_is_ptr_ptr = 1 - case _: - struct_ty = inner1 - case _: + if at_addr.value.kind() == ast.ASTKind.Subscript: + # c.Addr(s.threads[0].rsp):base 是数组元素(Subscript)→ 先取 + # 元素指针(GEP 数组),再 GEP 到字段。此前仅支持 Name base, + # Subscript 落到下方 return None → 跌落跨模块函数调用生成 + # call @Addr(sched.vp L90/L182/L188/L196、coroutine.vp + # L99/L106/L112/L120 根因)。 + sub_base: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(at_addr.value) + if sub_base.value is None or sub_base.slice is None: return None + bval: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, sub_base.value, None, 0, trans) + bidx: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, sub_base.slice, None, 0, trans) + if bval is None or bidx is None or bval.Ty is None: + return None + bpointee: llvmlite.LLVMType | t.CPtr = None + if self.Gen.ExprGenBase.is_ptr_type(bval.Ty) != 0: + bpointee = bval.Ty.Pointee + if bpointee is not None: + match bpointee: + case llvmlite.LLVMType.Array(bet, _): + obj_ptr = llvmlite.build_gep_array( + builder, bpointee, bet, bval, bidx) + struct_ty = bet + case _: + return None + else: + return None + elif at_addr.value.kind() == ast.ASTKind.Name: + self_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value) + if self_nm.id is None: + return None + self_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var( + trans.SymTab, self_nm.id) + if self_alloca is None or self_alloca.Ty is None: + return None + # 从 self 类型提取结构体类型(Ptr(Struct) 或 Ptr(Ptr(Struct))) + self_is_ptr_ptr: int = 0 + match self_alloca.Ty: + case llvmlite.LLVMType.Ptr(inner1): + match inner1: + case llvmlite.LLVMType.Ptr(inner2): + struct_ty = inner2 + self_is_ptr_ptr = 1 + case _: + struct_ty = inner1 + case _: + return None + # self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针 + obj_ptr = self_alloca + if self_is_ptr_ptr == 1: + self_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty) + obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca) + else: + return None + if obj_ptr is None or struct_ty is None: + return None # 查找字段 fe_addr: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( struct_ty, at_addr.attr) if fe_addr is None: return None - # self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针 - obj_ptr: llvmlite.Value | t.CPtr = self_alloca - if self_is_ptr_ptr == 1: - self_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty) - obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca) - if obj_ptr is None: - return None # GEP 到字段地址 return llvmlite.build_gep_struct( builder, struct_ty, fe_addr.Ty, obj_ptr, fe_addr.Index) - # Subscript 形式: c.Addr(arr[i]) → 数组元素地址(GEP 数组,不 load) - # 用于 self.root_directory = c.Addr(directories[directory_count]) 等。 - # 此前只支持 Name/Attribute,Subscript 落到函数末尾返回 None → - # 赋值右侧为 None(devfs.vp:159 根因)。 + # Subscript 形式: c.Addr(arr[i]) / c.Addr(a.b[i][j]) → 元素地址(GEP,不 load) + # 统一走 get_subscript_ptr(递归处理嵌套下标、数组/指针/str|t.CPtr 形态)。 + # 此前仅处理单层数组 GEP:嵌套下标(c.Addr(self.lines[line_idx][0]), + # vpui.vp L985)的 value 是内层 Subscript,translate_value 把内层加载成 + # 数组值([80 x i8])→ 非 Ptr → return None → 跌落跨模块函数调用生成 + # call @Addr(ld.lld undefined symbol: Addr 根因)。 if arg0.kind() == ast.ASTKind.Subscript: if trans is None: return None + elem_ptr_ca: llvmlite.Value | t.CPtr = self.Gen.ExprGenName.get_subscript_ptr( + builder, pool, mod, arg0, trans) + if elem_ptr_ca is not None: + return elem_ptr_ca + # 兜底:单层数组基址 GEP(get_subscript_ptr 未命中的形态) sub_addr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(arg0) if sub_addr is None or sub_addr.value is None or sub_addr.slice is None: return None @@ -434,7 +604,6 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): builder, pool, mod, sub_addr.slice, None, 0, trans) if idx_val is None: return None - # 数组基址 pointee 是 Array(elem_ty, N) → GEP 得元素指针 arr_pointee_ca: llvmlite.LLVMType | t.CPtr = None if self.Gen.ExprGenBase.is_ptr_type(arr_val.Ty) != 0: arr_pointee_ca = arr_val.Ty.Pointee @@ -444,7 +613,12 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): return llvmlite.build_gep_array( builder, arr_pointee_ca, arr_et_ca, arr_val, idx_val) case _: - pass + # 标量/指针元素(如字符串 a[4])→ 简单 GEP 元素地址。 + # 此前仅 Array 走 GEP,标量 pointee 落 return None → + # 跌落跨模块函数调用生成 call @Addr(main.vp L272 + # c.Addr(a[4]) 根因)。 + return llvmlite.build_gep( + builder, arr_pointee_ca, arr_val, idx_val) return None # Call 形式: c.Addr(expr) → 求值 expr 存临时槽(entry alloca),返回槽地址 @@ -462,8 +636,13 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): llvmlite.build_store(builder, call_val, tmp_slot) return tmp_slot - # 其他 arg0 形式(非 Name/Attribute/Subscript/Call):真实掉出函数末尾, - # 补显式 return None(fail-fast 不允许流程掉出末尾) + # 其他 arg0 形式(非 Name/Attribute/Subscript/Call):fail-fast。 + # c.Addr 是编译期指令,绝不跌落成运行时 call @Addr(ld.lld undefined + # symbol: Addr 根因——未识别形态 return None 会被 translate_call 当作 + # 普通函数调用处理)。 + self.Gen.TypeGenErr.fatal_error( + cl, f"c.Addr 不支持的操作数形式(kind={arg0.kind()})," + f"c.Addr 仅支持 变量/全局/函数/数组元素/字段", "TypeError") return None @@ -529,6 +708,78 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): dst[dlen + i] = '\0' + def _asm_mangle_global_symbols(self, pool: memhub.MemBuddy | t.CPtr, + asm_text: str, asm_size: t.CSizeT, + mod: llvmlite.LLVMModule | t.CPtr) -> str: + """asm 模板中裸符号引用模块全局的检查(fail-fast) + + 内联汇编 `lea rax, [rip + gdt]` 的 gdt 是汇编器符号,若对应模块全局 + 带 SHA1 混淆头(@"054f5e56cbefcaa4.gdt")则无法在 LLVM MC 中引用: + 数字开头的符号名会被解析成数字 → llc "Unexpected offset"(VKernel + gdt.vp L76/L93、sched.vp `[rip + _switch_old_rsp]` 根因)。因此引用 + 的全局必须标记 t.CExport 使用裸名(@gdt),此处按词边界匹配(前后 + 非标识符字符,避免误伤寄存器名/局部标签/指令助记符)命中即报错。 + """ + if asm_text is None or mod is None: + return asm_text + src_len: t.CSizeT = string.strlen(asm_text) + if src_len == 0: + return asm_text + out: str = pool.alloc(asm_size * 2) + if out is None: + return asm_text + out[0] = '\0' + out_pos: t.CSizeT = 0 + i: t.CSizeT = 0 + while i < src_len: + matched: int = 0 + cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead + while cur is not None: + gname: str = cur.Name + if gname is not None and string.strchr(gname, 46) is not None: + dot_g: str = string.strchr(gname, 46) + bare_g: str = dot_g + 1 + bare_len: t.CSizeT = string.strlen(bare_g) + if bare_len > 0 and i + bare_len <= src_len: + if string.strncmp(asm_text + i, bare_g, bare_len) == 0: + # 词边界:前后非标识符字符(字母/数字/_) + prev_ok: int = 1 + next_ok: int = 1 + if i > 0: + prev_ch: t.CChar = asm_text[i - 1] + if (prev_ch >= 'a' and prev_ch <= 'z') \ + or (prev_ch >= 'A' and prev_ch <= 'Z') \ + or (prev_ch >= '0' and prev_ch <= '9') \ + or prev_ch == '_': + prev_ok = 0 + if i + bare_len < src_len: + next_ch: t.CChar = asm_text[i + bare_len] + if (next_ch >= 'a' and next_ch <= 'z') \ + or (next_ch >= 'A' and next_ch <= 'Z') \ + or (next_ch >= '0' and next_ch <= '9') \ + or next_ch == '_': + next_ok = 0 + if prev_ok != 0 and next_ok != 0: + # fail-fast:asm 模板引用的模块全局带 SHA1 混淆头 + # 不可在 LLVM MC 内联汇编中使用(数字开头的符号名 + # 会被解析成数字而报 "Unexpected offset"),必须 + # 标记 t.CExport 使用裸名(如 gdt → @gdt)。 + self.Gen.TypeGenErr.fatal_error( + None, + f"asm 模板引用的全局符号 '{bare_g}' 带 SHA1 混淆头" + f"({gname}),内联汇编必须引用裸名符号,请将该" + f"全局定义标记为 t.CExport", "TypeError") + return asm_text + cur = cur.Next + if matched == 0: + if out_pos + 1 < asm_size * 2: + out[out_pos] = asm_text[i] + out_pos += 1 + i += 1 + out[out_pos] = '\0' + return out + + def _asm_escape_for_ir(self, pool: memhub.MemBuddy | t.CPtr, src: str) -> str: """转义汇编文本用于 LLVM IR 字符串字面量""" @@ -730,6 +981,38 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): return at.attr + def _asm_name_is_mangled(self, name: str) -> int: + """判断 LLVM 名称是否带 SHA1 混淆头(16 hex + '.'),返回 1=是 + + 兼容 make_global_ref 生成的 '@"sha1.name"' 引用名和 GlobalVariable + 裸名 "sha1.name"。'@name' 裸引用与局部 SSA(%N)都不算混淆。 + """ + if name is None: + return 0 + n: str = name + nlen: t.CSizeT = string.strlen(n) + # 去掉 '@"..."' 包装(引用名) + if nlen >= 3 and n[0] == '@' and n[1] == '"' and n[nlen - 1] == '"': + n = n + 2 + nlen -= 3 + elif nlen >= 1 and n[0] == '@': + n = n + 1 + nlen -= 1 + if nlen <= 17: + return 0 + for i in range(16, nlen): + if n[i] == '.': + ok: int = 1 + for j in range(16): + cc: t.CChar = n[i - 16 + j] + if not (('0' <= cc <= '9') or ('a' <= cc <= 'f') or ('A' <= cc <= 'F')): + ok = 0 + break + if ok != 0: + return 1 + return 0 + + def _asm_add_operand(self, operands: GenTypes.AsmOperand | t.CPtr, count: int, val: llvmlite.Value | t.CPtr, @@ -744,6 +1027,16 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): """ if count >= ASM_MAX_OPERANDS: return count + # fail-fast:AsmInp/AsmOut 操作数不允许带 SHA1 混淆头的符号 + # (模块级全局/函数)。LLVM MC 内联汇编无法解析数字开头的符号名, + # 且裸机链接要求裸名 —— 必须将对应定义标记为 t.CExport。 + if val is not None and val.Name is not None: + if self._asm_name_is_mangled(val.Name) != 0: + self.Gen.TypeGenErr.fatal_error( + None, + f"c.Asm 操作数(AsmInp/AsmOut)不允许带 SHA1 混淆头的符号 " + f"'{val.Name}',请将对应函数/全局定义标记为 t.CExport(裸名)", + "TypeError") if val is not None: for ei in range(count): eop: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, ei) @@ -751,6 +1044,16 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): return count op: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, count) op.Value = val + # 缺省约束:AsmInp(reg) 只传 1 参无 descr → 约束空 → llc + # "number of input constraints does not match number of parameters" + # (mm.vp L730 c.Asm(f"mov al, {AsmInp(reg)}\nout 0x70, al") 根因)。 + # 输入默认 "r"(通用寄存器)、输出默认 "=r"(寄存器输出), + # 与 _resolve_register 的 REG_ANY/OUTPUT_REG 一致。 + if constraint is None or string.strlen(constraint) == 0: + if is_output != 0: + constraint = "=r" + else: + constraint = "r" op.Constraint = constraint op.IsOutput = is_output return count + 1 @@ -845,13 +1148,60 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): cons_str = self._resolve_asm_descr(pool, fargs.get(1)) if is_out != 0: - # 输出:获取变量 alloca - if val_node is None or val_node.kind() != ast.ASTKind.Name: + # 输出:获取变量 alloca(Name 局部/全局 或 + # Attribute 字段地址) + if val_node is None: continue - nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node) - if nm.id is None: - continue - alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) + alloca: llvmlite.Value | t.CPtr = None + if val_node.kind() == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node) + if nm.id is None: + continue + alloca = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) + if alloca is None: + # 模块级全局变量(process.vp _saved_cr3 来自 + # isr.s .comm):lookup_var 只查局部 → 全局 + # 落空 → 操作数缺失 → asm 模板 "$0" 空 → + # "mov , cr3" llc unknown token in expression。 + # 回退查模块全局(精确+后缀),全局 Value + # (@name,类型 i64*)直接当 store 目标。 + alloca = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id) + elif val_node.kind() == ast.ASTKind.Attribute: + # AsmOut(p.pml4_root)(p 为 Process 指针): + # GEP 到字段地址(对齐 translate_c_addr Attribute + # 分支)。仅 Name 检查时 Attribute 被跳过 → + # 空操作数 "mov , cr3"(process.vp L127/L208 根因)。 + at_a: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(val_node) + if at_a.attr is not None and at_a.value is not None \ + and at_a.value.kind() == ast.ASTKind.Name: + base_nm_a: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_a.value) + if base_nm_a.id is not None: + base_alloca_a: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var( + trans.SymTab, base_nm_a.id) + if base_alloca_a is not None and base_alloca_a.Ty is not None: + struct_ty_a: llvmlite.LLVMType | t.CPtr = None + self_is_ptr_ptr_a: int = 0 + match base_alloca_a.Ty: + case llvmlite.LLVMType.Ptr(inner1): + match inner1: + case llvmlite.LLVMType.Ptr(inner2): + struct_ty_a = inner2 + self_is_ptr_ptr_a = 1 + case _: + struct_ty_a = inner1 + case _: + pass + if struct_ty_a is not None: + fe_a: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( + struct_ty_a, at_a.attr) + if fe_a is not None: + obj_ptr_a: llvmlite.Value | t.CPtr = base_alloca_a + if self_is_ptr_ptr_a == 1: + obj_ptr_a = llvmlite.build_load( + builder, llvmlite.Ptr(pool, struct_ty_a), base_alloca_a) + if obj_ptr_a is not None: + alloca = llvmlite.build_gep_struct( + builder, struct_ty_a, fe_a.Ty, obj_ptr_a, fe_a.Index) if alloca is None: continue operand_count = self._asm_add_operand( @@ -1192,8 +1542,12 @@ class CallGenCBuiltin(CallGenBase.CallGenBase): # ============================================================ # 7. 转义汇编文本 - # ============================================================ - escaped_asm: str = self._asm_escape_for_ir(pool, asm_text) + # 7.0 先做裸符号→混淆名替换(lea rax, [rip + gdt] → [rip + 054f5e56cbefcaa4.gdt]) + mangled_asm: str = self._asm_mangle_global_symbols( + pool, asm_text, ASM_TEXT_BUF_SIZE, mod) + if mangled_asm is None: + mangled_asm = asm_text + escaped_asm: str = self._asm_escape_for_ir(pool, mangled_asm) if escaped_asm is None: return None diff --git a/App/lib/core/Gen/CallGenCall.vp b/App/lib/core/Gen/CallGenCall.vp index dfd7cb9..edc1d6b 100644 --- a/App/lib/core/Gen/CallGenCall.vp +++ b/App/lib/core/Gen/CallGenCall.vp @@ -710,6 +710,12 @@ class CallGenCall(CallGenBase.CallGenBase): mc_entry = self.Gen.StructGen.find_struct_by_type(mc_struct_ty) if mc_entry is None: mc_entry = self.Gen.StructGen.find_struct(class_name) + # [MMDBG] 诊断:mc_entry 解析(用后移除) + if string.strcmp(method_name, "Sub") == 0: + if mc_entry is not None: + stdio.printf("[MMDBG] mc_entry.Name=%s\n", mc_entry.Name) + else: + stdio.printf("[MMDBG] mc_entry=None\n") # 构造方法查找名: ClassName.method_name lookup_name: str = pool.alloc(128) @@ -719,6 +725,12 @@ class CallGenCall(CallGenBase.CallGenBase): # 查找函数(支持 SHA1 后缀匹配) found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name) + # [MMDBG] 诊断:found_func 查找(用后移除) + if string.strcmp(method_name, "Sub") == 0: + if found_func is not None: + stdio.printf("[MMDBG] found_func=%s\n", llvmlite.function_get_name(found_func)) + else: + stdio.printf("[MMDBG] found_func=None lookup=%s\n", lookup_name) # 泛型模板类方法:特化函数名带类型参数({sha1}.list[int].get), # "ClassName.method" 短名匹配不到。特化由触发方(通常即调用方)模块完成, @@ -1577,9 +1589,20 @@ class CallGenCall(CallGenBase.CallGenBase): is_variadic_3441: int = self.Gen.CallGenVa._check_and_promote_variadic_call( pool, builder, mod, call_name, head, total_count, cargs, 1, trans) # try-except:方法含 raise(EH 输出参数)时追加输出实参 + 调用后检测 - return self.Gen.CallGenLeafKit._emit_eh_guarded_call( + # [MMDBG] 诊断:调用发射前状态(用后移除) + if string.strcmp(method_name, "Sub") == 0: + stdio.printf("[MMDBG] pre-call call_name=%s ret_ty_ptr=%llu head=%llu count=%d\n", + call_name, t.CUInt64T(call_ret_ty), t.CUInt64T(head), total_count) + res_final: llvmlite.Value | t.CPtr = self.Gen.CallGenLeafKit._emit_eh_guarded_call( builder, pool, mod, trans, found_func, call_name, head, total_count, call_ret_ty, is_variadic_3441) + # [MMDBG] 诊断:调用发射结果(用后移除) + if string.strcmp(method_name, "Sub") == 0: + if res_final is not None: + stdio.printf("[MMDBG] _emit_eh_guarded_call ok\n") + else: + stdio.printf("[MMDBG] _emit_eh_guarded_call None\n") + return res_final # ============================================================ # _translate_super_call — super().method(args) → 静态调用父类方法 # diff --git a/App/lib/core/Gen/CallGenCtor.vp b/App/lib/core/Gen/CallGenCtor.vp index c35262a..3792b67 100644 --- a/App/lib/core/Gen/CallGenCtor.vp +++ b/App/lib/core/Gen/CallGenCtor.vp @@ -715,7 +715,7 @@ class CallGenCtor(CallGenBase.CallGenBase): if kw is None or kw.arg is None or kw.value is None: continue fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field_by_class( - class_name, kw.arg, kw_sha1) + class_name, kw.arg, kw_sha1, kw, trans._from_imports) if fe is None: # 关键字字段不存在:fail-fast(AttributeError),绝不静默跳过 self.Gen.TypeGenErr.fatal_error( diff --git a/App/lib/core/Gen/CallGenDispatch.vp b/App/lib/core/Gen/CallGenDispatch.vp index 5fc5bfb..1848ad9 100644 --- a/App/lib/core/Gen/CallGenDispatch.vp +++ b/App/lib/core/Gen/CallGenDispatch.vp @@ -306,6 +306,81 @@ class CallGenDispatch(CallGenBase.CallGenBase): if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name: tc_mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tc_at.value) if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0: + # t.CType 特殊形式:t.CType(value, TypeName, t.CPtr) → + # 把 value 转为 TypeName|t.CPtr(TypeName* 指针)。TypeName + # 是类型名(Name/Attribute),不是值表达式——此前被当普通 + # 参数翻译 → NameError: 未定义名称 'memory_block' + # (VKernel mm.vp merge_blocks 根因)。CType 不是 map_t_type + # 的具名类型,须在此显式处理,否则跌落到跨模块 FATAL。 + if string.strcmp(func_name, "CType") == 0 and can >= 2: + cty_node: ast.AST | t.CPtr = cargs.get(1) + ctype_target: llvmlite.LLVMType | t.CPtr = None + if cty_node is not None: + cti_imp: str = None + cti_fi: str = None + if trans is not None: + cti_imp = trans._imported_modules + cti_fi = trans._from_imports + ctype_target = self.Gen.TypeGenResolve.resolve_annotation_type( + pool, cty_node, cti_imp, cti_fi, trans, 0) + if ctype_target is not None: + if can == 3: + cty_arg3: ast.AST | t.CPtr = cargs.get(2) + cty_is_cptr: int = 0 + if cty_arg3 is not None: + # 形式 1: t.CPtr(Attribute) + if cty_arg3.kind() == ast.ASTKind.Attribute: + cty_at3: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cty_arg3) + if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0: + if cty_at3.value is not None and cty_at3.value.kind() == ast.ASTKind.Name: + cty_nm3: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cty_at3.value) + if cty_nm3.id is not None and string.strcmp(cty_nm3.id, "t") == 0: + cty_is_cptr = 1 + # 形式 2: 裸名 CPtr + elif cty_arg3.kind() == ast.ASTKind.Name: + cty_bn3: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cty_arg3) + if cty_bn3.id is not None and string.strcmp(cty_bn3.id, "CPtr") == 0: + cty_is_cptr = 1 + if cty_is_cptr != 0: + # CVoid* → i8*(LLVM 中 void* 表示为 i8*) + match ctype_target: + case llvmlite.LLVMType.Void(): + ctype_target = llvmlite.Int8(pool) + case _: + pass + ctype_target = llvmlite.Ptr(pool, ctype_target) + cty_arg0: ast.AST | t.CPtr = cargs.get(0) + if cty_arg0 is not None: + cty_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, cty_arg0, funcs_ptr, func_count, trans) + if cty_val is not None: + return self.Gen.CallGenCtor._translate_t_type_cast( + pool, builder, cty_val, ctype_target, + func_name, cty_arg0, trans) + # t.CType 语法已确认:即使类型解析失败也不跌落到跨模块 FATAL + return None + # CConst const 标记:CConst(val, t.CPtr) → Ptr(i8)(const void*)。 + # CConst 不是 map_t_type 具名类型——t.CConst|t.CChar|t.CPtr 注解 = + # const char*(i8*),显式调用 t.CConst(addr, t.CPtr) 把整数地址转 + # const 指针。此前 map_t_type 失败 → 跌落跨模块函数调用 → 生成 + # call @CConst(裸名,无 obj 定义)→ ld.lld undefined symbol: + # CConst(elf.vp L650 t.CConst(t.CUInt64T(strtab_ptr)+sym.st_name, + # t.CPtr) 根因)。单参 CConst(x) 仅 const 限定,LLVM 类型不变, + # 直接返回原值。 + if string.strcmp(func_name, "CConst") == 0: + if can >= 2: + cconst_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + cconst_arg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + if cconst_arg is not None: + return self.Gen.CallGenCtor._translate_t_type_cast( + pool, builder, cconst_arg, cconst_ptr_ty, + func_name, cargs.get(0), trans) + elif can == 1: + return self.Gen.ExprGen.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + return None target_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.map_t_type(pool, func_name) if target_ty is not None: tc_effective_can: t.CSizeT = can @@ -374,7 +449,7 @@ class CallGenDispatch(CallGenBase.CallGenBase): mod_imp_td = trans._imported_modules mod_fi_td = trans._from_imports mod_td_ty = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, mod_td_ast, mod_imp_td, mod_fi_td, trans) + pool, mod_td_ast, mod_imp_td, mod_fi_td, trans, 0) if mod_td_ty is not None and can == 1: mt_arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) @@ -412,7 +487,7 @@ class CallGenDispatch(CallGenBase.CallGenBase): imp_gp = trans._imported_modules fi_gp = trans._from_imports bare_ty = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, gp_ast, imp_gp, fi_gp, trans) + pool, gp_ast, imp_gp, fi_gp, trans, 0) # 泛型类型参数双参数形式: T(val, t.CPtr) → Ptr(T) # (与 t.CInt(val, t.CPtr) → Ptr(i32) 对齐;vector.vp get() 的 # typed_ptr = T(offset, t.CPtr) 依赖此路径,否则双参数不进 @@ -944,9 +1019,72 @@ class CallGenDispatch(CallGenBase.CallGenBase): if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: method_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) if method_at is not None and method_at.value is not None: + # 链式方法调用: a.b().c() —— value 是内层 Call(如 gfx.vp 的 + # eye.Sub(target).Norm())。此前未处理 → 外层 .Norm() 的 value + # 非 Name 跳过方法分支 → 通用路径把 eye.Sub 当属性解析 → + # "属性写入失败: .Sub"。先翻译内层调用得接收者,再方法调用。 + if method_at.value.kind() == ast.ASTKind.Call and trans is not None: + recv_val_c: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, method_at.value, None, 0, trans) + if recv_val_c is not None and recv_val_c.Ty is not None: + recv_ty_c: llvmlite.LLVMType | t.CPtr = recv_val_c.Ty + if self.Gen.ExprGenBase.is_ptr_type(recv_ty_c) != 0: + recv_ty_c = recv_ty_c.Pointee + # 堆对象(@t.Object)双指针 → 再解一层取 struct + if self.Gen.ExprGenBase.is_ptr_type(recv_ty_c) != 0: + recv_ty_c = recv_ty_c.Pointee + recv_cls_c: str = self.Gen.StructGen.get_class_name_by_type( + pool, recv_ty_c) + if recv_cls_c is not None: + if self.Gen.CallGenProp._is_list_builtin_method( + recv_cls_c, method_at.attr) == 0: + obj_ptr_c: llvmlite.Value | t.CPtr = recv_val_c + if self.Gen.ExprGenBase.is_ptr_type(recv_val_c.Ty) != 0 \ + and recv_val_c.Ty.Pointee is not None \ + and self.Gen.ExprGenBase.is_ptr_type(recv_val_c.Ty.Pointee) != 0: + obj_ptr_c = llvmlite.build_load( + builder, recv_val_c.Ty, recv_val_c) + if obj_ptr_c is not None: + if hit_box is not None: + hit_box[0] = 1 + return self.Gen.CallGenCall._translate_method_call( + pool, builder, mod, recv_cls_c, method_at.attr, + obj_ptr_c, cl, trans) if method_at.value.kind() == ast.ASTKind.Name and trans is not None: method_obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(method_at.value) if method_obj_nm.id is not None: + # nonlocal 对象:提升函数内从 env 读对象指针(load_nonlocal_var), + # 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱 → + # llc "use of undefined value")。entry.get() 等方法调用的 + # 基座对象经此路径解析(closure 隐式捕获场景)。 + # 用 get_struct_type_from_value 递归解 Ptr,兼容 + # Ptr(Struct)(load_nonlocal_var 已 load)与 + # Ptr(Ptr(Struct))(联合简化未解引用)两种形态。 + if HT.is_nonlocal_name(trans, method_obj_nm.id) != 0: + nl_obj: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.load_nonlocal_var( + trans, method_obj_nm.id) + if nl_obj is not None and nl_obj.Ty is not None: + nl_deref: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase._deref_if_ptr_ptr( + builder, nl_obj) + if nl_deref is None: + nl_deref = nl_obj + nl_struct: llvmlite.LLVMType | t.CPtr = \ + self.Gen.StructGen.get_struct_type_from_value(nl_deref) + if nl_struct is not None: + nl_cls: str = self.Gen.StructGen.get_class_name_by_type( + pool, nl_struct) + if nl_cls is not None: + if self.Gen.CallGenProp._is_list_builtin_method( + nl_cls, func_name) == 0: + if hit_box is not None: + hit_box[0] = 1 + return self.Gen.CallGenCall._translate_method_call( + pool, builder, mod, nl_cls, func_name, + nl_deref, cl, trans) + # 非 local 对象但分派未成功:绝不回退 lookup_var + # (其 Alloca 是父函数存储,跨函数引用 → SSA 错乱), + # 返回 None 让上层 fast-fail 暴露类型解析问题。 + return None # 查找对象变量 obj_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var( trans.SymTab, method_obj_nm.id) diff --git a/App/lib/core/Gen/CallGenLeafKit.vp b/App/lib/core/Gen/CallGenLeafKit.vp index 5f4fb98..8511167 100644 --- a/App/lib/core/Gen/CallGenLeafKit.vp +++ b/App/lib/core/Gen/CallGenLeafKit.vp @@ -1183,6 +1183,13 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): entry.Defaults = defaults entry.DefaultCount = default_count entry.ParamCount = param_count + # 范围防御:default_count 异常(vpi tree 损坏时 len 垃圾大数)时放弃 + # 提取快照(调用方哨兵已拦截,此为第二道),避免 alloc(垃圾) + 数组 + # 越界写 perm_pool 覆盖当前模块 AST/表(SlotsBugTest 崩溃根因候选)。 + if default_count > 128: + entry.Defaults = None + entry.DefaultCount = 0 + return 0 # 提取常量快照到 stdlib if default_count > 0: kinds: t.CInt | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 4) diff --git a/App/lib/core/Gen/CallGenVa.vp b/App/lib/core/Gen/CallGenVa.vp index 7ac1dad..85864b5 100644 --- a/App/lib/core/Gen/CallGenVa.vp +++ b/App/lib/core/Gen/CallGenVa.vp @@ -79,7 +79,7 @@ class CallGenVa(CallGenBase.CallGenBase): return llvmlite.Int32(pool) # 使用 HandlesType.resolve_annotation_type 解析类型表达式 resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, type_node, trans._imported_modules, trans._from_imports, trans) + pool, type_node, trans._imported_modules, trans._from_imports, trans, 0) if resolved is not None: return resolved # 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位) @@ -230,6 +230,50 @@ class CallGenVa(CallGenBase.CallGenBase): count += 1 param = llvmlite.param_get_next(param) return count + def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int: + """已知变参 C 库函数固定参数个数(-1=未知) + + 跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin + 注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None), + 无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数 + (viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。 + """ + if name is None: + return -1 + if string.strcmp(name, "snprintf") == 0: + return 3 + if string.strcmp(name, "sprintf") == 0: + return 2 + if string.strcmp(name, "vsprintf") == 0: + return 2 + return -1 + + def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy | t.CPtr, + name: str, idx: int) -> llvmlite.LLVMType | t.CPtr: + """已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None) + + 固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入 + 时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。 + """ + if pool is None or name is None or idx < 0: + return None + i8_ptr_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + i64_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + if string.strcmp(name, "snprintf") == 0: + if idx == 0: + return i8_ptr_kv + if idx == 1: + return i64_kv + if idx == 2: + return i8_ptr_kv + return None + if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0: + if idx == 0: + return i8_ptr_kv + if idx == 1: + return i8_ptr_kv + return None + return None def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -252,13 +296,20 @@ class CallGenVa(CallGenBase.CallGenBase): return 0 # 查找函数对象 func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name) - if func is None: - return 0 - if func.IsVarArg != 1: - return 0 - - # 获取固定参数数量 - fixed_count: int = self._get_func_fixed_param_count(func) + fixed_count: int = -1 + if func is not None and func.IsVarArg == 1: + fixed_count = self._get_func_fixed_param_count(func) + else: + # 两种情况回退已知变参 C 库函数签名表(snprintf 等): + # 1. func 为 None——跨模块 stub 未注入(CallGenMixin 注释:stubs + # 在翻译后才注入,翻译时跨模块函数 found_func 恒 None); + # 2. func 存在但 IsVarArg=0——该 declare 由 _ensure_method_declare + # 按实参类型临时创建(无 ... 标记),不能代表真实签名。 + # 否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → + # va_arg(i64) 高 4 字节读残留栈垃圾(clean 日志 0x7ff700000052 根因)。 + fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name) + if fixed_count < 0: + return 0 # 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1 if can <= fixed_count: @@ -319,6 +370,32 @@ class CallGenVa(CallGenBase.CallGenBase): promoted.Next = next_node if prev is not None: prev.Next = promoted + # Win64 变参槽位 8 字节:i8/i16/i32 → i64 + # TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar), + # 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到 + # 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin + # L798-811 的 found_func.IsVarArg 提升)。 + i64_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty) + if pb_va != 0 and pb_va < 64: + promoted_va2: llvmlite.Value | t.CPtr = None + is_u_va2: int = 0 + if arg_nodes is not None and trans is not None: + ast_idx_va2: int = v_idx - self_offset + if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes): + an_va2: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va2) + if an_va2 is not None: + if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0: + is_u_va2 = 1 + if is_u_va2 != 0: + promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote) + else: + promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote) + if promoted_va2 is not None: + promoted_va2.Next = next_node + if prev is not None: + prev.Next = promoted_va2 + promoted = promoted_va2 prev = promoted cur = next_node v_idx += 1 diff --git a/App/lib/core/Gen/CallGenVt.vp b/App/lib/core/Gen/CallGenVt.vp index 134d203..761bab4 100644 --- a/App/lib/core/Gen/CallGenVt.vp +++ b/App/lib/core/Gen/CallGenVt.vp @@ -137,17 +137,32 @@ class CallGenVt(CallGenBase.CallGenBase): llvmlite.module_add_global(mod, vcopy_gv) vcopy_gv.Linkage = None vcopy_gv.Initializer = "zeroinitializer" + # 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名 + # devfs_file.vtable.copy.71 无 @ → llc "expected value token" + # (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable + # 输出时加的,引用侧不自动加) + vcopy_ref_nm: str = pool.alloc(272) + if vcopy_ref_nm is None: + vcopy_ref_nm = vcopy_buf + else: + viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf) vcopy_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( - pool, llvmlite.Ptr(pool, vtable_ty), vcopy_buf) + pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm) if vcopy_ref is not None: vcopy_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( builder, vcopy_ref, i8_ptr_ty) if vcopy_i8 is not None: - vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt( - pool, llvmlite.Int64(pool), method_count * 8, "vtsz") - if vt_size is not None: - self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic( - pool, builder, mod, vcopy_i8, class_vt, vt_size) + # ConstInt 的 name 必须是数字文本(如 "56"),不能是 + # 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 % + # → llc "expected value token"(devfs memcpy 根因) + vtsz_buf: str = pool.alloc(32) + if vtsz_buf is not None: + viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8) + vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf) + if vt_size is not None: + self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic( + pool, builder, mod, vcopy_i8, class_vt, vt_size) llvmlite.build_store(builder, vcopy_i8, vtable_slot) llvmlite.build_br(builder, vt_skip_bb) # skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*) diff --git a/App/lib/core/Gen/ClassGenDef.vp b/App/lib/core/Gen/ClassGenDef.vp index 1b472a2..23cf2ed 100644 --- a/App/lib/core/Gen/ClassGenDef.vp +++ b/App/lib/core/Gen/ClassGenDef.vp @@ -328,7 +328,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase): # 解析字段类型 field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) + pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0) # 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem] # (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义): # 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**), diff --git a/App/lib/core/Gen/ClassGenEnum.vp b/App/lib/core/Gen/ClassGenEnum.vp index 631a840..721293c 100644 --- a/App/lib/core/Gen/ClassGenEnum.vp +++ b/App/lib/core/Gen/ClassGenEnum.vp @@ -449,7 +449,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): # 解析字段类型 field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) + pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0) if field_ty is None: # Phase A-pre(declare_only=1):依赖的类可能尚未注册, # 静默 return 1 让多遍收敛机制生效(renum 返回 1=跳过该类) @@ -707,7 +707,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): mem_ty_l = self.Gen.TypeGenResolve.resolve_annotation_type( pool, vaa.annotation, trans._imported_modules, - trans._from_imports, trans) + trans._from_imports, trans, 0) self._register_renum_field_alias( pool, class_name, vnm.id, field_idx + 1, mem_ty_l, variant_cd.name) diff --git a/App/lib/core/Gen/ExprGenBase.vp b/App/lib/core/Gen/ExprGenBase.vp index 5846b3f..5572ae1 100644 --- a/App/lib/core/Gen/ExprGenBase.vp +++ b/App/lib/core/Gen/ExprGenBase.vp @@ -167,12 +167,18 @@ class ExprGenBase: level: int = 0 cur: llvmlite.LLVMType | t.CPtr = ty while cur is not None: + # 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接 + # 更新 while 条件变量(cur = pointee)时自举编译出的 match 把赋值 + # 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case + # 绑定到局部变量,match 外更新循环变量。 + nxt_cur: llvmlite.LLVMType | t.CPtr = None match cur: case llvmlite.LLVMType.Ptr(pointee): level += 1 - cur = pointee + nxt_cur = pointee case _: break + cur = nxt_cur return level # ============================================================ # _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0 diff --git a/App/lib/core/Gen/ExprGenConst.vp b/App/lib/core/Gen/ExprGenConst.vp index 613ee5d..42fcdf7 100644 --- a/App/lib/core/Gen/ExprGenConst.vp +++ b/App/lib/core/Gen/ExprGenConst.vp @@ -95,7 +95,8 @@ class ExprGenConst(ExprGenBase.ExprGenBase): pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, node: ast.AST | t.CPtr, - trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + trans: HT.Translator | t.CPtr = None, + target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr: """翻译常量(int/str/bool)""" cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) if cn is None: @@ -112,7 +113,27 @@ class ExprGenConst(ExprGenBase.ExprGenBase): double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool) return llvmlite.ConstFloat(pool, double_ty, cn.float_val) elif cn.const_kind == ast.CONST_CHAR: - # 单引号单字符 → i32 值(字符 ASCII 码),coerce_to_type 会 trunc 为 i8 + # 单引号单字符字面量:默认按字符值(i32,coerce_to_type 截断为 i8)。 + # 但当目标类型是指针(str/bytes/t.CPtr 等)时,对齐 TPC 引号推断 + # (HandlesExpr:"目标是 ptr 但值是 char → 走字符串路径"):物化为 + # 1 字符字符串。否则 coerce inttoptr 把字符值当指针(如 '"'=0x22), + # 运行期 strlen 访问低地址 → ASan 崩溃(二代编 StringTest PyI 生成根因)。 + # 1a 阶段(declare_only=1)不 LoadDepVpi(vpi 未预生成)→ 依赖函数 + # found_func=None → translate_call 缺省参数补全不生效 → translate_value + # 调用参数数 < define(如 target_ty 缺省)→ x64 ABI 被调方读第 9 参 + # 栈槽残留垃圾(0x4 等)。declare_only 不产出 IR,target_ty 无用, + # 未对齐(8 对齐失败)即视为垃圾跳过,避免 is_ptr_type 解引用崩溃 + # (SlotsBugTest 1a string.vp 崩溃根因;1b found_func 非 None 正常填充)。 + if target_ty is not None and (t.CUInt64T(target_ty) & 7) == 0 \ + and self.is_ptr_type(target_ty) != 0: + cbuf: bytes = stdlib.malloc(2) + if cbuf is None: + return llvmlite.const_int32(pool, 0) + cbuf[0] = t.CChar(cn.int_val) + cbuf[1] = '\0' + r_cs: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, cbuf, trans) + stdlib.free(cbuf) + return r_cs cv: t.CInt64T = cn.int_val return llvmlite.const_int32(pool, cv) elif cn.const_kind == ast.CONST_STR: diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 41a5f60..5b89a50 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -79,13 +79,38 @@ class ExprGenMixin(ExprGenBase.ExprGenBase): node: ast.AST | t.CPtr, funcs_ptr: t.CPtr = None, func_count: int = 0, - trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + trans: HT.Translator | t.CPtr = None, + target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl) + + target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号 + 单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。 + """ + if trans is not None: + if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: + self.Gen.TypeGenErr.fatal_error( + node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError") + trans.TranslateDepth += 1 + rv: llvmlite.Value | t.CPtr = self._translate_value_impl( + builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty) + if trans is not None: + trans.TranslateDepth -= 1 + return rv + + def _translate_value_impl(self, builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + funcs_ptr: t.CPtr = None, + func_count: int = 0, + trans: HT.Translator | t.CPtr = None, + target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr: """翻译值表达式为 LLVM Value""" if node is None: return None k: int = node.kind() if k == ast.ASTKind.Constant: - rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans) + rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty) return rv elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans) diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 60a316a..67f4bd7 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -1,1173 +1,1564 @@ -import lib.core.Gen.GenTypes as GenTypes -import t, c -from stdint import * -import ast -import llvmlite -import memhub -import string -import viperlib -import stdio -import stdlib -import lib.core.VLogger as VLogger -import lib.core.Handles.HandlesTranslator as HT -import lib.core.Handles.HandlesExpr as HandlesExpr -import lib.core.Handles.HandlesExprCall as HandlesExprCall -import lib.core.Gen.CallGenBase as CallGenBase - - -# ============================================================ -# FuncGenMixin - 函数/方法声明与翻译 Mixin(对象化 P5-1,wiki/14-oo-refactor.md) -# -# 从 HandlesFunctions.vp 迁移:模块级纯函数(trans 首参,无全局状态) -# → 类方法(self 替代,trans 参数保留)。 -# -# 迁移原则(与 P2/P3/P4 一致,行为零变化): -# 1. 方法保留原函数签名(含 trans 首参),仅函数体内的 -# 本模块函数调用改为 self.xxx(...) -# 2. 外部模块调用(HandlesExprCall.xxx/HandlesType.xxx 等)保留模块前缀 -# 3. HandlesFunctions.vp 原函数保留为"委托":本地模块级单例 _g_gen -# (BuildPipeline/main 注入)cast 成 Generator 类型 -# -# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。 -# ============================================================ - - -# ============================================================ -# HandlesFunctions - 函数定义处理(trans 单参模式) -# -# 参考 Python 版 TransPyC FunctionHandle -# -# 函数有自己的局部作用域,通过 HandlesVar.enter_scope/exit_scope -# 管理嵌套作用域链,翻译函数体前进入函数作用域,翻译完退出。 -# ============================================================ - - -# 作用域类型常量(本地副本,与 HandlesFunctions.vp 保持一致) -SCOPE_FUNCTION: t.CDefine = 1 - - -# ============================================================ -# FuncGenMixin - 函数属性/命名/声明/翻译 -# ============================================================ -@t.NoVTable -class FuncGenMixin(CallGenBase.CallGenBase): - """函数/方法翻译 Mixin(纯方法化迁移,无状态字段)""" - - - - - - - - - - - - - - # ============================================================ - # translate_nested_function_def - 嵌套函数提升 + 闭包创建 - # - # 将嵌套函数提升为顶层函数 @__closure_{name}(i8* %env) -> i32, - # 在父函数中创建闭包对象 {i8* fn_ptr, i8* env_ptr} 并存储到 - # 以函数名命名的局部变量中。 - # - # 闭包结构: {i8* fn_ptr, i8* env_ptr} (16 字节, malloc 分配) - # Env 结构: {i8* ptr0, i8* ptr1, ...} (每个 nonlocal 变量 8 字节,指向父函数 alloca) - # ============================================================ - def translate_nested_function_def(self, trans: HT.Translator | t.CPtr, - node: ast.AST | t.CPtr) -> int: - """翻译嵌套函数定义:提升为顶层函数 + 创建闭包""" - fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node) - if fd is None or fd.name is None: - return 0 - - pool: memhub.MemBuddy | t.CPtr = trans.Pool - mod: llvmlite.LLVMModule | t.CPtr = trans.Module - func_name: str = fd.name - - i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) - i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - - # 1. 构建提升后的函数名: __closure_{name}(加 SHA1 前缀避免跨模块冲突) - promoted_name: str = pool.alloc(64) - if promoted_name is not None: - viperlib.snprintf(promoted_name, 64, "__closure_%s", func_name) - mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, promoted_name) - - # 2. 创建提升后的函数: define i32 @__closure_{name}(i8* %env, args...) - func: llvmlite.Function | t.CPtr = llvmlite.create_function( - pool, mod, mangled_promoted, i32_ty) - if func is None: - return 0 - llvmlite.add_param(pool, func, i8_ptr_ty, "%env") - - # 2a. 嵌套函数参数:加入提升函数签名(i8* %env, 参数...), - # 类型存入 param_tys 缓冲(CVoid 跳过,与 alloca 创建索引对齐) - imported_modules: str = trans._imported_modules - from_imports: str = trans._from_imports - param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) - if param_tys is not None: - string.memset(param_tys, 0, 8 * 64) - param_count: int = 0 - if fd.args is not None: - ags_n: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) - if ags_n.args is not None: - alist_n: list[ast.AST | t.CPtr] | t.CPtr = ags_n.args - an_n: t.CSizeT = len(alist_n) - for ai_n in range(an_n): - arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_n.get(ai_n)) - if arg_n is None or arg_n.arg is None: - continue - if arg_n.annotation is not None: - if self.Gen.TypeGenResolve._is_t_attr(arg_n.annotation, "CVoid", imported_modules) != 0: - continue - pty_n: llvmlite.LLVMType | t.CPtr = i32_ty - if arg_n.annotation is not None: - resolved_n: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, arg_n.annotation, imported_modules, from_imports, trans) - if resolved_n is not None: - match resolved_n: - case llvmlite.LLVMType.Struct(_, _, _): - pty_n = llvmlite.Ptr(pool, resolved_n) - case llvmlite.LLVMType.Array(aelem_ty_n, _): - pty_n = llvmlite.Ptr(pool, aelem_ty_n) - case _: - pty_n = resolved_n - if param_count < 64 and param_tys is not None: - pty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(param_tys) + param_count * 8) - pty_slot_n[0] = pty_n - pname_n: str = pool.alloc(32) - if pname_n is not None: - viperlib.snprintf(pname_n, 32, "%%%s", arg_n.arg) - llvmlite.add_param(pool, func, pty_n, pname_n) - param_count += 1 - - # 3. 创建 entry 块 + builder - entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry") - if entry_blk is None: - return 0 - func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func) - if func_builder is None: - return 0 - llvmlite.position_at_end(func_builder, entry_blk) - - # 4. 进入嵌套函数作用域(嵌套符号表) - self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION) - - # 5. 创建 _env_ptr alloca 并存储 %env 参数 - env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty) - if env_alloca is not None: - self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca) - env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env") - llvmlite.build_store(func_builder, env_param_val, env_alloca) - - # 5a. 创建嵌套函数参数 alloca(函数体内参数名可引用,如 - # accumulate(n) 内 for i in range(n)、set_result(val) 内 result = val) - if fd.args is not None: - ags_p: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) - if ags_p.args is not None: - alist_p: list[ast.AST | t.CPtr] | t.CPtr = ags_p.args - idx_p: int = 0 - apn: t.CSizeT = len(alist_p) - for api in range(apn): - arg_p: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_p.get(api)) - if arg_p is None or arg_p.arg is None: - continue - if arg_p.annotation is not None: - if self.Gen.TypeGenResolve._is_t_attr(arg_p.annotation, "CVoid", imported_modules) != 0: - continue - if idx_p >= param_count: - break - pty2_n: llvmlite.LLVMType | t.CPtr = i32_ty - if param_tys is not None: - ty_addr_n: t.CUInt64T = t.CUInt64T(param_tys) + idx_p * 8 - ty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr_n) - pty2_n = (llvmlite.LLVMType | t.CPtr)(ty_slot_n[0]) - if pty2_n is None: - pty2_n = i32_ty - p_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, pty2_n) - if p_alloca is not None: - self.Gen.VarGen.define_var(trans.SymTab, arg_p.arg, p_alloca) - if arg_p.annotation is not None: - cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation( - arg_p.annotation, imported_modules, pool, trans._from_imports) - if cls_nm_p is not None: - self.Gen.VarGen.set_var_annot_class_name( - trans.SymTab, arg_p.arg, cls_nm_p) - pname2_n: str = pool.alloc(32) - if pname2_n is not None: - viperlib.snprintf(pname2_n, 32, "%%%s", arg_p.arg) - p_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, pty2_n, pname2_n) - llvmlite.build_store(func_builder, p_val, p_alloca) - idx_p += 1 - - # 6. 保存父函数作用域状态(仅非变量表相关) - old_func: llvmlite.Function | t.CPtr = trans._cur_func - old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder - old_func_name: str = trans._cur_func_name - old_global_count: int = trans._global_name_count - old_nonlocal_count: int = trans._nonlocal_name_count - old_env_count: int = trans._closure_env_count - # nonlocal 集合独立缓冲:嵌套函数注册 nonlocal 写入共享缓冲 [0] 会 - # 覆盖父函数集合(ClosureTest2 Test 7 不同变量场景:level2 的 b 覆盖 - # level1 的 a → level1 env[0] 存错)。嵌套函数用独立缓冲,翻译完恢复。 - saved_nl_names: t.CVoid | t.CPtr = trans._nonlocal_names - saved_nl_types: t.CVoid | t.CPtr = trans._nonlocal_types - - # 7. 切换到嵌套函数作用域 + 清空 scope names - trans._cur_func = func - trans._cur_builder = func_builder - trans._cur_func_name = func_name - HT.clear_scope_names(trans) - # 嵌套函数独立 nonlocal 缓冲(避免覆盖父函数集合) - trans._nonlocal_names = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8) - trans._nonlocal_types = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8) - if trans._nonlocal_names is not None: - string.memset(trans._nonlocal_names, 0, HT.MAX_GLOBAL_NAMES * 8) - if trans._nonlocal_types is not None: - string.memset(trans._nonlocal_types, 0, HT.MAX_GLOBAL_NAMES * 8) - - # 8. 预扫描函数体: - # a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效) - # b. 再预扫描 AnnAssign 为局部变量提前创建 alloca - body: list[ast.AST | t.CPtr] | t.CPtr = fd.children - if body is not None: - bn: t.CSizeT = len(body) - for bi in range(bn): - stmt: ast.AST | t.CPtr = body.get(bi) - if stmt is not None: - self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt) - for bi in range(bn): - stmt: ast.AST | t.CPtr = body.get(bi) - if stmt is not None: - self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt) - - # 9. 翻译函数体(Nonlocal 语句会填充 _nonlocal_names) - if body is not None: - bn2: t.CSizeT = len(body) - for bi2 in range(bn2): - stmt2: ast.AST | t.CPtr = body.get(bi2) - if stmt2 is not None: - self.Gen.StmtGen.translate_stmt(trans, stmt2) - - # 10. 隐式 ret - last_is_return: int = 0 - if body is not None: - bn3: t.CSizeT = len(body) - if bn3 > 0: - last_stmt: ast.AST | t.CPtr = body.get(bn3 - 1) - if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return: - last_is_return = 1 - if last_is_return == 0: - zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) - llvmlite.build_ret(func_builder, zero_val) - - # 11. 保存嵌套函数的 nonlocal 信息 - nested_nonlocal_count: int = trans._nonlocal_name_count - - # 12. 退出嵌套函数作用域,回到父函数作用域(保留 _nonlocal_names 用于 env 创建) - self.Gen.VarGen.exit_scope(trans.SymTab) - trans._cur_func = old_func - trans._cur_builder = old_builder - # NOTE: _nonlocal_names 和 _nonlocal_name_count 仍为嵌套函数的值 - - # 13. 在父函数中创建闭包对象 - parent_builder: llvmlite.IRBuilder | t.CPtr = old_builder - if parent_builder is None: - # 无 builder(模块级嵌套函数?)→ 无法创建闭包,仅恢复状态 - trans._cur_func_name = old_func_name - trans._global_name_count = old_global_count - trans._nonlocal_name_count = old_nonlocal_count - trans._closure_env_count = old_env_count - trans._nonlocal_names = saved_nl_names - trans._nonlocal_types = saved_nl_types - return 0 - - # 13a. 创建 env: malloc(8 * nested_nonlocal_count) — env 存储指针数组 - # 每个 nonlocal 变量占 8 字节(i8*),指向父函数的 alloca(共享存储) - env_ptr: llvmlite.Value | t.CPtr = None - if nested_nonlocal_count > 0: - env_size: t.CInt64T = nested_nonlocal_count * 8 - env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size) - if env_size_val is not None: - env_size_val.Next = None - env_ptr = llvmlite.build_call( - parent_builder, "malloc", env_size_val, 1, i8_ptr_ty, 0) - - # 为每个 nonlocal 变量建立堆存储并注册到 env: - # 运行时 env 链式共享(无需编译期映射——映射存编译期 Value 对象 - # 地址会被 inttoptr 当运行时堆地址解引用崩溃;存整型也拿不到 - # 运行时 malloc 结果): - # - 变量是父函数局部(lookup_current 在当前作用域找到 alloca) - # → 新建堆(malloc + 拷贝当前值),并把父函数 VarEntry.Alloca - # 重定向到堆——父函数后续读写走共享堆 - # - 变量是父函数的 nonlocal(多层嵌套,当前作用域无 alloca) - # → 从父函数 env 读该变量的堆地址(_load_raw_var_ptr), - # env[ni] = 同一堆地址(运行时链式共享,各层写同一堆) - if env_ptr is not None: - i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) - for ni in range(nested_nonlocal_count): - # 从 _nonlocal_names 缓冲区读取名称 - name_addr: t.CUInt64T = t.CUInt64T(trans._nonlocal_names) + ni * 8 - nl_slot: str | t.CPtr = (t.CVoid | t.CPtr)(name_addr) - nl_name: str = nl_slot[0] - if nl_name is None: - continue - heap_store: llvmlite.Value | t.CPtr = None - # 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为 - # 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获 - # 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享) - gname_ck: str = self._nl_heap_global_name( - pool, trans, nl_name) - if gname_ck is not None \ - and self._nl_heap_global_exists(mod, gname_ck) != 0: - gv_ref0: llvmlite.Value | t.CPtr = self._nl_heap_global_ref( - pool, trans, nl_name) - if gv_ref0 is not None: - heap_store = llvmlite.build_load( - parent_builder, i8_ptr_ty, gv_ref0) - if heap_store is None: - local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current( - trans.SymTab, nl_name) - if local_alloca is not None and local_alloca.Ty is not None: - # 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向 - nl_load_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee - if nl_load_ty is not None: - cur_val_nl: llvmlite.Value | t.CPtr = llvmlite.build_load( - parent_builder, nl_load_ty, local_alloca) - # 堆存储大小按类型字节数:数组(如 [4 x i32]=16 字节) - # 不能固定 8 字节(越界写崩溃);指针/标量 ≥ 8 字节 - st_size: t.CInt = self._type_byte_size(pool, nl_load_ty) - st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64( - pool, st_size) - if st_size_val is not None: - st_size_val.Next = None - var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( - parent_builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) - if var_storage is not None: - # 拷贝当前值到堆存储 - st_slot_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty)) - if st_slot_nl is not None and cur_val_nl is not None: - llvmlite.build_store(parent_builder, cur_val_nl, st_slot_nl) - # 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆) - ve_nl: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( - trans.SymTab, nl_name) - heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty)) - if ve_nl is not None and heap_typed is not None: - ve_nl.Alloca = heap_typed - # 记录到全局映射(后续层/父函数可见) - gv_ref_new: llvmlite.Value | t.CPtr = None - if gname_ck is not None \ - and self._nl_heap_global_exists(mod, gname_ck) == 0: - gv_new: llvmlite.GlobalVariable | t.CPtr = \ - llvmlite.new_global_variable(pool, gname_ck, i8_ptr_ty) - if gv_new is not None: - # 清除 external linkage + 零初始化(防 undefined reference) - gv_new.Linkage = None - gv_new.Initializer = "null" - llvmlite.module_add_global(mod, gv_new) - gv_ref_new = self._nl_heap_global_ref(pool, trans, nl_name) - if gv_ref_new is not None: - llvmlite.build_store( - parent_builder, var_storage, gv_ref_new) - heap_store = var_storage - else: - # 父函数中该变量是 nonlocal(多层嵌套):从父函数 env - # 读堆地址(运行时链式共享同一堆),不新建 - heap_store = self.Gen.StmtGenDecl._load_raw_var_ptr(trans, nl_name) - if heap_store is None: - continue - # env[ni] = 堆存储地址(共享) - offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ni * 8) - slot: llvmlite.Value | t.CPtr = llvmlite.build_gep( - parent_builder, i8_ty, env_ptr, offset_val) - if slot is not None: - slot_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, slot, i8_ptr_ptr_ty) - if slot_typed is not None: - llvmlite.build_store(parent_builder, heap_store, slot_typed) - - # 13b. 创建闭包结构: malloc(16) - closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16) - if closure_size_val is not None: - closure_size_val.Next = None - closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call( - parent_builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0) - if closure_ptr is None: - # malloc 失败,恢复状态 - trans._cur_func_name = old_func_name - trans._global_name_count = old_global_count - trans._nonlocal_name_count = old_nonlocal_count - trans._closure_env_count = old_env_count - trans._nonlocal_names = saved_nl_names - trans._nonlocal_types = saved_nl_types - return 0 - - # 13c. 存储 fn_ptr 到 offset 0 - # 创建函数指针类型 i32(i8*)* - fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) - fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1) - fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty) - - # 创建函数引用 Value(类型为 i32(i8*)*,使用 SHA1 混淆名) - fn_ptr_name: str = pool.alloc(64) - if fn_ptr_name is not None: - # SHA1 前缀名含 '.' 需要加引号(如 @"sha1.__closure_func") - if string.strchr(mangled_promoted, '.') is not None: - viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted) - else: - viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted) - fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name) - - # bitcast 函数指针到 i8*(闭包存储 i8* 类型) - fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, fn_ptr_val, i8_ptr_ty) - fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty)) - if fn_slot is not None and fn_as_i8ptr is not None: - llvmlite.build_store(parent_builder, fn_as_i8ptr, fn_slot) - - # 13d. 存储 env_ptr 到 offset 8 - eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8) - env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( - parent_builder, i8_ty, closure_ptr, eight_val) - if env_slot_addr is not None: - env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - parent_builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty)) - if env_slot is not None: - if env_ptr is not None: - llvmlite.build_store(parent_builder, env_ptr, env_slot) - else: - # 无 nonlocal 变量,存储 null - null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null") - llvmlite.build_store(parent_builder, null_val, env_slot) - - # 13e. 将闭包指针存储到父函数的局部变量 {func_name} - closure_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(parent_builder, i8_ptr_ty) - if closure_alloca is not None: - llvmlite.build_store(parent_builder, closure_ptr, closure_alloca) - self.Gen.VarGen.define_var(trans.SymTab, func_name, closure_alloca) - # 注册闭包变量名(供调用时区分闭包 vs 函数指针变量, - # 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分) - self.Gen.CallGenLeafKit.register_closure_name(func_name) - - # 14. 恢复 scope names(含 nonlocal 集合指针) - trans._cur_func_name = old_func_name - trans._global_name_count = old_global_count - trans._nonlocal_name_count = old_nonlocal_count - trans._closure_env_count = old_env_count - trans._nonlocal_names = saved_nl_names - trans._nonlocal_types = saved_nl_types - - return 1 - - # ============================================================ - # _type_byte_size - 计算 LLVM 类型占用的字节大小(递归) - # - # 用于 nonlocal 共享堆存储分配:数组(如 [4 x i32]=16 字节)不能 - # 用固定 8 字节(越界写破坏 canary/邻块 → 运行时崩溃, - # ClosureTest2 Test 4 非local 数组崩溃根因)。未知类型保守返回 8。 - # ============================================================ - def _type_byte_size(self, pool: memhub.MemBuddy | t.CPtr, - ty: llvmlite.LLVMType | t.CPtr) -> t.CInt: - """计算 LLVM 类型占用的字节大小(递归),未知类型返回 8""" - if ty is None: - return 8 - match ty: - case llvmlite.LLVMType.Int(bits): - sz_i: t.CInt = (bits + 7) // 8 - if sz_i < 8: - sz_i = 8 - return sz_i - case llvmlite.LLVMType.Ptr(_pe): - return 8 - case llvmlite.LLVMType.Float(fb): - if fb == 64: - return 8 - if fb >= 128: - return 16 - return 4 - case llvmlite.LLVMType.Array(et, cnt): - esz: t.CInt = self._type_byte_size(pool, et) - return esz * cnt - case _: - return 8 - - # ============================================================ - # _nl_heap_global_name - nonlocal 堆地址全局映射变量名 - # - # 变量名: nlheap_{sha1}.{name}(全局变量,跨函数可见;SSA 编号是函数 - # 局部的,跨函数传递堆地址只能经全局变量/模块级映射)。 - # ============================================================ - def _nl_heap_global_name(self, pool: memhub.MemBuddy | t.CPtr, - trans: HT.Translator | t.CPtr, - name: str) -> str: - """构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}""" - if pool is None or trans is None or name is None: - return None - mangled: str = self.Gen.FuncGenBase._mangle_name(trans, name) - buf: str = pool.alloc(160) - if buf is not None: - viperlib.snprintf(buf, 160, "nlheap_%s", mangled) - return buf - - # ============================================================ - # _nl_heap_global_ref - 全局映射变量引用 Value(i8**) - # ============================================================ - def _nl_heap_global_ref(self, pool: memhub.MemBuddy | t.CPtr, - trans: HT.Translator | t.CPtr, - name: str) -> llvmlite.Value | t.CPtr: - """获取 nonlocal 堆地址全局映射变量的引用(@"..." 引号包裹)""" - if pool is None or trans is None or name is None: - return None - gname: str = self._nl_heap_global_name(pool, trans, name) - if gname is None: - return None - i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - ref_buf: str = pool.alloc(176) - if ref_buf is None: - return None - viperlib.snprintf(ref_buf, 176, "@\"%s\"", gname) - return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, i8_ptr_ty), ref_buf) - - # ============================================================ - # _nl_heap_global_exists - 检查 nonlocal 堆地址全局映射变量是否存在 - # ============================================================ - def _nl_heap_global_exists(self, mod: llvmlite.LLVMModule | t.CPtr, - gname: str) -> int: - """检查全局映射变量 gname 是否已在模块中,1=存在""" - if mod is None or gname is None: - return 0 - gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead - while gcur is not None: - if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0: - return 1 - gcur = gcur.Next - return 0 - - # ============================================================ - # _collect_chain_nonlocals - 递归收集嵌套函数链内 nonlocal 声明 - # - # 遍历 node(FunctionDef)的 children:遇嵌套 FunctionDef 递归, - # 遇 Nonlocal 语句收集变量名(去重)。返回更新后的计数。 - # ============================================================ - def _collect_chain_nonlocals(self, trans: HT.Translator | t.CPtr, - node: ast.AST | t.CPtr, - names: t.CVoid | t.CPtr, - count: int) -> int: - """递归收集 node(FunctionDef)链内所有 nonlocal 声明名(去重)""" - if node is None or names is None: - return count - fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node) - if fd is None or fd.children is None: - return count - ch: list[ast.AST | t.CPtr] | t.CPtr = fd.children - cn: t.CSizeT = len(ch) - for ci in range(cn): - st: ast.AST | t.CPtr = ch.get(ci) - if st is None: - continue - k: int = st.kind() - if k == ast.ASTKind.FunctionDef: - count = self._collect_chain_nonlocals(trans, st, names, count) - elif k == ast.ASTKind.Nonlocal: - nl_node: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(st) - if nl_node is not None and nl_node.names is not None: - nl_list: list[ast.AST | t.CPtr] | t.CPtr = nl_node.names - nn: t.CSizeT = len(nl_list) - for ni in range(nn): - nm_node: ast.AST | t.CPtr = nl_list.get(ni) - if nm_node is None: - continue - nm_x: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node) - if nm_x is None or nm_x.id is None: - continue - dup: int = 0 - for di in range(count): - if string.strcmp((str)( - t.CUInt64T(names) + di * 8), nm_x.id) == 0: - dup = 1 - break - if dup == 0 and count < 64: - nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(names) + count * 8) - nm_slot[0] = nm_x.id - count += 1 - return count - - # ============================================================ - # _prepare_chain_nonlocals - 预扫描嵌套函数链内 nonlocal 并建堆 - # - # 在翻译嵌套函数定义(translate_nested_function_def)之前调用。 - # 场景(ClosureTest2 Test 7):level1→a、level2→b、level3→c_val 各自 - # nonlocal 不同变量,都定义在最外层函数——level1 env 无 b 槽,level2 - # 无法经 env 链获取 b 的堆。此处在最外层函数中为链内所有 nonlocal - # 建堆 + 重定向 VarEntry + 把堆地址 store 到模块级映射 - # @nlheap.{sha1}.{name}(全局变量跨函数可见),内层闭包创建时从映射 load。 - # ============================================================ - def _prepare_chain_nonlocals(self, trans: HT.Translator | t.CPtr, - node: ast.AST | t.CPtr, - pool: memhub.MemBuddy | t.CPtr, - builder: llvmlite.IRBuilder | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - i8_ty: llvmlite.LLVMType | t.CPtr, - i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> int: - """预收集嵌套函数链内 nonlocal 并建堆,1 成功""" - if node is None or trans is None or builder is None or mod is None: - return 0 - # 每函数只预建一次:多个嵌套函数定义共享同一堆(防重复 malloc + - # 二次重定向 VarEntry.Alloca 导致兄弟闭包各持一堆,计数不同步) - if trans._nl_chain_done != 0: - return 1 - if trans._nl_chain_names is None: - trans._nl_chain_names = pool.alloc(8 * 64) - if trans._nl_chain_names is not None: - string.memset(trans._nl_chain_names, 0, 8 * 64) - if trans._nl_chain_names is None: - return 0 - trans._nl_chain_count = self._collect_chain_nonlocals( - trans, node, trans._nl_chain_names, 0) - trans._nl_chain_done = 1 - for ci in range(trans._nl_chain_count): - nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(trans._nl_chain_names) + ci * 8) - nl_name: str = nm_slot[0] - if nl_name is None: - continue - # 只在"定义它的函数"(当前顶层作用域有 alloca)中建堆 - local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current( - trans.SymTab, nl_name) - if local_alloca is None or local_alloca.Ty is None: - continue - nl_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee - if nl_ty is None: - continue - gname: str = self._nl_heap_global_name(pool, trans, nl_name) - if gname is None: - continue - # 建堆(按类型字节大小)并拷贝当前值——每次本函数执行都新建共享堆, - # 并 store 到模块级映射(跨函数可见)。注意:不能因映射变量已存在 - # 就跳过(前一个测试函数已把 @nlheap.* 加入模块——若跳过,本函数 - # 运行时 @nlheap.* 保持上一函数的堆地址,env 读到错堆。 - # ClosureTest2 Test 7 根因)。映射变量仅首次 add(模块去重), - # store 指令每次照发。 - st_size: t.CInt = self._type_byte_size(pool, nl_ty) - st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size) - if st_size_val is not None: - st_size_val.Next = None - var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( - builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) - if var_storage is None: - continue - cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, nl_ty, local_alloca) - st_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, var_storage, llvmlite.Ptr(pool, nl_ty)) - if st_slot is not None and cur_val is not None: - llvmlite.build_store(builder, cur_val, st_slot) - # 重定向 VarEntry(父函数读写走共享堆) - ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( - trans.SymTab, nl_name) - if ve is not None: - heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, var_storage, llvmlite.Ptr(pool, nl_ty)) - if heap_typed is not None: - ve.Alloca = heap_typed - # 创建/复用全局映射变量并 store 堆地址(跨函数可见) - gv_found: llvmlite.GlobalVariable | t.CPtr = None - gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead - while gcur is not None: - if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0: - gv_found = gcur - break - gcur = gcur.Next - if gv_found is None: - gv_found = llvmlite.new_global_variable(pool, gname, i8_ptr_ty) - if gv_found is not None: - # 清除 external linkage 并给零初始化,否则打印为 external - # 声明、store 引用无定义体 → 链接 undefined reference - gv_found.Linkage = None - gv_found.Initializer = "null" - llvmlite.module_add_global(mod, gv_found) - if gv_found is not None: - gv_ref: llvmlite.Value | t.CPtr = self._nl_heap_global_ref( - pool, trans, nl_name) - if gv_ref is not None: - llvmlite.build_store(builder, var_storage, gv_ref) - return 1 - - # ============================================================ - # translate_lambda - 翻译 lambda 表达式 - # - # 与 translate_nested_function_def 同架构(TPV 闭包 {i8* fn_ptr, - # i8* env_ptr},16 字节): - # 1. 提升函数 __lambda_{id}(i8* %env, [args...]) — 恒返回 i32 - # 2. 捕获变量按值拷贝到堆存储(env[i] = 堆地址),避免闭包返回 - # 后父栈帧销毁导致悬垂 - # 3. 创建闭包 {fn_ptr, env_ptr},返回 i8* 闭包指针 - # 赋值语句据此创建 i8** alloca,调用走 translate_closure_call - # ============================================================ - def translate_lambda(self, builder: llvmlite.IRBuilder | t.CPtr, - pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - node: ast.AST | t.CPtr, - trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: - """翻译 lambda 表达式,返回闭包指针(i8*)""" - if node is None or trans is None or builder is None: - return None - lam: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node) - if lam is None: - return None - - i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) - i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) - - # 1. lambda id + 提升函数名 - # 注意:计数器放 trans(_lambda_count)而非 self——Mixin 类自身无字段, - # self.新字段在 TPC 编译 Mixin 时查 class_members 失败触发未完成的 - # _PreRegisterClassMembers fallback 崩溃(FuncGen.py L437) - lambda_id: int = trans._lambda_count - trans._lambda_count += 1 - pname_buf: str = pool.alloc(64) - if pname_buf is None: - return None - viperlib.snprintf(pname_buf, 64, "__lambda_%d", lambda_id) - mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, pname_buf) - - # 2. 解析参数(名称 + 类型,类型默认 i32,有注解则解析) - param_names: str = pool.alloc(32 * 64) # 64 参数 × 32 字节名 - if param_names is None: - return None - string.memset(param_names, 0, 32 * 64) - param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # LLVMType 指针槽 - if param_tys is None: - return None - string.memset(param_tys, 0, 8 * 64) - param_count: int = 0 - if lam.args is not None: - ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(lam.args) - if ags.args is not None: - alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args - pn: t.CSizeT = len(alist) - for pi in range(pn): - if param_count >= 64: - break - arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(pi)) - if arg_n is None or arg_n.arg is None: - continue - string.strncpy(param_names + param_count * 32, arg_n.arg, 31) - p_ty: llvmlite.LLVMType | t.CPtr = i32_ty - if arg_n.annotation is not None: - resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( - pool, arg_n.annotation, trans._imported_modules, trans._from_imports, trans) - if resolved is None: - # fail-fast:lambda 参数有注解但类型解析失败,不再静默保持 i32 - self.Gen.TypeGenErr.fatal_error( - arg_n, f"参数 '{arg_n.arg}' 类型解析失败: {self.Gen.TypeGenResolve.annotation_to_str(arg_n.annotation)}", - "NameError") - resolved = i32_ty # 不可达(fatal_error 已退出) - if resolved is not None: - match resolved: - case llvmlite.LLVMType.Struct(_, _, _): - p_ty = llvmlite.Ptr(pool, resolved) - case llvmlite.LLVMType.Array(aelem_ty_l, _): - p_ty = llvmlite.Ptr(pool, aelem_ty_l) - case _: - p_ty = resolved - ty_slot_p: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(param_tys) + param_count * 8) - ty_slot_p[0] = p_ty - param_count += 1 - - # 3. 收集捕获变量(body 中的外部 Name 引用) - cap_names: str = pool.alloc(64 * 64) # 64 捕获 × 64 字节名 - if cap_names is None: - return None - string.memset(cap_names, 0, 64 * 64) - cap_allocas: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 父函数 alloca 指针槽 - if cap_allocas is None: - return None - string.memset(cap_allocas, 0, 8 * 64) - cap_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 捕获变量类型槽 - if cap_tys is None: - return None - string.memset(cap_tys, 0, 8 * 64) - cap_count: int = self._lambda_scan_body( - trans, lam.body, param_names, param_count, - cap_names, cap_allocas, cap_tys, 0) - - # 4. 创建提升函数 __lambda_{id}(i8* %env, [args...]) - func: llvmlite.Function | t.CPtr = llvmlite.create_function( - pool, mod, mangled_promoted, i32_ty) - if func is None: - return None - llvmlite.add_param(pool, func, i8_ptr_ty, "%env") - for pi2 in range(param_count): - ty_addr2: t.CUInt64T = t.CUInt64T(param_tys) + pi2 * 8 - ty_slot2: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr2) - p_ty2: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot2[0]) - if p_ty2 is None: - p_ty2 = i32_ty - pname_l: str = pool.alloc(32) - if pname_l is not None: - viperlib.snprintf(pname_l, 32, "%%%s", param_names + pi2 * 32) - llvmlite.add_param(pool, func, p_ty2, pname_l) - - # 5. entry 块 + builder - entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry") - if entry_blk is None: - return None - func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func) - if func_builder is None: - return None - llvmlite.position_at_end(func_builder, entry_blk) - - # 6. 进入 lambda 作用域 - self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION) - - # 7. _env_ptr alloca(存储 %env 参数) - env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty) - if env_alloca is not None: - self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca) - env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env") - llvmlite.build_store(func_builder, env_param_val, env_alloca) - - # 8. 保存父函数状态并切换到提升函数 - old_func: llvmlite.Function | t.CPtr = trans._cur_func - old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder - old_func_name: str = trans._cur_func_name - old_global_count: int = trans._global_name_count - old_nonlocal_count: int = trans._nonlocal_name_count - old_env_count: int = trans._closure_env_count - trans._cur_func = func - trans._cur_builder = func_builder - trans._cur_func_name = mangled_promoted - HT.clear_scope_names(trans) - - # 9. 捕获变量:从 env 堆槽加载值到局部 alloca - for ci in range(cap_count): - # 直接 str 指针偏移取捕获名(与 param_names + pi*32 一致)。 - # 注意:不能用 (str)(cap_addr) 整数转换——TPV 将其编译为 itoa(整数转字符串), - # 会把 cap_slot 变成数字串,define_var 键名错乱,lambda body lookup 无法命中。 - cap_slot: str = cap_names + ci * 64 - if cap_slot[0] == '\0': - continue - ty_addr3: t.CUInt64T = t.CUInt64T(cap_tys) + ci * 8 - ty_slot3: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr3) - cap_ty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot3[0]) - if cap_ty is None: - continue - off_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci * 8) - slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( - func_builder, i8_ty, env_param_val, off_val) - if slot_addr is None: - continue - storage_addr: llvmlite.Value | t.CPtr = llvmlite.build_load( - func_builder, i8_ptr_ty, slot_addr) - if storage_addr is None: - continue - st_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - func_builder, storage_addr, llvmlite.Ptr(pool, cap_ty)) - if st_typed is None: - continue - cap_val: llvmlite.Value | t.CPtr = llvmlite.build_load( - func_builder, cap_ty, st_typed) - if cap_val is None: - continue - var_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, cap_ty) - if var_alloca is not None: - llvmlite.build_store(func_builder, cap_val, var_alloca) - self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca) - - # 10. 参数 alloca - for pi3 in range(param_count): - ty_addr4: t.CUInt64T = t.CUInt64T(param_tys) + pi3 * 8 - ty_slot4: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr4) - p_ty3: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot4[0]) - if p_ty3 is None: - # fail-fast:lambda 参数类型槽缺失(内部状态不一致),不再静默保持 i32 - self.Gen.TypeGenErr.fatal_error( - lam, f"lambda 参数 '{param_names + pi3 * 32}' 类型信息缺失", "InternalCompilerError") - p_ty3 = i32_ty # 不可达(fatal_error 已退出) - pname_ref: str = pool.alloc(32) - if pname_ref is not None: - viperlib.snprintf(pname_ref, 32, "%%%s", param_names + pi3 * 32) - param_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, p_ty3, pname_ref) - arg_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, p_ty3) - if arg_alloca is not None: - llvmlite.build_store(func_builder, param_ref, arg_alloca) - self.Gen.VarGen.define_var(trans.SymTab, param_names + pi3 * 32, arg_alloca) - - # 11. 翻译 body 表达式,coerce 到 i32 后 ret - body_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( - func_builder, pool, mod, lam.body, None, 0, trans) - if body_val is not None and body_val.Ty is not None: - if self.Gen.ExprGenBase.get_llvm_type_bits(body_val.Ty) == 32 \ - and self.Gen.ExprGenBase.is_ptr_type(body_val.Ty) == 0: - llvmlite.build_ret(func_builder, body_val) - else: - ret_val: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type( - func_builder, body_val, i32_ty, lam.body) - if ret_val is not None: - llvmlite.build_ret(func_builder, ret_val) - else: - # fail-fast:lambda 体类型转换失败,不再静默 ret 0 - self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError") - else: - # fail-fast:lambda 体翻译失败,不再静默 ret 0 - self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError") - - # 12. 退出 lambda 作用域,恢复父函数状态 - self.Gen.VarGen.exit_scope(trans.SymTab) - trans._cur_func = old_func - trans._cur_builder = old_builder - trans._cur_func_name = old_func_name - trans._global_name_count = old_global_count - trans._nonlocal_name_count = old_nonlocal_count - trans._closure_env_count = old_env_count - - # 13. 在父函数中创建 env(每个捕获变量 8 字节堆存储,env[i] = 堆地址) - env_ptr: llvmlite.Value | t.CPtr = None - if cap_count > 0: - env_size: t.CInt64T = cap_count * 8 - env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size) - if env_size_val is not None: - env_size_val.Next = None - env_ptr = llvmlite.build_call( - builder, "malloc", env_size_val, 1, i8_ptr_ty, 0) - if env_ptr is not None: - for ci3 in range(cap_count): - # 与步骤 9 一致:str 指针偏移判空,避免整数转换 itoa 语义 - cap_slot3: str = cap_names + ci3 * 64 - if cap_slot3[0] == '\0': - continue - al_addr: t.CUInt64T = t.CUInt64T(cap_allocas) + ci3 * 8 - al_slot: str | t.CPtr = (t.CVoid | t.CPtr)(al_addr) - var_alloca2: llvmlite.Value | t.CPtr = ( - llvmlite.Value | t.CPtr)(al_slot[0]) - if var_alloca2 is None: - continue - ty_addr5: t.CUInt64T = t.CUInt64T(cap_tys) + ci3 * 8 - ty_slot5: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr5) - cap_ty2: llvmlite.LLVMType | t.CPtr = ( - llvmlite.LLVMType | t.CPtr)(ty_slot5[0]) - if cap_ty2 is None: - continue - # 堆存储大小:位宽/8,至少 8 字节(容纳指针/标量) - ty_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cap_ty2) - st_size: int = 8 - if ty_bits > 64: - st_size = ty_bits // 8 - st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size) - if st_size_val is not None: - st_size_val.Next = None - var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( - builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) - if var_storage is not None: - cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, cap_ty2, var_alloca2) - if cur_val is not None: - st_slot5: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, var_storage, llvmlite.Ptr(pool, cap_ty2)) - if st_slot5 is not None: - llvmlite.build_store(builder, cur_val, st_slot5) - off_val2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci3 * 8) - slot2: llvmlite.Value | t.CPtr = llvmlite.build_gep( - builder, i8_ty, env_ptr, off_val2) - if slot2 is not None: - slot_typed2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, slot2, i8_ptr_ptr_ty) - if slot_typed2 is not None: - llvmlite.build_store(builder, var_storage, slot_typed2) - - # 14. 在父函数中创建闭包 malloc(16) - closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16) - if closure_size_val is not None: - closure_size_val.Next = None - closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call( - builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0) - if closure_ptr is None: - return None - - # 15. [0] = fn_ptr(函数引用 bitcast 到 i8*) - fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) - fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1) - fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty) - fn_ptr_name: str = pool.alloc(64) - if fn_ptr_name is not None: - if string.strchr(mangled_promoted, '.') is not None: - viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted) - else: - viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted) - fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name) - fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, fn_ptr_val, i8_ptr_ty) - fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty)) - if fn_slot is not None and fn_as_i8ptr is not None: - llvmlite.build_store(builder, fn_as_i8ptr, fn_slot) - - # 16. [8] = env_ptr(无捕获存 null) - eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8) - env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( - builder, i8_ty, closure_ptr, eight_val) - if env_slot_addr is not None: - env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty)) - if env_slot is not None: - if env_ptr is not None: - llvmlite.build_store(builder, env_ptr, env_slot) - else: - null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null") - llvmlite.build_store(builder, null_val, env_slot) - - # 17. 返回闭包指针(i8*) - return closure_ptr - - # ============================================================ - # _lambda_scan_body - 递归扫描 lambda body 中的 Name 引用 - # - # 收集外部变量(排除 lambda 参数、模块级全局、已捕获项), - # 记录到 cap_names/cap_allocas/cap_tys 三个并行缓冲。 - # 返回更新后的捕获数量(递归调用逐层累加)。 - # ============================================================ - def _lambda_scan_body(self, trans: HT.Translator | t.CPtr, - node: ast.AST | t.CPtr, - param_names: str, - param_count: int, - cap_names: str, - cap_allocas: t.CVoid | t.CPtr, - cap_tys: t.CVoid | t.CPtr, - cap_count: int) -> int: - """递归扫描 lambda body 中的 Name 引用,收集捕获变量""" - if node is None or trans is None: - return cap_count - k: int = node.kind() - if k == ast.ASTKind.Name: - nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) - if nm.id is None: - return cap_count - # 排除 lambda 参数 - for pi in range(param_count): - if string.strcmp(param_names + pi * 32, nm.id) == 0: - return cap_count - # 已捕获跳过 - for ci in range(cap_count): - if string.strcmp(cap_names + ci * 64, nm.id) == 0: - return cap_count - # 查当前作用域链 - alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) - if alloca is None or alloca.Ty is None: - return cap_count - pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee - if pointee is None: - return cap_count - # 跳过模块级全局(body 直接解析全局,无需 env 捕获) - mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) - if mod_gv is not None: - if t.CUInt64T(mod_gv) == t.CUInt64T(alloca): - return cap_count - if cap_count >= 64: - return cap_count - string.strncpy(cap_names + cap_count * 64, nm.id, 63) - al_slot: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(cap_allocas) + cap_count * 8) - al_slot[0] = alloca - ty_slot: str | t.CPtr = (t.CVoid | t.CPtr)( - t.CUInt64T(cap_tys) + cap_count * 8) - ty_slot[0] = pointee - return cap_count + 1 - # 递归遍历常见表达式子节点 - if k == ast.ASTKind.BinOp: - bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) - cc1: int = self._lambda_scan_body(trans, bop.left, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - return self._lambda_scan_body(trans, bop.right, param_names, param_count, - cap_names, cap_allocas, cap_tys, cc1) - if k == ast.ASTKind.UnaryOp: - uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) - return self._lambda_scan_body(trans, uop.operand, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - if k == ast.ASTKind.BoolOp: - bop2: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node) - cc2: int = cap_count - if bop2.values is not None: - vs: list[ast.AST | t.CPtr] | t.CPtr = bop2.values - vn: t.CSizeT = len(vs) - for vi in range(vn): - cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count, - cap_names, cap_allocas, cap_tys, cc2) - return cc2 - if k == ast.ASTKind.Compare: - cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node) - cc3: int = self._lambda_scan_body(trans, cmp.left, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - if cmp.comparators is not None: - cl: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators - cn: t.CSizeT = len(cl) - for cvi in range(cn): - cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count, - cap_names, cap_allocas, cap_tys, cc3) - return cc3 - if k == ast.ASTKind.Call: - cal: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) - cc4: int = self._lambda_scan_body(trans, cal.func, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - if cal.args is not None: - als: list[ast.AST | t.CPtr] | t.CPtr = cal.args - an: t.CSizeT = len(als) - for ai in range(an): - cc4 = self._lambda_scan_body(trans, als.get(ai), param_names, param_count, - cap_names, cap_allocas, cap_tys, cc4) - if cal.keywords is not None: - kws: list[ast.AST | t.CPtr] | t.CPtr = cal.keywords - kn: t.CSizeT = len(kws) - for ki in range(kn): - kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kws.get(ki)) - if kw is not None and kw.value is not None: - cc4 = self._lambda_scan_body(trans, kw.value, param_names, param_count, - cap_names, cap_allocas, cap_tys, cc4) - return cc4 - if k == ast.ASTKind.Subscript: - sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) - cc5: int = self._lambda_scan_body(trans, sub.value, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - if sub.slice is not None: - cc5 = self._lambda_scan_body(trans, sub.slice, param_names, param_count, - cap_names, cap_allocas, cap_tys, cc5) - return cc5 - if k == ast.ASTKind.Attribute: - at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) - return self._lambda_scan_body(trans, at.value, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - if k == ast.ASTKind.IfExp: - ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node) - cc6: int = self._lambda_scan_body(trans, ie.test, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - cc6 = self._lambda_scan_body(trans, ie.body, param_names, param_count, - cap_names, cap_allocas, cap_tys, cc6) - return self._lambda_scan_body(trans, ie.orelse, param_names, param_count, - cap_names, cap_allocas, cap_tys, cc6) - if k == ast.ASTKind.List: - ls: ast.List | t.CPtr = (ast.List | t.CPtr)(node) - cc7: int = cap_count - if ls.elts is not None: - es: list[ast.AST | t.CPtr] | t.CPtr = ls.elts - en: t.CSizeT = len(es) - for ei in range(en): - cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count, - cap_names, cap_allocas, cap_tys, cc7) - return cc7 - if k == ast.ASTKind.Lambda: - lam2: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node) - return self._lambda_scan_body(trans, lam2.body, param_names, param_count, - cap_names, cap_allocas, cap_tys, cap_count) - return cap_count +import lib.core.Gen.GenTypes as GenTypes +import t, c +from stdint import * +import ast +import llvmlite +import memhub +import string +import viperlib +import stdio +import stdlib +import lib.core.VLogger as VLogger +import lib.core.Handles.HandlesTranslator as HT +import lib.core.Handles.HandlesExpr as HandlesExpr +import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Gen.CallGenBase as CallGenBase + + +# ============================================================ +# FuncGenMixin - 函数/方法声明与翻译 Mixin(对象化 P5-1,wiki/14-oo-refactor.md) +# +# 从 HandlesFunctions.vp 迁移:模块级纯函数(trans 首参,无全局状态) +# → 类方法(self 替代,trans 参数保留)。 +# +# 迁移原则(与 P2/P3/P4 一致,行为零变化): +# 1. 方法保留原函数签名(含 trans 首参),仅函数体内的 +# 本模块函数调用改为 self.xxx(...) +# 2. 外部模块调用(HandlesExprCall.xxx/HandlesType.xxx 等)保留模块前缀 +# 3. HandlesFunctions.vp 原函数保留为"委托":本地模块级单例 _g_gen +# (BuildPipeline/main 注入)cast 成 Generator 类型 +# +# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。 +# ============================================================ + + +# ============================================================ +# HandlesFunctions - 函数定义处理(trans 单参模式) +# +# 参考 Python 版 TransPyC FunctionHandle +# +# 函数有自己的局部作用域,通过 HandlesVar.enter_scope/exit_scope +# 管理嵌套作用域链,翻译函数体前进入函数作用域,翻译完退出。 +# ============================================================ + + +# 作用域类型常量(本地副本,与 HandlesFunctions.vp 保持一致) +SCOPE_FUNCTION: t.CDefine = 1 + + +# ============================================================ +# FuncGenMixin - 函数属性/命名/声明/翻译 +# ============================================================ +@t.NoVTable +class FuncGenMixin(CallGenBase.CallGenBase): + """函数/方法翻译 Mixin(纯方法化迁移,无状态字段)""" + + + + + + + + + + + + + + # ============================================================ + # translate_nested_function_def - 嵌套函数提升 + 闭包创建 + # + # 将嵌套函数提升为顶层函数 @__closure_{name}(i8* %env) -> i32, + # 在父函数中创建闭包对象 {i8* fn_ptr, i8* env_ptr} 并存储到 + # 以函数名命名的局部变量中。 + # + # 闭包结构: {i8* fn_ptr, i8* env_ptr} (16 字节, malloc 分配) + # Env 结构: {i8* ptr0, i8* ptr1, ...} (每个 nonlocal 变量 8 字节,指向父函数 alloca) + # ============================================================ + def translate_nested_function_def(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr) -> int: + """翻译嵌套函数定义:提升为顶层函数 + 创建闭包""" + fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node) + if fd is None or fd.name is None: + return 0 + + pool: memhub.MemBuddy | t.CPtr = trans.Pool + mod: llvmlite.LLVMModule | t.CPtr = trans.Module + func_name: str = fd.name + + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + + # 1. 构建提升后的函数名: __closure_{name}(加 SHA1 前缀避免跨模块冲突) + promoted_name: str = pool.alloc(64) + if promoted_name is not None: + viperlib.snprintf(promoted_name, 64, "__closure_%s", func_name) + mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, promoted_name) + + # 2. 创建提升后的函数: define i32 @__closure_{name}(i8* %env, args...) + func: llvmlite.Function | t.CPtr = llvmlite.create_function( + pool, mod, mangled_promoted, i32_ty) + if func is None: + return 0 + llvmlite.add_param(pool, func, i8_ptr_ty, "%env") + + # 2a. 嵌套函数参数:加入提升函数签名(i8* %env, 参数...), + # 类型存入 param_tys 缓冲(CVoid 跳过,与 alloca 创建索引对齐) + imported_modules: str = trans._imported_modules + from_imports: str = trans._from_imports + param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) + if param_tys is not None: + string.memset(param_tys, 0, 8 * 64) + param_count: int = 0 + if fd.args is not None: + ags_n: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) + if ags_n.args is not None: + alist_n: list[ast.AST | t.CPtr] | t.CPtr = ags_n.args + an_n: t.CSizeT = len(alist_n) + for ai_n in range(an_n): + arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_n.get(ai_n)) + if arg_n is None or arg_n.arg is None: + continue + if arg_n.annotation is not None: + if self.Gen.TypeGenResolve._is_t_attr(arg_n.annotation, "CVoid", imported_modules) != 0: + continue + pty_n: llvmlite.LLVMType | t.CPtr = i32_ty + if arg_n.annotation is not None: + resolved_n: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( + pool, arg_n.annotation, imported_modules, from_imports, trans, 0) + if resolved_n is not None: + match resolved_n: + case llvmlite.LLVMType.Struct(_, _, _): + pty_n = llvmlite.Ptr(pool, resolved_n) + case llvmlite.LLVMType.Array(aelem_ty_n, _): + pty_n = llvmlite.Ptr(pool, aelem_ty_n) + case _: + pty_n = resolved_n + if param_count < 64 and param_tys is not None: + pty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(param_tys) + param_count * 8) + pty_slot_n[0] = pty_n + pname_n: str = pool.alloc(32) + if pname_n is not None: + viperlib.snprintf(pname_n, 32, "%%%s", arg_n.arg) + llvmlite.add_param(pool, func, pty_n, pname_n) + param_count += 1 + + # 3. 创建 entry 块 + builder + entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry") + if entry_blk is None: + return 0 + func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func) + if func_builder is None: + return 0 + llvmlite.position_at_end(func_builder, entry_blk) + + # 4. 进入嵌套函数作用域(嵌套符号表) + self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION) + + # 5. 创建 _env_ptr alloca 并存储 %env 参数 + env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty) + if env_alloca is not None: + self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca) + env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env") + llvmlite.build_store(func_builder, env_param_val, env_alloca) + + # 5a. 创建嵌套函数参数 alloca(函数体内参数名可引用,如 + # accumulate(n) 内 for i in range(n)、set_result(val) 内 result = val) + if fd.args is not None: + ags_p: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) + if ags_p.args is not None: + alist_p: list[ast.AST | t.CPtr] | t.CPtr = ags_p.args + idx_p: int = 0 + apn: t.CSizeT = len(alist_p) + for api in range(apn): + arg_p: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_p.get(api)) + if arg_p is None or arg_p.arg is None: + continue + if arg_p.annotation is not None: + if self.Gen.TypeGenResolve._is_t_attr(arg_p.annotation, "CVoid", imported_modules) != 0: + continue + if idx_p >= param_count: + break + pty2_n: llvmlite.LLVMType | t.CPtr = i32_ty + if param_tys is not None: + ty_addr_n: t.CUInt64T = t.CUInt64T(param_tys) + idx_p * 8 + ty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr_n) + pty2_n = (llvmlite.LLVMType | t.CPtr)(ty_slot_n[0]) + if pty2_n is None: + pty2_n = i32_ty + p_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, pty2_n) + if p_alloca is not None: + self.Gen.VarGen.define_var(trans.SymTab, arg_p.arg, p_alloca) + if arg_p.annotation is not None: + cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation( + arg_p.annotation, imported_modules, pool, trans._from_imports) + if cls_nm_p is not None: + self.Gen.VarGen.set_var_annot_class_name( + trans.SymTab, arg_p.arg, cls_nm_p) + pname2_n: str = pool.alloc(32) + if pname2_n is not None: + viperlib.snprintf(pname2_n, 32, "%%%s", arg_p.arg) + p_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, pty2_n, pname2_n) + llvmlite.build_store(func_builder, p_val, p_alloca) + idx_p += 1 + + # 6. 保存父函数作用域状态(仅非变量表相关) + old_func: llvmlite.Function | t.CPtr = trans._cur_func + old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + old_func_name: str = trans._cur_func_name + old_global_count: int = trans._global_name_count + old_nonlocal_count: int = trans._nonlocal_name_count + old_env_count: int = trans._closure_env_count + # nonlocal 集合独立缓冲:嵌套函数注册 nonlocal 写入共享缓冲 [0] 会 + # 覆盖父函数集合(ClosureTest2 Test 7 不同变量场景:level2 的 b 覆盖 + # level1 的 a → level1 env[0] 存错)。嵌套函数用独立缓冲,翻译完恢复。 + saved_nl_names: t.CVoid | t.CPtr = trans._nonlocal_names + saved_nl_types: t.CVoid | t.CPtr = trans._nonlocal_types + + # 7. 切换到嵌套函数作用域 + 清空 scope names + trans._cur_func = func + trans._cur_builder = func_builder + trans._cur_func_name = func_name + HT.clear_scope_names(trans) + # 嵌套函数独立 nonlocal 缓冲(避免覆盖父函数集合) + trans._nonlocal_names = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8) + trans._nonlocal_types = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8) + if trans._nonlocal_names is not None: + string.memset(trans._nonlocal_names, 0, HT.MAX_GLOBAL_NAMES * 8) + if trans._nonlocal_types is not None: + string.memset(trans._nonlocal_types, 0, HT.MAX_GLOBAL_NAMES * 8) + + # 8. 预扫描函数体: + # a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效) + # b. 再预扫描 AnnAssign 为局部变量提前创建 alloca + body: list[ast.AST | t.CPtr] | t.CPtr = fd.children + if body is not None: + bn: t.CSizeT = len(body) + for bi in range(bn): + stmt: ast.AST | t.CPtr = body.get(bi) + if stmt is not None: + self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt) + for bi in range(bn): + stmt: ast.AST | t.CPtr = body.get(bi) + if stmt is not None: + self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt) + + # 8c. 隐式捕获扫描:嵌套函数读取的外层函数局部变量注册为 nonlocal + # (Python 语义,无需显式 nonlocal;否则 read 经 lookup_var + # 跨函数命中父函数 alloca → SSA 编号错乱 → llc undefined value)。 + # 分支内 AnnAssign 局部已由 pre_scan_allocas 递归预注册 + # (StmtGenDeclMixin),本扫描经 lookup_current 自然排除。 + if body is not None: + bnc: t.CSizeT = len(body) + for bic in range(bnc): + stmtc: ast.AST | t.CPtr = body.get(bic) + if stmtc is not None: + self._closure_scan_captures(trans, stmtc) + + # 9. 翻译函数体(Nonlocal 语句会填充 _nonlocal_names) + if body is not None: + bn2: t.CSizeT = len(body) + for bi2 in range(bn2): + stmt2: ast.AST | t.CPtr = body.get(bi2) + if stmt2 is not None: + self.Gen.StmtGen.translate_stmt(trans, stmt2) + + # 10. 隐式 ret + last_is_return: int = 0 + if body is not None: + bn3: t.CSizeT = len(body) + if bn3 > 0: + last_stmt: ast.AST | t.CPtr = body.get(bn3 - 1) + if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return: + last_is_return = 1 + if last_is_return == 0: + zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + llvmlite.build_ret(func_builder, zero_val) + + # 11. 保存嵌套函数的 nonlocal 信息 + nested_nonlocal_count: int = trans._nonlocal_name_count + + # 12. 退出嵌套函数作用域,回到父函数作用域(保留 _nonlocal_names 用于 env 创建) + self.Gen.VarGen.exit_scope(trans.SymTab) + trans._cur_func = old_func + trans._cur_builder = old_builder + # NOTE: _nonlocal_names 和 _nonlocal_name_count 仍为嵌套函数的值 + + # 13. 在父函数中创建闭包对象 + parent_builder: llvmlite.IRBuilder | t.CPtr = old_builder + if parent_builder is None: + # 无 builder(模块级嵌套函数?)→ 无法创建闭包,仅恢复状态 + trans._cur_func_name = old_func_name + trans._global_name_count = old_global_count + trans._nonlocal_name_count = old_nonlocal_count + trans._closure_env_count = old_env_count + trans._nonlocal_names = saved_nl_names + trans._nonlocal_types = saved_nl_types + return 0 + + # 13a. 创建 env: malloc(8 * nested_nonlocal_count) — env 存储指针数组 + # 每个 nonlocal 变量占 8 字节(i8*),指向父函数的 alloca(共享存储) + env_ptr: llvmlite.Value | t.CPtr = None + if nested_nonlocal_count > 0: + env_size: t.CInt64T = nested_nonlocal_count * 8 + env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size) + if env_size_val is not None: + env_size_val.Next = None + env_ptr = llvmlite.build_call( + parent_builder, "malloc", env_size_val, 1, i8_ptr_ty, 0) + + # 为每个 nonlocal 变量建立堆存储并注册到 env: + # 运行时 env 链式共享(无需编译期映射——映射存编译期 Value 对象 + # 地址会被 inttoptr 当运行时堆地址解引用崩溃;存整型也拿不到 + # 运行时 malloc 结果): + # - 变量是父函数局部(lookup_current 在当前作用域找到 alloca) + # → 新建堆(malloc + 拷贝当前值),并把父函数 VarEntry.Alloca + # 重定向到堆——父函数后续读写走共享堆 + # - 变量是父函数的 nonlocal(多层嵌套,当前作用域无 alloca) + # → 从父函数 env 读该变量的堆地址(_load_raw_var_ptr), + # env[ni] = 同一堆地址(运行时链式共享,各层写同一堆) + if env_ptr is not None: + i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) + for ni in range(nested_nonlocal_count): + # 从 _nonlocal_names 缓冲区读取名称 + name_addr: t.CUInt64T = t.CUInt64T(trans._nonlocal_names) + ni * 8 + nl_slot: str | t.CPtr = (t.CVoid | t.CPtr)(name_addr) + nl_name: str = nl_slot[0] + if nl_name is None: + continue + heap_store: llvmlite.Value | t.CPtr = None + # 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为 + # 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获 + # 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享) + gname_ck: str = self._nl_heap_global_name( + pool, trans, nl_name) + if gname_ck is not None \ + and self._nl_heap_global_exists(mod, gname_ck) != 0: + gv_ref0: llvmlite.Value | t.CPtr = self._nl_heap_global_ref( + pool, trans, nl_name) + if gv_ref0 is not None: + heap_store = llvmlite.build_load( + parent_builder, i8_ptr_ty, gv_ref0) + if heap_store is None: + local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current( + trans.SymTab, nl_name) + if local_alloca is not None and local_alloca.Ty is not None: + # 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向 + nl_load_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee + if nl_load_ty is not None: + cur_val_nl: llvmlite.Value | t.CPtr = llvmlite.build_load( + parent_builder, nl_load_ty, local_alloca) + # 堆存储大小按类型字节数:数组(如 [4 x i32]=16 字节) + # 不能固定 8 字节(越界写崩溃);指针/标量 ≥ 8 字节 + st_size: t.CInt = self._type_byte_size(pool, nl_load_ty) + st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64( + pool, st_size) + if st_size_val is not None: + st_size_val.Next = None + var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( + parent_builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) + if var_storage is not None: + # 拷贝当前值到堆存储 + st_slot_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty)) + if st_slot_nl is not None and cur_val_nl is not None: + llvmlite.build_store(parent_builder, cur_val_nl, st_slot_nl) + # 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆) + ve_nl: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( + trans.SymTab, nl_name) + heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty)) + if ve_nl is not None and heap_typed is not None: + ve_nl.Alloca = heap_typed + # 记录到全局映射(后续层/父函数可见) + gv_ref_new: llvmlite.Value | t.CPtr = None + if gname_ck is not None \ + and self._nl_heap_global_exists(mod, gname_ck) == 0: + gv_new: llvmlite.GlobalVariable | t.CPtr = \ + llvmlite.new_global_variable(pool, gname_ck, i8_ptr_ty) + if gv_new is not None: + # 清除 external linkage + 零初始化(防 undefined reference) + gv_new.Linkage = None + gv_new.Initializer = "null" + llvmlite.module_add_global(mod, gv_new) + gv_ref_new = self._nl_heap_global_ref(pool, trans, nl_name) + if gv_ref_new is not None: + llvmlite.build_store( + parent_builder, var_storage, gv_ref_new) + heap_store = var_storage + else: + # 父函数中该变量是 nonlocal(多层嵌套):从父函数 env + # 读堆地址(运行时链式共享同一堆),不新建 + heap_store = self.Gen.StmtGenDecl._load_raw_var_ptr(trans, nl_name) + if heap_store is None: + continue + # env[ni] = 堆存储地址(共享) + offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ni * 8) + slot: llvmlite.Value | t.CPtr = llvmlite.build_gep( + parent_builder, i8_ty, env_ptr, offset_val) + if slot is not None: + slot_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, slot, i8_ptr_ptr_ty) + if slot_typed is not None: + llvmlite.build_store(parent_builder, heap_store, slot_typed) + + # 13b. 创建闭包结构: malloc(16) + closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16) + if closure_size_val is not None: + closure_size_val.Next = None + closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call( + parent_builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0) + if closure_ptr is None: + # malloc 失败,恢复状态 + trans._cur_func_name = old_func_name + trans._global_name_count = old_global_count + trans._nonlocal_name_count = old_nonlocal_count + trans._closure_env_count = old_env_count + trans._nonlocal_names = saved_nl_names + trans._nonlocal_types = saved_nl_types + return 0 + + # 13c. 存储 fn_ptr 到 offset 0 + # 创建函数指针类型 i32(i8*)* + fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) + fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1) + fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty) + + # 创建函数引用 Value(类型为 i32(i8*)*,使用 SHA1 混淆名) + fn_ptr_name: str = pool.alloc(64) + if fn_ptr_name is not None: + # SHA1 前缀名含 '.' 需要加引号(如 @"sha1.__closure_func") + if string.strchr(mangled_promoted, '.') is not None: + viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted) + else: + viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted) + fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name) + + # bitcast 函数指针到 i8*(闭包存储 i8* 类型) + fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, fn_ptr_val, i8_ptr_ty) + fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty)) + if fn_slot is not None and fn_as_i8ptr is not None: + llvmlite.build_store(parent_builder, fn_as_i8ptr, fn_slot) + + # 13d. 存储 env_ptr 到 offset 8 + eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8) + env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( + parent_builder, i8_ty, closure_ptr, eight_val) + if env_slot_addr is not None: + env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + parent_builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty)) + if env_slot is not None: + if env_ptr is not None: + llvmlite.build_store(parent_builder, env_ptr, env_slot) + else: + # 无 nonlocal 变量,存储 null + null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null") + llvmlite.build_store(parent_builder, null_val, env_slot) + + # 13e. 将闭包指针存储到父函数的局部变量 {func_name} + closure_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(parent_builder, i8_ptr_ty) + if closure_alloca is not None: + llvmlite.build_store(parent_builder, closure_ptr, closure_alloca) + self.Gen.VarGen.define_var(trans.SymTab, func_name, closure_alloca) + # 注册闭包变量名(供调用时区分闭包 vs 函数指针变量, + # 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分) + self.Gen.CallGenLeafKit.register_closure_name(func_name) + + # 14. 恢复 scope names(含 nonlocal 集合指针) + trans._cur_func_name = old_func_name + trans._global_name_count = old_global_count + trans._nonlocal_name_count = old_nonlocal_count + trans._closure_env_count = old_env_count + trans._nonlocal_names = saved_nl_names + trans._nonlocal_types = saved_nl_types + + return 1 + + # ============================================================ + # _closure_scan_captures - 递归扫描嵌套函数体中的隐式捕获变量 + # + # Python 语义:嵌套函数读取外层函数局部变量无需显式 nonlocal。 + # 此前嵌套 def 闭包仅在显式 `nonlocal` 语句时注册捕获;无 nonlocal + # 的 read 在翻译时会经 lookup_var 跨函数命中父函数 alloca(SSA 编号 + # 属于父函数,提升函数内引用 → llc "use of undefined value '%X'")。 + # lambda 已有 _lambda_scan_body 预扫描,这里对嵌套 def 做等价预扫描: + # 把"读取的外层函数局部变量"注册为 nonlocal(复用 env 共享堆机制), + # 使 read 走 _load_raw_var_ptr 从 env 读堆值。 + # 注意:与 lambda 快照方案不同,嵌套 def 复用显式 nonlocal 的 + # env 共享堆路径,因此注册顺序必须与 step 13 的 env 构建一致 + # (本扫描在 body 翻译前执行,注册顺序稳定)。 + # ============================================================ + def _closure_scan_captures(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr) -> int: + """递归扫描节点中的隐式捕获并注册为 nonlocal""" + if node is None or trans is None: + return 1 + k: int = node.kind() + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is None: + return 1 + # 当前作用域已定义(参数 / AnnAssign 局部,含分支内)→ 非捕获 + if self.Gen.VarGen.lookup_current(trans.SymTab, nm.id) is not None: + return 1 + # 已注册 nonlocal(显式 nonlocal 语句 / 已捕获)→ 跳过 + if HT.is_nonlocal_name(trans, nm.id) != 0: + return 1 + ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( + trans.SymTab, nm.id) + if ve is None or ve.Alloca is None or ve.Alloca.Ty is None: + return 1 + nl_ty: llvmlite.LLVMType | t.CPtr = ve.Alloca.Ty.Pointee + if nl_ty is None: + return 1 + # 模块级全局直接解析,无需 env 捕获 + mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var( + trans.SymTab, nm.id) + if mod_gv is not None: + if t.CUInt64T(mod_gv) == t.CUInt64T(ve.Alloca): + return 1 + # 外层函数局部变量 → 隐式捕获(注册为 nonlocal) + HT.add_nonlocal_name(trans, nm.id, nl_ty) + return 1 + # ---- 语句 ---- + if k == ast.ASTKind.Assign: + asn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node) + if asn.value is not None: + self._closure_scan_captures(trans, asn.value) + return 1 + if k == ast.ASTKind.AnnAssign: + aan: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) + if aan.value is not None: + self._closure_scan_captures(trans, aan.value) + return 1 + if k == ast.ASTKind.AugAssign: + aun: ast.AugAssign | t.CPtr = (ast.AugAssign | t.CPtr)(node) + # 目标同时是读(先读旧值),参与捕获 + if aun.target is not None: + self._closure_scan_captures(trans, aun.target) + if aun.value is not None: + self._closure_scan_captures(trans, aun.value) + return 1 + if k == ast.ASTKind.If: + ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node) + if ifn.test is not None: + self._closure_scan_captures(trans, ifn.test) + if ifn.children is not None: + bn_if: t.CSizeT = len(ifn.children) + for bi_if in range(bn_if): + self._closure_scan_captures(trans, ifn.children.get(bi_if)) + if ifn.orelse is not None: + bn_el: t.CSizeT = len(ifn.orelse) + for bi_el in range(bn_el): + self._closure_scan_captures(trans, ifn.orelse.get(bi_el)) + return 1 + if k == ast.ASTKind.For: + for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node) + # 循环目标是新局部变量,不参与捕获 + if for_n.iter is not None: + self._closure_scan_captures(trans, for_n.iter) + if for_n.children is not None: + bn_f: t.CSizeT = len(for_n.children) + for bi_f in range(bn_f): + self._closure_scan_captures(trans, for_n.children.get(bi_f)) + if for_n.orelse is not None: + bn_fe: t.CSizeT = len(for_n.orelse) + for bi_fe in range(bn_fe): + self._closure_scan_captures(trans, for_n.orelse.get(bi_fe)) + return 1 + if k == ast.ASTKind.While: + wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node) + if wh_n.test is not None: + self._closure_scan_captures(trans, wh_n.test) + if wh_n.children is not None: + bn_w: t.CSizeT = len(wh_n.children) + for bi_w in range(bn_w): + self._closure_scan_captures(trans, wh_n.children.get(bi_w)) + if wh_n.orelse is not None: + bn_we: t.CSizeT = len(wh_n.orelse) + for bi_we in range(bn_we): + self._closure_scan_captures(trans, wh_n.orelse.get(bi_we)) + return 1 + if k == ast.ASTKind.Return: + rt_n: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node) + if rt_n.value is not None: + self._closure_scan_captures(trans, rt_n.value) + return 1 + if k == ast.ASTKind.Expr: + ex_n: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node) + if ex_n.value is not None: + self._closure_scan_captures(trans, ex_n.value) + return 1 + if k == ast.ASTKind.With: + wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node) + if wth.items is not None: + bn_wi: t.CSizeT = len(wth.items) + for bi_wi in range(bn_wi): + wi_n: ast.AST | t.CPtr = wth.items.get(bi_wi) + if wi_n is not None and wi_n.kind() == ast.ASTKind.WithItem: + wi_c: ast.WithItem | t.CPtr = (ast.WithItem | t.CPtr)(wi_n) + if wi_c.context_expr is not None: + self._closure_scan_captures(trans, wi_c.context_expr) + if wth.children is not None: + bn_wb: t.CSizeT = len(wth.children) + for bi_wb in range(bn_wb): + self._closure_scan_captures(trans, wth.children.get(bi_wb)) + return 1 + if k == ast.ASTKind.Raise: + rs_n: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(node) + if rs_n.exc is not None: + self._closure_scan_captures(trans, rs_n.exc) + if rs_n.cause is not None: + self._closure_scan_captures(trans, rs_n.cause) + return 1 + if k == ast.ASTKind.Assert: + as_n: ast.Assert | t.CPtr = (ast.Assert | t.CPtr)(node) + if as_n.test is not None: + self._closure_scan_captures(trans, as_n.test) + if as_n.msg is not None: + self._closure_scan_captures(trans, as_n.msg) + return 1 + if k == ast.ASTKind.Try: + tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node) + if tr_n.children is not None: + bn_t: t.CSizeT = len(tr_n.children) + for bi_t in range(bn_t): + self._closure_scan_captures(trans, tr_n.children.get(bi_t)) + if tr_n.handlers is not None: + bn_th: t.CSizeT = len(tr_n.handlers) + for bi_th in range(bn_th): + eh_n: ast.AST | t.CPtr = tr_n.handlers.get(bi_th) + if eh_n is not None and eh_n.kind() == ast.ASTKind.ExceptHandler: + eh_c: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_n) + if eh_c.children is not None: + bn_ehb: t.CSizeT = len(eh_c.children) + for bi_ehb in range(bn_ehb): + self._closure_scan_captures(trans, eh_c.children.get(bi_ehb)) + if tr_n.orelse is not None: + bn_to: t.CSizeT = len(tr_n.orelse) + for bi_to in range(bn_to): + self._closure_scan_captures(trans, tr_n.orelse.get(bi_to)) + if tr_n.finalbody is not None: + bn_tf: t.CSizeT = len(tr_n.finalbody) + for bi_tf in range(bn_tf): + self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf)) + return 1 + if k == ast.ASTKind.Match: + mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node) + if mt_n.subject is not None: + self._closure_scan_captures(trans, mt_n.subject) + if mt_n.cases is not None: + bn_mc: t.CSizeT = len(mt_n.cases) + for bi_mc in range(bn_mc): + mc_n: ast.AST | t.CPtr = mt_n.cases.get(bi_mc) + if mc_n is not None and mc_n.kind() == ast.ASTKind.MatchCase: + mc_c: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_n) + if mc_c.guard is not None: + self._closure_scan_captures(trans, mc_c.guard) + if mc_c.children is not None: + bn_mcb: t.CSizeT = len(mc_c.children) + for bi_mcb in range(bn_mcb): + self._closure_scan_captures(trans, mc_c.children.get(bi_mcb)) + return 1 + # ---- 表达式 ---- + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + if bop.left is not None: + self._closure_scan_captures(trans, bop.left) + if bop.right is not None: + self._closure_scan_captures(trans, bop.right) + return 1 + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + if uop.operand is not None: + self._closure_scan_captures(trans, uop.operand) + return 1 + if k == ast.ASTKind.BoolOp: + bop2: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node) + if bop2.values is not None: + bn_bo: t.CSizeT = len(bop2.values) + for bi_bo in range(bn_bo): + self._closure_scan_captures(trans, bop2.values.get(bi_bo)) + return 1 + if k == ast.ASTKind.Compare: + cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node) + if cmp.left is not None: + self._closure_scan_captures(trans, cmp.left) + if cmp.comparators is not None: + bn_cmp: t.CSizeT = len(cmp.comparators) + for bi_cmp in range(bn_cmp): + self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp)) + return 1 + if k == ast.ASTKind.Call: + cal: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cal.func is not None: + self._closure_scan_captures(trans, cal.func) + if cal.args is not None: + bn_ca: t.CSizeT = len(cal.args) + for bi_ca in range(bn_ca): + self._closure_scan_captures(trans, cal.args.get(bi_ca)) + if cal.keywords is not None: + bn_ck: t.CSizeT = len(cal.keywords) + for bi_ck in range(bn_ck): + kw_n: ast.AST | t.CPtr = cal.keywords.get(bi_ck) + if kw_n is not None and kw_n.kind() == ast.ASTKind.Keyword: + kw_c: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kw_n) + if kw_c.value is not None: + self._closure_scan_captures(trans, kw_c.value) + return 1 + if k == ast.ASTKind.Subscript: + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + if sub.value is not None: + self._closure_scan_captures(trans, sub.value) + if sub.slice is not None: + self._closure_scan_captures(trans, sub.slice) + return 1 + if k == ast.ASTKind.Attribute: + at_n: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at_n.value is not None: + self._closure_scan_captures(trans, at_n.value) + return 1 + if k == ast.ASTKind.IfExp: + ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node) + if ie.test is not None: + self._closure_scan_captures(trans, ie.test) + if ie.body is not None: + self._closure_scan_captures(trans, ie.body) + if ie.orelse is not None: + self._closure_scan_captures(trans, ie.orelse) + return 1 + if k == ast.ASTKind.Dict: + dc: ast.Dict | t.CPtr = (ast.Dict | t.CPtr)(node) + if dc.keys is not None: + bn_dk: t.CSizeT = len(dc.keys) + for bi_dk in range(bn_dk): + self._closure_scan_captures(trans, dc.keys.get(bi_dk)) + if dc.values is not None: + bn_dv: t.CSizeT = len(dc.values) + for bi_dv in range(bn_dv): + self._closure_scan_captures(trans, dc.values.get(bi_dv)) + return 1 + if k == ast.ASTKind.Set: + st_n: ast.Set | t.CPtr = (ast.Set | t.CPtr)(node) + if st_n.elts is not None: + bn_set: t.CSizeT = len(st_n.elts) + for bi_set in range(bn_set): + self._closure_scan_captures(trans, st_n.elts.get(bi_set)) + return 1 + if k == ast.ASTKind.List: + ls_n: ast.List | t.CPtr = (ast.List | t.CPtr)(node) + if ls_n.elts is not None: + bn_ls: t.CSizeT = len(ls_n.elts) + for bi_ls in range(bn_ls): + self._closure_scan_captures(trans, ls_n.elts.get(bi_ls)) + return 1 + if k == ast.ASTKind.Tuple: + tp_n: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node) + if tp_n.elts is not None: + bn_tp: t.CSizeT = len(tp_n.elts) + for bi_tp in range(bn_tp): + self._closure_scan_captures(trans, tp_n.elts.get(bi_tp)) + return 1 + if k == ast.ASTKind.Starred: + sr_n: ast.Starred | t.CPtr = (ast.Starred | t.CPtr)(node) + if sr_n.value is not None: + self._closure_scan_captures(trans, sr_n.value) + return 1 + if k == ast.ASTKind.NamedExpr: + ne_n: ast.NamedExpr | t.CPtr = (ast.NamedExpr | t.CPtr)(node) + if ne_n.value is not None: + self._closure_scan_captures(trans, ne_n.value) + return 1 + if k == ast.ASTKind.Slice: + sl_n: ast.Slice | t.CPtr = (ast.Slice | t.CPtr)(node) + if sl_n.lower is not None: + self._closure_scan_captures(trans, sl_n.lower) + if sl_n.upper is not None: + self._closure_scan_captures(trans, sl_n.upper) + if sl_n.step is not None: + self._closure_scan_captures(trans, sl_n.step) + return 1 + if k == ast.ASTKind.JoinedStr: + js_n: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node) + if js_n.values is not None: + bn_js: t.CSizeT = len(js_n.values) + for bi_js in range(bn_js): + self._closure_scan_captures(trans, js_n.values.get(bi_js)) + return 1 + if k == ast.ASTKind.FormattedValue: + fv_n: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node) + if fv_n.value is not None: + self._closure_scan_captures(trans, fv_n.value) + if fv_n.format_spec is not None: + self._closure_scan_captures(trans, fv_n.format_spec) + return 1 + if k == ast.ASTKind.Await: + aw_n: ast.Await | t.CPtr = (ast.Await | t.CPtr)(node) + if aw_n.value is not None: + self._closure_scan_captures(trans, aw_n.value) + return 1 + if k == ast.ASTKind.Yield: + yd_n: ast.Yield | t.CPtr = (ast.Yield | t.CPtr)(node) + if yd_n.value is not None: + self._closure_scan_captures(trans, yd_n.value) + return 1 + if k == ast.ASTKind.YieldFrom: + yf_n: ast.YieldFrom | t.CPtr = (ast.YieldFrom | t.CPtr)(node) + if yf_n.value is not None: + self._closure_scan_captures(trans, yf_n.value) + return 1 + is_comp: int = 0 + if k == ast.ASTKind.ListComp: + is_comp = 1 + elif k == ast.ASTKind.SetComp: + is_comp = 1 + elif k == ast.ASTKind.GeneratorExp: + is_comp = 1 + if is_comp != 0: + lc_n: ast.ListComp | t.CPtr = (ast.ListComp | t.CPtr)(node) + if lc_n.elt is not None: + self._closure_scan_captures(trans, lc_n.elt) + if lc_n.generators is not None: + bn_gen: t.CSizeT = len(lc_n.generators) + for bi_gen in range(bn_gen): + cp_n: ast.AST | t.CPtr = lc_n.generators.get(bi_gen) + if cp_n is not None and cp_n.kind() == ast.ASTKind.Comprehension: + cp_c: ast.Comprehension | t.CPtr = (ast.Comprehension | t.CPtr)(cp_n) + if cp_c.iter is not None: + self._closure_scan_captures(trans, cp_c.iter) + if cp_c.ifs is not None: + bn_cpi: t.CSizeT = len(cp_c.ifs) + for bi_cpi in range(bn_cpi): + self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi)) + return 1 + if k == ast.ASTKind.DictComp: + dcn: ast.DictComp | t.CPtr = (ast.DictComp | t.CPtr)(node) + if dcn.key is not None: + self._closure_scan_captures(trans, dcn.key) + if dcn.value is not None: + self._closure_scan_captures(trans, dcn.value) + if dcn.generators is not None: + bn_dgen: t.CSizeT = len(dcn.generators) + for bi_dgen in range(bn_dgen): + cp_dn: ast.AST | t.CPtr = dcn.generators.get(bi_dgen) + if cp_dn is not None and cp_dn.kind() == ast.ASTKind.Comprehension: + cp_dc: ast.Comprehension | t.CPtr = (ast.Comprehension | t.CPtr)(cp_dn) + if cp_dc.iter is not None: + self._closure_scan_captures(trans, cp_dc.iter) + if cp_dc.ifs is not None: + bn_dcpi: t.CSizeT = len(cp_dc.ifs) + for bi_dcpi in range(bn_dcpi): + self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi)) + return 1 + return 1 + + + # ============================================================ + # _type_byte_size - 计算 LLVM 类型占用的字节大小(递归) + # + # 用于 nonlocal 共享堆存储分配:数组(如 [4 x i32]=16 字节)不能 + # 用固定 8 字节(越界写破坏 canary/邻块 → 运行时崩溃, + # ClosureTest2 Test 4 非local 数组崩溃根因)。未知类型保守返回 8。 + # ============================================================ + def _type_byte_size(self, pool: memhub.MemBuddy | t.CPtr, + ty: llvmlite.LLVMType | t.CPtr) -> t.CInt: + """计算 LLVM 类型占用的字节大小(递归),未知类型返回 8""" + if ty is None: + return 8 + match ty: + case llvmlite.LLVMType.Int(bits): + sz_i: t.CInt = (bits + 7) // 8 + if sz_i < 8: + sz_i = 8 + return sz_i + case llvmlite.LLVMType.Ptr(_pe): + return 8 + case llvmlite.LLVMType.Float(fb): + if fb == 64: + return 8 + if fb >= 128: + return 16 + return 4 + case llvmlite.LLVMType.Array(et, cnt): + esz: t.CInt = self._type_byte_size(pool, et) + return esz * cnt + case _: + return 8 + + # ============================================================ + # _nl_heap_global_name - nonlocal 堆地址全局映射变量名 + # + # 变量名: nlheap_{sha1}.{name}(全局变量,跨函数可见;SSA 编号是函数 + # 局部的,跨函数传递堆地址只能经全局变量/模块级映射)。 + # ============================================================ + def _nl_heap_global_name(self, pool: memhub.MemBuddy | t.CPtr, + trans: HT.Translator | t.CPtr, + name: str) -> str: + """构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}""" + if pool is None or trans is None or name is None: + return None + mangled: str = self.Gen.FuncGenBase._mangle_name(trans, name) + buf: str = pool.alloc(160) + if buf is not None: + viperlib.snprintf(buf, 160, "nlheap_%s", mangled) + return buf + + # ============================================================ + # _nl_heap_global_ref - 全局映射变量引用 Value(i8**) + # ============================================================ + def _nl_heap_global_ref(self, pool: memhub.MemBuddy | t.CPtr, + trans: HT.Translator | t.CPtr, + name: str) -> llvmlite.Value | t.CPtr: + """获取 nonlocal 堆地址全局映射变量的引用(@"..." 引号包裹)""" + if pool is None or trans is None or name is None: + return None + gname: str = self._nl_heap_global_name(pool, trans, name) + if gname is None: + return None + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + ref_buf: str = pool.alloc(176) + if ref_buf is None: + return None + viperlib.snprintf(ref_buf, 176, "@\"%s\"", gname) + return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, i8_ptr_ty), ref_buf) + + # ============================================================ + # _nl_heap_global_exists - 检查 nonlocal 堆地址全局映射变量是否存在 + # ============================================================ + def _nl_heap_global_exists(self, mod: llvmlite.LLVMModule | t.CPtr, + gname: str) -> int: + """检查全局映射变量 gname 是否已在模块中,1=存在""" + if mod is None or gname is None: + return 0 + gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead + while gcur is not None: + if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0: + return 1 + gcur = gcur.Next + return 0 + + # ============================================================ + # _collect_chain_nonlocals - 递归收集嵌套函数链内 nonlocal 声明 + # + # 遍历 node(FunctionDef)的 children:遇嵌套 FunctionDef 递归, + # 遇 Nonlocal 语句收集变量名(去重)。返回更新后的计数。 + # ============================================================ + def _collect_chain_nonlocals(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr, + names: t.CVoid | t.CPtr, + count: int) -> int: + """递归收集 node(FunctionDef)链内所有 nonlocal 声明名(去重)""" + if node is None or names is None: + return count + fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node) + if fd is None or fd.children is None: + return count + ch: list[ast.AST | t.CPtr] | t.CPtr = fd.children + cn: t.CSizeT = len(ch) + for ci in range(cn): + st: ast.AST | t.CPtr = ch.get(ci) + if st is None: + continue + k: int = st.kind() + if k == ast.ASTKind.FunctionDef: + count = self._collect_chain_nonlocals(trans, st, names, count) + elif k == ast.ASTKind.Nonlocal: + nl_node: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(st) + if nl_node is not None and nl_node.names is not None: + nl_list: list[ast.AST | t.CPtr] | t.CPtr = nl_node.names + nn: t.CSizeT = len(nl_list) + for ni in range(nn): + nm_node: ast.AST | t.CPtr = nl_list.get(ni) + if nm_node is None: + continue + nm_x: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node) + if nm_x is None or nm_x.id is None: + continue + dup: int = 0 + for di in range(count): + if string.strcmp((str)( + t.CUInt64T(names) + di * 8), nm_x.id) == 0: + dup = 1 + break + if dup == 0 and count < 64: + nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(names) + count * 8) + nm_slot[0] = nm_x.id + count += 1 + return count + + # ============================================================ + # _prepare_chain_nonlocals - 预扫描嵌套函数链内 nonlocal 并建堆 + # + # 在翻译嵌套函数定义(translate_nested_function_def)之前调用。 + # 场景(ClosureTest2 Test 7):level1→a、level2→b、level3→c_val 各自 + # nonlocal 不同变量,都定义在最外层函数——level1 env 无 b 槽,level2 + # 无法经 env 链获取 b 的堆。此处在最外层函数中为链内所有 nonlocal + # 建堆 + 重定向 VarEntry + 把堆地址 store 到模块级映射 + # @nlheap.{sha1}.{name}(全局变量跨函数可见),内层闭包创建时从映射 load。 + # ============================================================ + def _prepare_chain_nonlocals(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + i8_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> int: + """预收集嵌套函数链内 nonlocal 并建堆,1 成功""" + if node is None or trans is None or builder is None or mod is None: + return 0 + # 每函数只预建一次:多个嵌套函数定义共享同一堆(防重复 malloc + + # 二次重定向 VarEntry.Alloca 导致兄弟闭包各持一堆,计数不同步) + if trans._nl_chain_done != 0: + return 1 + if trans._nl_chain_names is None: + trans._nl_chain_names = pool.alloc(8 * 64) + if trans._nl_chain_names is not None: + string.memset(trans._nl_chain_names, 0, 8 * 64) + if trans._nl_chain_names is None: + return 0 + trans._nl_chain_count = self._collect_chain_nonlocals( + trans, node, trans._nl_chain_names, 0) + trans._nl_chain_done = 1 + for ci in range(trans._nl_chain_count): + nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(trans._nl_chain_names) + ci * 8) + nl_name: str = nm_slot[0] + if nl_name is None: + continue + # 只在"定义它的函数"(当前顶层作用域有 alloca)中建堆 + local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current( + trans.SymTab, nl_name) + if local_alloca is None or local_alloca.Ty is None: + continue + nl_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee + if nl_ty is None: + continue + gname: str = self._nl_heap_global_name(pool, trans, nl_name) + if gname is None: + continue + # 建堆(按类型字节大小)并拷贝当前值——每次本函数执行都新建共享堆, + # 并 store 到模块级映射(跨函数可见)。注意:不能因映射变量已存在 + # 就跳过(前一个测试函数已把 @nlheap.* 加入模块——若跳过,本函数 + # 运行时 @nlheap.* 保持上一函数的堆地址,env 读到错堆。 + # ClosureTest2 Test 7 根因)。映射变量仅首次 add(模块去重), + # store 指令每次照发。 + st_size: t.CInt = self._type_byte_size(pool, nl_ty) + st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size) + if st_size_val is not None: + st_size_val.Next = None + var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) + if var_storage is None: + continue + cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, nl_ty, local_alloca) + st_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, var_storage, llvmlite.Ptr(pool, nl_ty)) + if st_slot is not None and cur_val is not None: + llvmlite.build_store(builder, cur_val, st_slot) + # 重定向 VarEntry(父函数读写走共享堆) + ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( + trans.SymTab, nl_name) + if ve is not None: + heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, var_storage, llvmlite.Ptr(pool, nl_ty)) + if heap_typed is not None: + ve.Alloca = heap_typed + # 创建/复用全局映射变量并 store 堆地址(跨函数可见) + gv_found: llvmlite.GlobalVariable | t.CPtr = None + gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead + while gcur is not None: + if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0: + gv_found = gcur + break + gcur = gcur.Next + if gv_found is None: + gv_found = llvmlite.new_global_variable(pool, gname, i8_ptr_ty) + if gv_found is not None: + # 清除 external linkage 并给零初始化,否则打印为 external + # 声明、store 引用无定义体 → 链接 undefined reference + gv_found.Linkage = None + gv_found.Initializer = "null" + llvmlite.module_add_global(mod, gv_found) + if gv_found is not None: + gv_ref: llvmlite.Value | t.CPtr = self._nl_heap_global_ref( + pool, trans, nl_name) + if gv_ref is not None: + llvmlite.build_store(builder, var_storage, gv_ref) + return 1 + + # ============================================================ + # translate_lambda - 翻译 lambda 表达式 + # + # 与 translate_nested_function_def 同架构(TPV 闭包 {i8* fn_ptr, + # i8* env_ptr},16 字节): + # 1. 提升函数 __lambda_{id}(i8* %env, [args...]) — 恒返回 i32 + # 2. 捕获变量按值拷贝到堆存储(env[i] = 堆地址),避免闭包返回 + # 后父栈帧销毁导致悬垂 + # 3. 创建闭包 {fn_ptr, env_ptr},返回 i8* 闭包指针 + # 赋值语句据此创建 i8** alloca,调用走 translate_closure_call + # ============================================================ + def translate_lambda(self, builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """翻译 lambda 表达式,返回闭包指针(i8*)""" + if node is None or trans is None or builder is None: + return None + lam: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node) + if lam is None: + return None + + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) + + # 1. lambda id + 提升函数名 + # 注意:计数器放 trans(_lambda_count)而非 self——Mixin 类自身无字段, + # self.新字段在 TPC 编译 Mixin 时查 class_members 失败触发未完成的 + # _PreRegisterClassMembers fallback 崩溃(FuncGen.py L437) + lambda_id: int = trans._lambda_count + trans._lambda_count += 1 + pname_buf: str = pool.alloc(64) + if pname_buf is None: + return None + viperlib.snprintf(pname_buf, 64, "__lambda_%d", lambda_id) + mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, pname_buf) + + # 2. 解析参数(名称 + 类型,类型默认 i32,有注解则解析) + param_names: str = pool.alloc(32 * 64) # 64 参数 × 32 字节名 + if param_names is None: + return None + string.memset(param_names, 0, 32 * 64) + param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # LLVMType 指针槽 + if param_tys is None: + return None + string.memset(param_tys, 0, 8 * 64) + param_count: int = 0 + if lam.args is not None: + ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(lam.args) + if ags.args is not None: + alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args + pn: t.CSizeT = len(alist) + for pi in range(pn): + if param_count >= 64: + break + arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(pi)) + if arg_n is None or arg_n.arg is None: + continue + string.strncpy(param_names + param_count * 32, arg_n.arg, 31) + p_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if arg_n.annotation is not None: + resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type( + pool, arg_n.annotation, trans._imported_modules, trans._from_imports, trans, 0) + if resolved is None: + # fail-fast:lambda 参数有注解但类型解析失败,不再静默保持 i32 + self.Gen.TypeGenErr.fatal_error( + arg_n, f"参数 '{arg_n.arg}' 类型解析失败: {self.Gen.TypeGenResolve.annotation_to_str(arg_n.annotation)}", + "NameError") + resolved = i32_ty # 不可达(fatal_error 已退出) + if resolved is not None: + match resolved: + case llvmlite.LLVMType.Struct(_, _, _): + p_ty = llvmlite.Ptr(pool, resolved) + case llvmlite.LLVMType.Array(aelem_ty_l, _): + p_ty = llvmlite.Ptr(pool, aelem_ty_l) + case _: + p_ty = resolved + ty_slot_p: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(param_tys) + param_count * 8) + ty_slot_p[0] = p_ty + param_count += 1 + + # 3. 收集捕获变量(body 中的外部 Name 引用) + cap_names: str = pool.alloc(64 * 64) # 64 捕获 × 64 字节名 + if cap_names is None: + return None + string.memset(cap_names, 0, 64 * 64) + cap_allocas: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 父函数 alloca 指针槽 + if cap_allocas is None: + return None + string.memset(cap_allocas, 0, 8 * 64) + cap_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 捕获变量类型槽 + if cap_tys is None: + return None + string.memset(cap_tys, 0, 8 * 64) + cap_count: int = self._lambda_scan_body( + trans, lam.body, param_names, param_count, + cap_names, cap_allocas, cap_tys, 0) + + # 4. 创建提升函数 __lambda_{id}(i8* %env, [args...]) + func: llvmlite.Function | t.CPtr = llvmlite.create_function( + pool, mod, mangled_promoted, i32_ty) + if func is None: + return None + llvmlite.add_param(pool, func, i8_ptr_ty, "%env") + for pi2 in range(param_count): + ty_addr2: t.CUInt64T = t.CUInt64T(param_tys) + pi2 * 8 + ty_slot2: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr2) + p_ty2: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot2[0]) + if p_ty2 is None: + p_ty2 = i32_ty + pname_l: str = pool.alloc(32) + if pname_l is not None: + viperlib.snprintf(pname_l, 32, "%%%s", param_names + pi2 * 32) + llvmlite.add_param(pool, func, p_ty2, pname_l) + + # 5. entry 块 + builder + entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry") + if entry_blk is None: + return None + func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func) + if func_builder is None: + return None + llvmlite.position_at_end(func_builder, entry_blk) + + # 6. 进入 lambda 作用域 + self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION) + + # 7. _env_ptr alloca(存储 %env 参数) + env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty) + if env_alloca is not None: + self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca) + env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env") + llvmlite.build_store(func_builder, env_param_val, env_alloca) + + # 8. 保存父函数状态并切换到提升函数 + old_func: llvmlite.Function | t.CPtr = trans._cur_func + old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + old_func_name: str = trans._cur_func_name + old_global_count: int = trans._global_name_count + old_nonlocal_count: int = trans._nonlocal_name_count + old_env_count: int = trans._closure_env_count + trans._cur_func = func + trans._cur_builder = func_builder + trans._cur_func_name = mangled_promoted + HT.clear_scope_names(trans) + + # 9. 捕获变量:从 env 堆槽加载值到局部 alloca + for ci in range(cap_count): + # 直接 str 指针偏移取捕获名(与 param_names + pi*32 一致)。 + # 注意:不能用 (str)(cap_addr) 整数转换——TPV 将其编译为 itoa(整数转字符串), + # 会把 cap_slot 变成数字串,define_var 键名错乱,lambda body lookup 无法命中。 + cap_slot: str = cap_names + ci * 64 + if cap_slot[0] == '\0': + continue + ty_addr3: t.CUInt64T = t.CUInt64T(cap_tys) + ci * 8 + ty_slot3: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr3) + cap_ty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot3[0]) + if cap_ty is None: + continue + off_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci * 8) + slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( + func_builder, i8_ty, env_param_val, off_val) + if slot_addr is None: + continue + storage_addr: llvmlite.Value | t.CPtr = llvmlite.build_load( + func_builder, i8_ptr_ty, slot_addr) + if storage_addr is None: + continue + st_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + func_builder, storage_addr, llvmlite.Ptr(pool, cap_ty)) + if st_typed is None: + continue + cap_val: llvmlite.Value | t.CPtr = llvmlite.build_load( + func_builder, cap_ty, st_typed) + if cap_val is None: + continue + var_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, cap_ty) + if var_alloca is not None: + llvmlite.build_store(func_builder, cap_val, var_alloca) + self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca) + + # 10. 参数 alloca + for pi3 in range(param_count): + ty_addr4: t.CUInt64T = t.CUInt64T(param_tys) + pi3 * 8 + ty_slot4: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr4) + p_ty3: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot4[0]) + if p_ty3 is None: + # fail-fast:lambda 参数类型槽缺失(内部状态不一致),不再静默保持 i32 + self.Gen.TypeGenErr.fatal_error( + lam, f"lambda 参数 '{param_names + pi3 * 32}' 类型信息缺失", "InternalCompilerError") + p_ty3 = i32_ty # 不可达(fatal_error 已退出) + pname_ref: str = pool.alloc(32) + if pname_ref is not None: + viperlib.snprintf(pname_ref, 32, "%%%s", param_names + pi3 * 32) + param_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, p_ty3, pname_ref) + arg_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, p_ty3) + if arg_alloca is not None: + llvmlite.build_store(func_builder, param_ref, arg_alloca) + self.Gen.VarGen.define_var(trans.SymTab, param_names + pi3 * 32, arg_alloca) + + # 11. 翻译 body 表达式,coerce 到 i32 后 ret + body_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + func_builder, pool, mod, lam.body, None, 0, trans) + if body_val is not None and body_val.Ty is not None: + if self.Gen.ExprGenBase.get_llvm_type_bits(body_val.Ty) == 32 \ + and self.Gen.ExprGenBase.is_ptr_type(body_val.Ty) == 0: + llvmlite.build_ret(func_builder, body_val) + else: + ret_val: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type( + func_builder, body_val, i32_ty, lam.body) + if ret_val is not None: + llvmlite.build_ret(func_builder, ret_val) + else: + # fail-fast:lambda 体类型转换失败,不再静默 ret 0 + self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError") + else: + # fail-fast:lambda 体翻译失败,不再静默 ret 0 + self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError") + + # 12. 退出 lambda 作用域,恢复父函数状态 + self.Gen.VarGen.exit_scope(trans.SymTab) + trans._cur_func = old_func + trans._cur_builder = old_builder + trans._cur_func_name = old_func_name + trans._global_name_count = old_global_count + trans._nonlocal_name_count = old_nonlocal_count + trans._closure_env_count = old_env_count + + # 13. 在父函数中创建 env(每个捕获变量 8 字节堆存储,env[i] = 堆地址) + env_ptr: llvmlite.Value | t.CPtr = None + if cap_count > 0: + env_size: t.CInt64T = cap_count * 8 + env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size) + if env_size_val is not None: + env_size_val.Next = None + env_ptr = llvmlite.build_call( + builder, "malloc", env_size_val, 1, i8_ptr_ty, 0) + if env_ptr is not None: + for ci3 in range(cap_count): + # 与步骤 9 一致:str 指针偏移判空,避免整数转换 itoa 语义 + cap_slot3: str = cap_names + ci3 * 64 + if cap_slot3[0] == '\0': + continue + al_addr: t.CUInt64T = t.CUInt64T(cap_allocas) + ci3 * 8 + al_slot: str | t.CPtr = (t.CVoid | t.CPtr)(al_addr) + var_alloca2: llvmlite.Value | t.CPtr = ( + llvmlite.Value | t.CPtr)(al_slot[0]) + if var_alloca2 is None: + continue + ty_addr5: t.CUInt64T = t.CUInt64T(cap_tys) + ci3 * 8 + ty_slot5: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr5) + cap_ty2: llvmlite.LLVMType | t.CPtr = ( + llvmlite.LLVMType | t.CPtr)(ty_slot5[0]) + if cap_ty2 is None: + continue + # 堆存储大小:位宽/8,至少 8 字节(容纳指针/标量) + ty_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cap_ty2) + st_size: int = 8 + if ty_bits > 64: + st_size = ty_bits // 8 + st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size) + if st_size_val is not None: + st_size_val.Next = None + var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, "malloc", st_size_val, 1, i8_ptr_ty, 0) + if var_storage is not None: + cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, cap_ty2, var_alloca2) + if cur_val is not None: + st_slot5: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, var_storage, llvmlite.Ptr(pool, cap_ty2)) + if st_slot5 is not None: + llvmlite.build_store(builder, cur_val, st_slot5) + off_val2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci3 * 8) + slot2: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty, env_ptr, off_val2) + if slot2 is not None: + slot_typed2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, slot2, i8_ptr_ptr_ty) + if slot_typed2 is not None: + llvmlite.build_store(builder, var_storage, slot_typed2) + + # 14. 在父函数中创建闭包 malloc(16) + closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16) + if closure_size_val is not None: + closure_size_val.Next = None + closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0) + if closure_ptr is None: + return None + + # 15. [0] = fn_ptr(函数引用 bitcast 到 i8*) + fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) + fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1) + fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty) + fn_ptr_name: str = pool.alloc(64) + if fn_ptr_name is not None: + if string.strchr(mangled_promoted, '.') is not None: + viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted) + else: + viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted) + fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name) + fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, fn_ptr_val, i8_ptr_ty) + fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty)) + if fn_slot is not None and fn_as_i8ptr is not None: + llvmlite.build_store(builder, fn_as_i8ptr, fn_slot) + + # 16. [8] = env_ptr(无捕获存 null) + eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8) + env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty, closure_ptr, eight_val) + if env_slot_addr is not None: + env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty)) + if env_slot is not None: + if env_ptr is not None: + llvmlite.build_store(builder, env_ptr, env_slot) + else: + null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null") + llvmlite.build_store(builder, null_val, env_slot) + + # 17. 返回闭包指针(i8*) + return closure_ptr + + # ============================================================ + # _lambda_scan_body - 递归扫描 lambda body 中的 Name 引用 + # + # 收集外部变量(排除 lambda 参数、模块级全局、已捕获项), + # 记录到 cap_names/cap_allocas/cap_tys 三个并行缓冲。 + # 返回更新后的捕获数量(递归调用逐层累加)。 + # ============================================================ + def _lambda_scan_body(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr, + param_names: str, + param_count: int, + cap_names: str, + cap_allocas: t.CVoid | t.CPtr, + cap_tys: t.CVoid | t.CPtr, + cap_count: int) -> int: + """递归扫描 lambda body 中的 Name 引用,收集捕获变量""" + if node is None or trans is None: + return cap_count + k: int = node.kind() + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is None: + return cap_count + # 排除 lambda 参数 + for pi in range(param_count): + if string.strcmp(param_names + pi * 32, nm.id) == 0: + return cap_count + # 已捕获跳过 + for ci in range(cap_count): + if string.strcmp(cap_names + ci * 64, nm.id) == 0: + return cap_count + # 查当前作用域链 + alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) + if alloca is None or alloca.Ty is None: + return cap_count + pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee + if pointee is None: + return cap_count + # 跳过模块级全局(body 直接解析全局,无需 env 捕获) + mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) + if mod_gv is not None: + if t.CUInt64T(mod_gv) == t.CUInt64T(alloca): + return cap_count + if cap_count >= 64: + return cap_count + string.strncpy(cap_names + cap_count * 64, nm.id, 63) + al_slot: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(cap_allocas) + cap_count * 8) + al_slot[0] = alloca + ty_slot: str | t.CPtr = (t.CVoid | t.CPtr)( + t.CUInt64T(cap_tys) + cap_count * 8) + ty_slot[0] = pointee + return cap_count + 1 + # 递归遍历常见表达式子节点 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + cc1: int = self._lambda_scan_body(trans, bop.left, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + return self._lambda_scan_body(trans, bop.right, param_names, param_count, + cap_names, cap_allocas, cap_tys, cc1) + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + return self._lambda_scan_body(trans, uop.operand, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + if k == ast.ASTKind.BoolOp: + bop2: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node) + cc2: int = cap_count + if bop2.values is not None: + vs: list[ast.AST | t.CPtr] | t.CPtr = bop2.values + vn: t.CSizeT = len(vs) + for vi in range(vn): + cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count, + cap_names, cap_allocas, cap_tys, cc2) + return cc2 + if k == ast.ASTKind.Compare: + cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node) + cc3: int = self._lambda_scan_body(trans, cmp.left, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + if cmp.comparators is not None: + cl: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators + cn: t.CSizeT = len(cl) + for cvi in range(cn): + cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count, + cap_names, cap_allocas, cap_tys, cc3) + return cc3 + if k == ast.ASTKind.Call: + cal: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + cc4: int = self._lambda_scan_body(trans, cal.func, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + if cal.args is not None: + als: list[ast.AST | t.CPtr] | t.CPtr = cal.args + an: t.CSizeT = len(als) + for ai in range(an): + cc4 = self._lambda_scan_body(trans, als.get(ai), param_names, param_count, + cap_names, cap_allocas, cap_tys, cc4) + if cal.keywords is not None: + kws: list[ast.AST | t.CPtr] | t.CPtr = cal.keywords + kn: t.CSizeT = len(kws) + for ki in range(kn): + kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kws.get(ki)) + if kw is not None and kw.value is not None: + cc4 = self._lambda_scan_body(trans, kw.value, param_names, param_count, + cap_names, cap_allocas, cap_tys, cc4) + return cc4 + if k == ast.ASTKind.Subscript: + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + cc5: int = self._lambda_scan_body(trans, sub.value, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + if sub.slice is not None: + cc5 = self._lambda_scan_body(trans, sub.slice, param_names, param_count, + cap_names, cap_allocas, cap_tys, cc5) + return cc5 + if k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + return self._lambda_scan_body(trans, at.value, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + if k == ast.ASTKind.IfExp: + ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node) + cc6: int = self._lambda_scan_body(trans, ie.test, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + cc6 = self._lambda_scan_body(trans, ie.body, param_names, param_count, + cap_names, cap_allocas, cap_tys, cc6) + return self._lambda_scan_body(trans, ie.orelse, param_names, param_count, + cap_names, cap_allocas, cap_tys, cc6) + if k == ast.ASTKind.List: + ls: ast.List | t.CPtr = (ast.List | t.CPtr)(node) + cc7: int = cap_count + if ls.elts is not None: + es: list[ast.AST | t.CPtr] | t.CPtr = ls.elts + en: t.CSizeT = len(es) + for ei in range(en): + cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count, + cap_names, cap_allocas, cap_tys, cc7) + return cc7 + if k == ast.ASTKind.Lambda: + lam2: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node) + return self._lambda_scan_body(trans, lam2.body, param_names, param_count, + cap_names, cap_allocas, cap_tys, cap_count) + return cap_count diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index 58d0ff4..590ebec 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -37,6 +37,12 @@ MAX_MATCH_CASES: t.CDefine = 32 # 作用域种类常量(与 HandlesVar 的 Scope.Kind 区分,原 HandlesMatch.SCOPE_MATCH) SCOPE_MATCH: t.CDefine = 6 +# match 翻译最大递归深度(match → case body → translate_value → 嵌套 match)。 +# AST 自引用环/翻译 bug 可致无限递归 → ASan stack-overflow(崩溃点实测为 +# translate_match 内 subject kind() 虚调用,栈 2MB 耗尽)。入口计数出口递减, +# 超限即 fail-fast(对齐 TypeGenResolve 深度守卫,防栈溢出)。 +MATCH_TRANSLATE_MAX_DEPTH: t.CDefine = 64 + @t.NoVTable class MatchGenMixin: @@ -86,6 +92,19 @@ class MatchGenMixin: # ============================================================ def translate_match(self, trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: + """match 语句翻译主入口(深度守卫包装器,原逻辑见 _translate_match_impl)""" + if trans is not None: + if trans.MatchTranslateDepth >= MATCH_TRANSLATE_MAX_DEPTH: + self.Gen.TypeGenErr.fatal_error( + node, "match 翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError") + trans.MatchTranslateDepth += 1 + mret: int = self._translate_match_impl(trans, node) + if trans is not None: + trans.MatchTranslateDepth -= 1 + return mret + + def _translate_match_impl(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr) -> int: if node is None: return 0 pool: memhub.MemBuddy | t.CPtr = trans.Pool @@ -365,17 +384,15 @@ class MatchGenMixin: self.Gen.TypeGenErr.fatal_error(node, "match cases 缺失", "TypeError") case_count: t.CSizeT = len(cases_list) - # [MATCHDBG] 临时诊断:match 元信息(对比崩溃对应文件/语句;用后移除) - stdio.printf("[MATCHDBG] renum_match node=%llx renum=%s subject=%llx cases=%llx count=%zu\n", - t.CUInt64T(node), renum_name, t.CUInt64T(subject_ptr), - t.CUInt64T(cases_list), case_count) - # BB 指针数组、case 值指针数组、default 标记数组、nobreak 标记数组 bb_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES) cv_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES) def_arr: t.CInt | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES) nb_arr: t.CInt | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES) - if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None: + # pass1 缓存的 mc 指针数组(pass2 复用,避免重索引) + mc_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES) + if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None \ + or mc_arr is None: # fail-fast: match 分派数组分配失败 self.Gen.TypeGenErr.fatal_error(node, "match 分派数组分配失败", "TypeError") @@ -454,23 +471,14 @@ class MatchGenMixin: body_list: list[ast.AST | t.CPtr] | t.CPtr = mc.children nb: int = self._has_no_break(body_list) - # [MATCHDBG] 临时诊断:第一遍记录 case 快照(对比第二遍,定位列表破坏点;用后移除) - dbg_bl_len: t.CInt = -1 - dbg_first0: t.CSizeT = 0 - if body_list is not None: - dbg_bl_len = t.CInt(len(body_list)) - if dbg_bl_len > 0: - dbg_first0 = t.CUInt64T(body_list.get(0)) - stdio.printf("[MATCHDBG] pass1 i=%d mc=%llx pat=%llx children=%llx len=%d data0=%llx\n", - i, t.CUInt64T(mc), t.CUInt64T(pat), - t.CUInt64T(body_list), dbg_bl_len, dbg_first0) - # 存入数组 if collected < MAX_MATCH_CASES: bb_arr[collected] = t.CSizeT(bb) cv_arr[collected] = t.CSizeT(cv) def_arr[collected] = is_def nb_arr[collected] = nb + # 缓存 mc 指针(pass2 复用,避免重索引) + mc_arr[collected] = t.CSizeT(mc) collected = collected + 1 # 构建 SwitchCase 链表(非 default case 且有值) @@ -502,11 +510,12 @@ class MatchGenMixin: # 7. 第二遍:翻译每个 case body i = 0 while i < collected: - case_node: ast.AST | t.CPtr = cases_list.get(i) - if case_node is None: + # pass2 用 pass1 缓存的 mc(避免重索引错位) + mc: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)( + t.CVoid(mc_arr[i], t.CPtr)) + if mc is None: i = i + 1 continue - mc: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(case_node) pat: ast.AST | t.CPtr = mc.pattern # 取 BB @@ -540,11 +549,11 @@ class MatchGenMixin: bc: t.CSizeT = len(body_list) bi: int = 0 while bi < bc: + # 用 list.get() 读取元素值(已验证编译为 8 字节加载,与 + # pass1 的 cases_list.get(i) 同型同码)。切勿改为 + # `bl_data + bi*8` 裸地址:那会把数组槽地址当节点传入 + # translate_stmt,虚调用跳进 vtable 表 → DEP 崩溃。 stmt: ast.AST | t.CPtr = body_list.get(bi) - # [MATCHDBG] 临时诊断:第二遍逐条打印(对比 pass1 快照;用后移除) - stdio.printf("[MATCHDBG] pass2 i=%d bi=%d bc=%zu mc=%llx children=%llx stmt=%llx\n", - i, bi, bc, t.CUInt64T(mc), - t.CUInt64T(body_list), t.CUInt64T(stmt)) if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt) bi = bi + 1 @@ -582,13 +591,17 @@ class MatchGenMixin: dmc: ast.MatchCase | t.CPtr = ( ast.MatchCase | t.CPtr)(dcase) self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH) + # dbody 预声明+重赋值会使 .get 误分派成 list[int].get + # (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃, + # gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐 + # Converter arg_defaults 内联声明规避模式)。 dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children dbody = dbody2 - if dbody is not None: - dn: t.CSizeT = len(dbody) + if dbody2 is not None: + dn: t.CSizeT = len(dbody2) dj: int = 0 while dj < dn: - stmt: ast.AST | t.CPtr = dbody.get(dj) + stmt: ast.AST | t.CPtr = dbody2.get(dj) if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt) dj = dj + 1 @@ -801,7 +814,10 @@ class MatchGenMixin: if subj_vals is None: return 0 for ei in range(ecount): - elt: ast.AST | t.CPtr = elts.get(ei) + # elts 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断); + # 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式) + elts_inl: list[ast.AST | t.CPtr] | t.CPtr = elts + elt: ast.AST | t.CPtr = elts_inl.get(ei) sv: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( builder, pool, trans.Module, elt, trans._funcs, trans._func_count, trans) @@ -925,7 +941,10 @@ class MatchGenMixin: cond = llvmlite.const_int(pool, 1, 0) else: for pi in range(ecount): - pat1: ast.AST | t.CPtr = pats.get(pi) + # pats 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断); + # 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式) + pats_inl: list[ast.AST | t.CPtr] | t.CPtr = pats + pat1: ast.AST | t.CPtr = pats_inl.get(pi) if pat1 is None: continue pk1: int = pat1.kind() @@ -1064,13 +1083,17 @@ class MatchGenMixin: dmc: ast.MatchCase | t.CPtr = ( ast.MatchCase | t.CPtr)(dnode) self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH) + # dbody 预声明+重赋值会使 .get 误分派成 list[int].get + # (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃, + # gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐 + # Converter arg_defaults 内联声明规避模式)。 dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children dbody = dbody2 - if dbody is not None: - dn: t.CSizeT = len(dbody) + if dbody2 is not None: + dn: t.CSizeT = len(dbody2) dj: int = 0 while dj < dn: - stmt: ast.AST | t.CPtr = dbody.get(dj) + stmt: ast.AST | t.CPtr = dbody2.get(dj) if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt) dj = dj + 1 @@ -1301,13 +1324,17 @@ class MatchGenMixin: dmc: ast.MatchCase | t.CPtr = ( ast.MatchCase | t.CPtr)(dcase) self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH) + # dbody 预声明+重赋值会使 .get 误分派成 list[int].get + # (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃, + # gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐 + # Converter arg_defaults 内联声明规避模式)。 dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children dbody = dbody2 - if dbody is not None: - dn: t.CSizeT = len(dbody) + if dbody2 is not None: + dn: t.CSizeT = len(dbody2) dj: int = 0 while dj < dn: - stmt: ast.AST | t.CPtr = dbody.get(dj) + stmt: ast.AST | t.CPtr = dbody2.get(dj) if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt) dj = dj + 1 diff --git a/App/lib/core/Gen/StmtGenDeclMixin.vp b/App/lib/core/Gen/StmtGenDeclMixin.vp index d23f754..622c7cd 100644 --- a/App/lib/core/Gen/StmtGenDeclMixin.vp +++ b/App/lib/core/Gen/StmtGenDeclMixin.vp @@ -136,11 +136,98 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin): node: ast.AST | t.CPtr) -> int: """预扫描语句中的 AnnAssign,提前创建 alloca - 返回新增的变量数 + 递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部 + 也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c) + 在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的 + i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 → + 跨函数 SSA 错乱,llc "instruction expected to be numbered")。 + + 返回新增的变量数(调用方均忽略返回值)。 """ if node is None: return 0 - return self.Gen.AssignGen.translate_annassign_prescan(trans, node) + k: int = node.kind() + if k == ast.ASTKind.AnnAssign: + return self.Gen.AssignGen.translate_annassign_prescan(trans, node) + if k == ast.ASTKind.If: + ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node) + if ifn.children is not None: + bn_pre_i: t.CSizeT = len(ifn.children) + for bi_pre_i in range(bn_pre_i): + self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i)) + if ifn.orelse is not None: + bn_pre_e: t.CSizeT = len(ifn.orelse) + for bi_pre_e in range(bn_pre_e): + self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e)) + return 0 + if k == ast.ASTKind.For: + for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node) + if for_n.children is not None: + bn_pre_f: t.CSizeT = len(for_n.children) + for bi_pre_f in range(bn_pre_f): + self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f)) + if for_n.orelse is not None: + bn_pre_fe: t.CSizeT = len(for_n.orelse) + for bi_pre_fe in range(bn_pre_fe): + self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe)) + return 0 + if k == ast.ASTKind.While: + wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node) + if wh_n.children is not None: + bn_pre_w: t.CSizeT = len(wh_n.children) + for bi_pre_w in range(bn_pre_w): + self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w)) + if wh_n.orelse is not None: + bn_pre_we: t.CSizeT = len(wh_n.orelse) + for bi_pre_we in range(bn_pre_we): + self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we)) + return 0 + if k == ast.ASTKind.Try: + tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node) + if tr_n.children is not None: + bn_pre_t: t.CSizeT = len(tr_n.children) + for bi_pre_t in range(bn_pre_t): + self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t)) + if tr_n.handlers is not None: + bn_pre_th: t.CSizeT = len(tr_n.handlers) + for bi_pre_th in range(bn_pre_th): + eh_pre: ast.AST | t.CPtr = tr_n.handlers.get(bi_pre_th) + if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler: + ehc_pre: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_pre) + if ehc_pre.children is not None: + bn_pre_ehb: t.CSizeT = len(ehc_pre.children) + for bi_pre_ehb in range(bn_pre_ehb): + self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb)) + if tr_n.orelse is not None: + bn_pre_to: t.CSizeT = len(tr_n.orelse) + for bi_pre_to in range(bn_pre_to): + self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to)) + if tr_n.finalbody is not None: + bn_pre_tf: t.CSizeT = len(tr_n.finalbody) + for bi_pre_tf in range(bn_pre_tf): + self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf)) + return 0 + if k == ast.ASTKind.With: + wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node) + if wth.children is not None: + bn_pre_wb: t.CSizeT = len(wth.children) + for bi_pre_wb in range(bn_pre_wb): + self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb)) + return 0 + if k == ast.ASTKind.Match: + mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node) + if mt_n.cases is not None: + bn_pre_mc: t.CSizeT = len(mt_n.cases) + for bi_pre_mc in range(bn_pre_mc): + mc_pre: ast.AST | t.CPtr = mt_n.cases.get(bi_pre_mc) + if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase: + mcc_pre: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_pre) + if mcc_pre.children is not None: + bn_pre_mcb: t.CSizeT = len(mcc_pre.children) + for bi_pre_mcb in range(bn_pre_mcb): + self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb)) + return 0 + return 0 def pre_scan_scope_decls(self, trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: diff --git a/App/lib/core/Gen/StmtGenMixin.vp b/App/lib/core/Gen/StmtGenMixin.vp index ffbcbb8..a93f6be 100644 --- a/App/lib/core/Gen/StmtGenMixin.vp +++ b/App/lib/core/Gen/StmtGenMixin.vp @@ -41,6 +41,19 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin): def translate_stmt(self, trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: + """翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)""" + if trans is not None: + if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: + self.Gen.TypeGenErr.fatal_error( + node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError") + trans.TranslateDepth += 1 + mret: int = self._translate_stmt_impl(trans, node) + if trans is not None: + trans.TranslateDepth -= 1 + return mret + + def _translate_stmt_impl(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr) -> int: """翻译单条语句,返回新增的变量数""" if node is None: return 0 diff --git a/App/lib/core/Gen/TypeGenCType.vp b/App/lib/core/Gen/TypeGenCType.vp index fea32c6..90d46b4 100644 --- a/App/lib/core/Gen/TypeGenCType.vp +++ b/App/lib/core/Gen/TypeGenCType.vp @@ -280,16 +280,24 @@ class TypeGenCType(TypeGenBase.TypeGenBase): def _eval_const_int_expr(self, node: ast.AST | t.CPtr, pool: memhub.MemBuddy | t.CPtr = None, from_imports: str = None, - imported_modules: str = None) -> int: + imported_modules: str = None, + depth: int = 0) -> int: """递归计算常量整数表达式 pool/from_imports/imported_modules:跨模块 CDefine 查找上下文 (t.CArray count 引用依赖模块的 CDefine,如 SHA512_BLOCK_LEN 定义在 hashlib/__sha512.vp,本地 CDefine 表每模块清空,需跨模块回退查找)。 + depth:递归深度守卫(防御 AST 损坏成自引用环导致的无限递归—— + 本函数被 resolve_annotation_type 的 t.CArray count 解析调用,其内部 + BinOp 自递归不经过 resolve_annotation_type 的 depth 守卫)。 """ if node is None: self.ConstEvalOk = 0 return 0 + # 递归深度守卫:BinOp 左右子树成环时无限递归直至栈耗尽。 + # fail-fast 终止(fatal_type_error → sys.exit(1)),不静默回落。 + if depth > 64: + self.Gen.TypeGenErr.fatal_type_error(node, "常量表达式递归过深(AST 环或嵌套超过 64 层)") k: int = node.kind() # Constant: 整数字面量 @@ -369,11 +377,11 @@ class TypeGenCType(TypeGenBase.TypeGenBase): if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) # 递归计算左操作数 - lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules) + lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules, depth + 1) if self.ConstEvalOk == 0: return 0 # 递归计算右操作数 - rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules) + rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules, depth + 1) if self.ConstEvalOk == 0: return 0 # 根据运算符执行计算 diff --git a/App/lib/core/Gen/TypeGenErr.vp b/App/lib/core/Gen/TypeGenErr.vp index d94834f..c40ba8b 100644 --- a/App/lib/core/Gen/TypeGenErr.vp +++ b/App/lib/core/Gen/TypeGenErr.vp @@ -205,12 +205,18 @@ class TypeGenErr(TypeGenBase.TypeGenBase): ret_node_cr: ast.AST | t.CPtr = tp_cr.elts.get(1) if ret_node_cr is None: return None - return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans) + return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0) def has_decorator_marker(self, node: ast.AST | t.CPtr, - marker_name: str) -> int: + marker_name: str, + depth: int = 0) -> int: """检测类型注解中是否包含指定的装饰器标记""" if node is None or marker_name is None: return 0 + # 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数, + # vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813), + # BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。 + if depth > 128: + self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)") k: int = node.kind() @@ -225,9 +231,9 @@ class TypeGenErr(TypeGenBase.TypeGenBase): # BinOp 节点: int | t.CExtern(递归检测左右子节点) if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) - if self.has_decorator_marker(bop.left, marker_name) != 0: + if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0: return 1 - if self.has_decorator_marker(bop.right, marker_name) != 0: + if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0: return 1 return 0 diff --git a/App/lib/core/Gen/TypeGenResolve.vp b/App/lib/core/Gen/TypeGenResolve.vp index 4819b6b..289c95d 100644 --- a/App/lib/core/Gen/TypeGenResolve.vp +++ b/App/lib/core/Gen/TypeGenResolve.vp @@ -16,6 +16,7 @@ import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesExpr as HandlesExpr import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.Stub.StubMerger as StubMerger +import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.core.CompilerFactory as CompilerFactory @@ -70,6 +71,30 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): string.strcat(buf, at_attr_v) return buf return at_attr_v + # Subscript: 构造 "value[slice]" 格式(slice 递归,List/Tuple 保留完整下标) + # 如 t.Callable[[t.CInt], t.CInt] → "t.Callable[[t.CInt], t.CInt]"。 + # CTypedef 值提取(_register_ctypedef_from_annassign → lookup → _parse_type_string + # 重建)需要完整类型表达式字符串,slice 不能丢(此前不支持 Subscript → 返回 + # None → 注册失败 → 同 vpi 后续裸类名 lookup_ctypedef 失败 fail-fast, + # VKernel idt.vp irq_handler_t 根因)。 + if k == ast.ASTKind.Subscript: + sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + val_s: str = self._get_type_name_from_annotation(pool, sub_s.value) + if val_s is None: + return None + slice_s: str = self._subscript_slice_to_str(pool, sub_s.slice) + if slice_s is None: + return None + total_s: t.CSizeT = string.strlen(val_s) + 1 + string.strlen(slice_s) + 2 + buf_s: str = pool.alloc(total_s) + if buf_s is not None: + buf_s[0] = '\0' + string.strcat(buf_s, val_s) + string.strcat(buf_s, "[") + string.strcat(buf_s, slice_s) + string.strcat(buf_s, "]") + return buf_s + return None # BinOp (BitOr): 构造 "left|right" 格式 if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) @@ -87,6 +112,70 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): return buf2 return None return None + def _subscript_slice_to_str(self, pool: memhub.MemBuddy | t.CPtr, + node: ast.AST | t.CPtr) -> str: + """Subscript slice 节点转类型字符串(递归,支持 Name/Attribute/BinOp/Constant/List/Tuple/Subscript)""" + if node is None: + return None + k2: int = node.kind() + # Name / Attribute / BinOp / 嵌套 Subscript:复用类型名提取 + if k2 == ast.ASTKind.Name or k2 == ast.ASTKind.Attribute \ + or k2 == ast.ASTKind.BinOp or k2 == ast.ASTKind.Subscript: + return self._get_type_name_from_annotation(pool, node) + # Constant:数值/字符串字面量(t.CArray[elem, 104] 的 count 等) + if k2 == ast.ASTKind.Constant: + cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn2.const_kind == ast.CONST_INT: + buf2: str = pool.alloc(24) + if buf2 is not None: + viperlib.snprintf(buf2, 24, "%lld", t.CInt64T(cn2.int_val)) + return buf2 + if cn2.const_kind == ast.CONST_STR and cn2.str_val is not None: + return cn2.str_val + return None + # List:Callable 参数列表([[A], B]),元素递归,逗号连接 + if k2 == ast.ASTKind.List: + lst: ast.List | t.CPtr = (ast.List | t.CPtr)(node) + return self._list_elts_to_type_str(pool, lst.elts, "[", "]") + # Tuple:t.CArray[elem, N] 等双参形式。注意 TPV parser 把 Subscript 内 + # 带逗号的 slice 统一打包为 Tuple("a[x, y]" 语法),故 slice 在 Subscript + # 内**不带括号**输出("x, y"),否则 "t.Callable[([t.CInt], t.CInt)]" + # 的显式括号 Tuple slice 会让 ast.parse_expression 解析失败。 + if k2 == ast.ASTKind.Tuple: + tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node) + return self._list_elts_to_type_str(pool, tup.elts, "", "") + return None + def _list_elts_to_type_str(self, pool: memhub.MemBuddy | t.CPtr, + elts: list[ast.AST | t.CPtr] | t.CPtr, + open_ch: str, close_ch: str) -> str: + """元素列表转逗号分隔字符串(List/Tuple 共用),包上 open/close 括号""" + if elts is None or pool is None: + return None + buf3: str = pool.alloc(1024) + if buf3 is None: + return None + buf3[0] = '\0' + n3: t.CSizeT = len(elts) + for i3 in range(n3): + e3: ast.AST | t.CPtr = elts.get(i3) + if e3 is None: + continue + es3: str = self._subscript_slice_to_str(pool, e3) + if es3 is None: + continue + if string.strlen(buf3) > 0 and string.strlen(buf3) + 2 < 1024: + string.strcat(buf3, ", ") + if string.strlen(buf3) + string.strlen(es3) < 1024: + string.strcat(buf3, es3) + total3: t.CSizeT = string.strlen(buf3) + 2 + 2 + buf4: str = pool.alloc(total3) + if buf4 is None: + return None + buf4[0] = '\0' + string.strcat(buf4, open_ch) + string.strcat(buf4, buf3) + string.strcat(buf4, close_ch) + return buf4 def _is_sha1_prefixed(self, s: str) -> int: """判断 s 是否已是 "{16位hex}." 前缀(已规范化,防二次前缀) @@ -273,6 +362,41 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): return ast.BinOp(pool, left_node, ast.OpKind.BitOr, right_node) return None + # 检查是否包含 [ (Subscript):t.Callable[[t.CInt], t.CInt] 等。 + # 必须在 . 检查之前("t.Callable[...]" 含 '.',但本质是 Subscript, + # 若先走 Attribute 会把 "Callable[[t.CInt], t.CInt]" 当 attr → resolve + # 失败)。用 ast.parse_expression 完整解析(支持 List/Tuple 字面量 + # slice)。CTypedef 值如 "t.Callable[[t.CInt], t.CInt]"(irq_handler_t) + # 与返回注解 "t.CArray[t.CUInt8T, 16]" 依赖此路径。 + bracket_idx: t.CSizeT = seg_len + bi: t.CSizeT = 0 + while bi < seg_len: + if type_str[bi] == '[': + bracket_idx = bi + break + bi += 1 + if bracket_idx < seg_len: + # 优先 ast.parse_expression(单表达式入口) + expr_node: ast.AST | t.CPtr = ast.parse_expression(type_str, pool) + if expr_node is not None: + expr_body: ast.AST | t.CPtr = expr_node.body + if expr_body is not None: + return expr_body + # 备选:ast.parse 文件级(TPV parser 的 _parse_test 单表达式入口对 + # 复杂 slice 如 "t.Callable[[t.CInt], t.CInt]" 解析失败,但文件级 + # _parse_expr_stmt 可解析裸表达式语句——vpi 同语法已验证 + # parse_tokens 成功,故此处用文件级 parse 兜底) + tree_s: ast.AST | t.CPtr = ast.parse(type_str, pool) + if tree_s is not None: + ch_s: list[ast.AST | t.CPtr] | t.CPtr = tree_s.children + if ch_s is not None and len(ch_s) > 0: + st0: ast.AST | t.CPtr = ch_s.get(0) + if st0 is not None and st0.kind() == ast.ASTKind.Expr: + ex0: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(st0) + if ex0.value is not None: + return ex0.value + return None + # 检查是否包含 . (Attribute):只在有效段 [0, seg_len) 内查找 dot_idx: t.CSizeT = seg_len di: t.CSizeT = 0 @@ -828,14 +952,28 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): val_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) if val_nm2.id is not None and string.strcmp(val_nm2.id, "t") == 0: return at.attr + # import 别名还原:val_nm2.id 可能是 "import X as Y" 的别名 Y + # (如 "import fat32_types as types" → types.fileinfo 实为 + # fat32_types.fileinfo)。还原为模块真名后,AnnotClassName 存 + # 真限定名,跨模块同名类查找可经 StubMergerStore 按模块归属解析。 + real_mod2: str = val_nm2.id + alias_mod2: str = None + if from_imports is not None: + alias_mod2 = StubMergerStore.LookupModNameByAlias(from_imports, val_nm2.id) + if alias_mod2 is not None: + real_mod2 = alias_mod2 # 返回模块限定名 "module.Class"(如 "memhub.MemBuddy") mod_qual_buf: str = stdlib.malloc( - string.strlen(val_nm2.id) + string.strlen(at.attr) + 2) + string.strlen(real_mod2) + string.strlen(at.attr) + 2) if mod_qual_buf is not None: viperlib.snprintf(mod_qual_buf, - string.strlen(val_nm2.id) + string.strlen(at.attr) + 2, - "%s.%s", val_nm2.id, at.attr) + string.strlen(real_mod2) + string.strlen(at.attr) + 2, + "%s.%s", real_mod2, at.attr) + if alias_mod2 is not None: + stdlib.free(alias_mod2) return mod_qual_buf + if alias_mod2 is not None: + stdlib.free(alias_mod2) return at.attr # BinOp 节点: 联合类型 A | B @@ -928,7 +1066,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): gen_cls_name: str, trans: HT.Translator | t.CPtr, imported_modules: str, - from_imports: str) -> llvmlite.LLVMType | t.CPtr: + from_imports: str, + depth: int = 0) -> llvmlite.LLVMType | t.CPtr: """解析泛型类类型注解(特化或查找已注册结构体),失败返回 None""" if pool is None or sub is None or gen_cls_name is None: return None @@ -960,7 +1099,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if tup_elt is None: continue tup_ety: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, tup_elt, imported_modules, from_imports, trans) + pool, tup_elt, imported_modules, from_imports, trans, depth + 1) if tup_ety is None: return None tup_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, tup_ety) @@ -1060,7 +1199,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): node: ast.AST | t.CPtr, imported_modules: str, from_imports: str, - trans: HT.Translator | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr: + trans: HT.Translator | t.CPtr = None, + depth: int = 0) -> llvmlite.LLVMType | t.CPtr: """解析类型注解节点为 LLVMType Args: @@ -1069,9 +1209,16 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): imported_modules: 已导入模块名字符串(空格分隔) from_imports: from-import 映射字符串("name:module name:module" 格式) trans: 翻译器对象(可选,用于触发泛型特化) + depth: 递归深度(防御 AST 损坏成自引用环导致的无限递归) """ if node is None: return None + # 递归深度守卫:AST 损坏成自引用环(子节点回指祖先)时,无守卫的 + # BinOp/Subscript/Constant/Tuple 递归会无限加深直至栈耗尽(自举版 + # TransPyV.exe 编译 FuncTest 时 ASan stack-overflow 首选嫌疑)。超过 + # 128 层即 fail-fast 终止(fatal_type_error → sys.exit(1)),不静默兜底。 + if depth > 128: + self.Gen.TypeGenErr.fatal_type_error(node, "类型注解递归过深(AST 环或嵌套超过 128 层)") k: int = node.kind() @@ -1083,7 +1230,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if cn.const_kind == ast.CONST_STR and cn.str_val is not None: sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, cn.str_val, ast.ASTCtx.Load) if sub_node is not None: - return self.resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans) + return self.resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans, depth + 1) return None # Name 节点: str, int, bytes, from-import 名称, class 名称等 @@ -1125,7 +1272,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 用具体类型名递归解析(支持 BinOp/Attribute 类型实参) gp_sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, gp_ta, nm.ctx) if gp_sub_node is not None: - return self.resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans) + return self.resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans, depth + 1) # str = bytes = str = i8* if string.strcmp(nm.id, "str") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) @@ -1189,7 +1336,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if inner_node is not None: self.Gen.TypeGenCType.CTypedefResolveDepth += 1 inner_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, inner_node, imported_modules, from_imports, trans) + pool, inner_node, imported_modules, from_imports, trans, depth + 1) self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 self.Gen.TypeGenCType._ctypedef_pop_visiting() if inner_ty is not None: @@ -1253,9 +1400,35 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): full_attr_ty = self.Gen.StructGen.get_struct_type(full_q) if full_attr_ty is not None: return full_attr_ty - mod_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(at.attr) - if mod_struct_ty is not None: - return mod_struct_ty + # 跨模块限定名(Module.Class,Module 非 t/c/SHA1):按模块归属解析, + # 禁止裸名回退抓跨模块同名类(根因:VKernel sched.Thread 被 + # includes/vthreading.vp 同名 Thread 顶掉 → 'tid' 字段缺失)。 + # 模块名经 StubMergerStore sha1 映射表解析为模块 sha1,再精确命中 + # "{sha1}.{Class}"(定义侧命名)或 name+sha1(vpi stub 注入)。 + if is_builtin_attr == 0: + # 模块名 → sha1(精确/后缀/import 别名还原,统一经 + # StructGenMixin._resolve_module_sha1 逐级尝试) + mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1( + val_name.id, from_imports) + if mod_sha1_v is not None: + full_q_v: str = self._concat_sha1_class(pool, mod_sha1_v, at.attr) + if full_q_v is not None: + full_ty_v: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(full_q_v) + if full_ty_v is not None: + return full_ty_v + mod_entry_v: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(at.attr, mod_sha1_v) + if mod_entry_v is not None and mod_entry_v.Ty is not None: + if mod_entry_v.HasNew != 0: + return llvmlite.Ptr(pool, mod_entry_v.Ty) + return mod_entry_v.Ty + # 模块可解析但类未注册:不裸名回退(防跨库抓错), + # 落到下方枚举/typedef 尝试;struct 缺失由调用侧暴露 + # 模块名未登记(未编译/未扫描):同样不裸名回退 + else: + # t/c 内置库限定:裸名回退合法(内置类名无跨模块冲突) + mod_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(at.attr) + if mod_struct_ty is not None: + return mod_struct_ty # 模块限定枚举类型:Module.EnumClass(如 fat32_types.FRESULT, # t.CEnum 枚举类非结构体,get_struct_type 查不到) if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: @@ -1274,7 +1447,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if mod_td_inner is not None: self.Gen.TypeGenCType.CTypedefResolveDepth += 1 mod_td_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, mod_td_inner, imported_modules, from_imports, trans) + pool, mod_td_inner, imported_modules, from_imports, trans, depth + 1) self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 self.Gen.TypeGenCType._ctypedef_pop_visiting() if mod_td_ty is not None: @@ -1305,7 +1478,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if nested_inner is not None: self.Gen.TypeGenCType.CTypedefResolveDepth += 1 nested_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, nested_inner, imported_modules, from_imports, trans) + pool, nested_inner, imported_modules, from_imports, trans, depth + 1) self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 self.Gen.TypeGenCType._ctypedef_pop_visiting() if nested_ty is not None: @@ -1354,7 +1527,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if other_is_cvoid != 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) other_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, other_node, imported_modules, from_imports, trans) + pool, other_node, imported_modules, from_imports, trans, depth + 1) # 仅裸名(Name)场景验证解析结果必须是已注册 struct: # 损坏节点可能解析出基础类型垃圾(如 Int8 → Ptr(i8))。 # Attribute 场景(Module.Class / t.XXX)resolve 结果直接可用 @@ -1444,20 +1617,27 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): cptr_other = bop.left if cptr_sub_side is not None and cptr_other is not None: cptr_sub_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, cptr_sub_side, imported_modules, from_imports, trans) + pool, cptr_sub_side, imported_modules, from_imports, trans, depth + 1) cptr_other_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, cptr_other, imported_modules, from_imports, trans) + pool, cptr_other, imported_modules, from_imports, trans, depth + 1) if cptr_sub_ty is not None and cptr_other_ty is not None: # 计算 CPtr 侧外层 Ptr 嵌套数(如 t.CPtr[t.CPtr]=i8** → 2 层) cptr_level: int = 0 cur_lvl: llvmlite.LLVMType | t.CPtr = cptr_sub_ty while cur_lvl is not None: + # 注意:不能在 case body 内直接更新 while 条件变量 + # (cur_lvl = cur_lvl.Pointee)——自举编译出的 match 会把 + # 赋值写到 subject 快照槽,while 条件仍读旧槽 → 死循环 + # (gen2 编 FuncTest 卡死根因,gdb 反汇编 +3770-3819 确认)。 + # 改法:case 绑定 pointee 到局部变量,match 外更新循环变量。 + nxt_lvl: llvmlite.LLVMType | t.CPtr = None match cur_lvl: - case llvmlite.LLVMType.Ptr(_): + case llvmlite.LLVMType.Ptr(pointee): cptr_level += 1 - cur_lvl = cur_lvl.Pointee + nxt_lvl = pointee case _: break + cur_lvl = nxt_lvl # 结果 = 另一侧类型外包 N 层 Ptr res_ty: llvmlite.LLVMType | t.CPtr = cptr_other_ty li2: int = 0 @@ -1468,11 +1648,11 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略) left_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, bop.left, imported_modules, from_imports, trans) + pool, bop.left, imported_modules, from_imports, trans, depth + 1) if left_ty is not None: return left_ty right_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, bop.right, imported_modules, from_imports, trans) + pool, bop.right, imported_modules, from_imports, trans, depth + 1) if right_ty is not None: return right_ty return None @@ -1491,7 +1671,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) if gen_nm.id is not None: # 裸名 CArray(from t import * 场景,如 core.vp 的 - # `__python_suffix__: CArray[str] = ["py", ...]`):与 t.CArray + # `__python_suffix__: CArray[str] = [...]):与 t.CArray # 语义等价。否则走泛型模板查找失败(CArray 不是泛型类模板)→ # 字段类型退化 Ptr(i8) → __before_init__ 默认值 List 字面量误走 # 动态 list 构造(list[str].__new__/append 符号未生成 → 链接 @@ -1500,7 +1680,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 单参数: CArray[elem_ty] → Ptr(elem_ty)(静态推导长度数组) if sub.slice.kind() != ast.ASTKind.Tuple: elem_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, sub.slice, imported_modules, from_imports, trans) + pool, sub.slice, imported_modules, from_imports, trans, depth + 1) if elem_n is None: return None return llvmlite.Ptr(pool, elem_n) @@ -1513,7 +1693,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if elem_node_n is None or count_node_n is None: return None elem_ty_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, elem_node_n, imported_modules, from_imports, trans) + pool, elem_node_n, imported_modules, from_imports, trans, depth + 1) if elem_ty_n is None: return None # count=None → Ptr(elem)(等价单参) @@ -1527,7 +1707,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): return None return llvmlite.Array(pool, elem_ty_n, count_n) return self._resolve_generic_subscript_ty(pool, sub, gen_nm.id, trans, - imported_modules, from_imports) + imported_modules, from_imports, depth + 1) # 检查 value 是否是 Attribute(t.CArray / t.Callable / 模块限定泛型类如 numpy.ndarray[T]) if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") @@ -1539,7 +1719,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if trans is not None: if self.Gen.ClassGenGeneric._find_generic_template(arr_at.attr) is not None: return self._resolve_generic_subscript_ty(pool, sub, arr_at.attr, trans, - imported_modules, from_imports) + imported_modules, from_imports, depth + 1) if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray / t.Callable 必须通过模块名限定") arr_val_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arr_at.value) @@ -1560,7 +1740,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: self.Gen.TypeGenErr.fatal_type_error(node, "t.CPtr 模块未导入") inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, sub.slice, imported_modules, from_imports, trans) + pool, sub.slice, imported_modules, from_imports, trans, depth + 1) if inner_cptr_ty is None: self.Gen.TypeGenErr.fatal_type_error(sub.slice, "t.CPtr[?] 元素类型解析失败") return llvmlite.Ptr(pool, inner_cptr_ty) @@ -1572,7 +1752,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 相当于 t.CArray[elem_ty, None],表示静态推导长度数组 if sub.slice.kind() != ast.ASTKind.Tuple: elem_ty_single: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, sub.slice, imported_modules, from_imports, trans) + pool, sub.slice, imported_modules, from_imports, trans, depth + 1) if elem_ty_single is None: self.Gen.TypeGenErr.fatal_type_error(sub.slice, "t.CArray[?] 元素类型解析失败") return llvmlite.Ptr(pool, elem_ty_single) @@ -1590,7 +1770,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple 元素为 None") # 递归解析元素类型 elem_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( - pool, elem_node, imported_modules, from_imports, trans) + pool, elem_node, imported_modules, from_imports, trans, depth + 1) if elem_ty is None: self.Gen.TypeGenErr.fatal_type_error(elem_node, "t.CArray 元素类型解析失败") # 解析 count(支持整数常量、None、CDefine 常量名、常量表达式如 2*MAX) diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index 968b358..2071f3a 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -39,6 +39,10 @@ import string SCOPE_MODULE: t.CDefine = 0 SCOPE_FUNCTION: t.CDefine = 1 SCOPE_BLOCK: t.CDefine = 2 +# match case 作用域(对齐 MatchGenMixin.SCOPE_MATCH;本地定义避免循环依赖)。 +# 该作用域仅用于"变体字段绑定隔离",普通赋值语义上属于外层函数作用域, +# get_or_create_sym/_lookup_through_match_scope 会"看穿"它写回外层变量。 +SCOPE_MATCH: t.CDefine = 6 # 每个作用域最大变量数 # 256 太小:NewAstTest main() 有 289 个局部变量(几十个测试用例), @@ -377,6 +381,39 @@ class VarGenMixin: return 1 return 0 + # ============================================================ + # _lookup_through_match_scope — 穿越 match case 作用域查找变量 + # + # match case 作用域(SCOPE_MATCH)仅用于"变体字段绑定隔离"(case 绑定的 + # 模式变量互不泄漏)。case 体内对"外层普通变量"的赋值(如 FuncGenDef + # 参数 match 的 param_ty2 = Ptr(...),或 translate_subscript 内层 case 的 + # list_struct_name = lsn)语义上应写回外层 alloca,而非新建 case 局部槽 + # ——否则外层变量恒为初始值。自举证据:gen1 编译出的 gen2 其 FuncGenDef + # 参数 match 把赋值写进 case 局部槽、build_alloca 读初始 i32_ty,导致 + # 指针参数 alloca 退化为 i32 → FuncTest main.vp:32 `p[0] = p[0] + val` + # 的 translate_subscript 读 i32 非指针返回 None("赋值右侧为 None + # [kind=11]" 根因)。 + # 遍历规则:逐级向上查找;SCOPE_MATCH 边界可穿越(可命中外层 case 声明的 + # 变量),遇到第一个非 match 作用域(函数/模块)后停止——不跨函数边界 + # (闭包 nonlocal 语义由 is_nonlocal_name 单独处理)。 + # ============================================================ + def _lookup_through_match_scope(self, symtab: GenTypes.SymbolTable | t.CPtr, + name: str) -> llvmlite.Value | t.CPtr: + """从当前作用域向上,仅穿越 SCOPE_MATCH 作用域查找变量 alloca""" + if symtab is None or name is None: + return None + scope: GenTypes.Scope | t.CPtr = symtab.Current + while scope is not None: + result: llvmlite.Value | t.CPtr = self.find_var( + scope.Vars, scope.VarCount, name) + if result is not None: + return result + if scope.Kind == SCOPE_MATCH: + scope = scope.Parent + continue + return None + return None + # ============================================================ # get_or_create_sym — 查找或创建变量(在当前作用域) # ============================================================ @@ -387,6 +424,12 @@ class VarGenMixin: ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr: """查找或创建变量 alloca(在当前作用域)""" existing: llvmlite.Value | t.CPtr = self.lookup_current(symtab, name) + if existing is not None: + return existing + # SCOPE_MATCH 隔离作用域对"赋值"必须透明(详见 _lookup_through_match_scope): + # case 体内对外层变量的赋值要写回外层 alloca。仅穿越 match 边界, + # 不跨函数/模块作用域(global/nonlocal 在 AssignGenMixin 提前拦截)。 + existing = self._lookup_through_match_scope(symtab, name) if existing is not None: return existing alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty) diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index 645b8f7..baadeec 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -49,6 +49,12 @@ MAX_CORO_FUNCS: t.CDefine = 256 MAX_CORO_VARS: t.CDefine = 256 MAX_CORO_TRY: t.CDefine = 16 +# 翻译主环最大递归深度(translate_stmt/translate_value 嵌套)。AST 自引用环 +# 或翻译 bug 可致翻译主环(语句/表达式/调用)无限递归 → ASan stack-overflow。 +# match 守卫(MatchTranslateDepth)只保护 match 递归,本常量保护语句/表达式 +# 翻译环(fail-fast,对齐 TypeGenResolve 深度守卫模式)。 +TRANSLATE_MAX_DEPTH: t.CDefine = 256 + # ============================================================ # 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式) @@ -150,6 +156,16 @@ class Translator: # 追加90:控制流 merge 块可达性(1=有 br 进入可达/0=全分支 return 结构性不可达) _ctrl_merge_reachable: t.CInt + # match 翻译递归深度(match → case body → translate_value → 嵌套 match)。 + # 守卫:AST 自引用环(子节点回指祖先)或翻译 bug 导致的无限递归在此 + # fail-fast(对齐 TypeGenResolve 深度守卫,防 ASan stack-overflow)。 + MatchTranslateDepth: t.CInt + + # 翻译主环递归深度(translate_stmt/translate_value 入口计数)。match 守卫 + # 只覆盖 match 递归;语句/表达式/调用环(如 translate_value ↔ translate_call) + # 由本字段守卫,超 TRANSLATE_MAX_DEPTH 即 fail-fast(防 ASan stack-overflow)。 + TranslateDepth: t.CInt + # global 声明的变量名集合(当前函数内) _global_names: str _global_name_count: t.CInt diff --git a/App/lib/core/IncludesScanner.vp b/App/lib/core/IncludesScanner.vp index 844210f..7b8aa4e 100644 --- a/App/lib/core/IncludesScanner.vp +++ b/App/lib/core/IncludesScanner.vp @@ -277,9 +277,9 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1 else: - # 检查是否为 .vp 或 .py 文件(.vp 为 Viper 源,.py 向后兼容) + # 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py) is_py: int = 0 - if Utils.is_vp_or_py_at(fname, 0, fname_len) != 0: + if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1 if is_py != 0: @@ -314,6 +314,7 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, if fb_sdf is not None: viperlib.snprintf(fb_sdf, 1024, "子目录扫描失败 %d 个", subdir_failed) VLogger.error(fb_sdf, "scan") + return 0 diff --git a/App/main.vp b/App/main.vp index 240e0a4..64179f7 100644 --- a/App/main.vp +++ b/App/main.vp @@ -435,18 +435,11 @@ Target: %s # === 单文件模式:--src 指定时使用指定文件;--project 但无 SourceDir 时回退 === if src_path is None and project is not None: if Config.SourceDir is not None: - # 构造入口路径: 优先 SourceDir/main.vp,回退 SourceDir/main.py + # 构造入口路径: SourceDir/main.vp(不再做 vp -> py 后缀识别回退) sd_len: t.CSizeT = string.strlen(Config.SourceDir) path_buf: bytes = stdlib.malloc(sd_len + 16) if path_buf is not None: - # 优先尝试 main.vp viperlib.snprintf(path_buf, sd_len + 16, "%s/main.vp", Config.SourceDir) - ef: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) - if ef.closed: - # .vp 不存在,回退 main.py - viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir) - else: - ef.close() src_path = path_buf if log is not None: fb4: str = VLogger.fmt_buf() diff --git a/project.vpj b/project.vpj index ce786c1..52da420 100644 --- a/project.vpj +++ b/project.vpj @@ -10,7 +10,7 @@ }, "linker": { "cmd": "clang++", - "flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000"], + "flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000", "-Wl,--no-insert-timestamp"], "output": "TransPyV.exe" }, "includes": [