From f9878e5a63c12e8fe52aab7e3c29d7f1d0c65f10 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 15 Aug 2026 00:15:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=E8=B7=A8=E6=A8=A1=E5=9D=97=20CDef?= =?UTF-8?q?ine=20=E6=9F=A5=E6=89=BE=E7=BC=93=E5=AD=98=20perm=5Fpool=20+=20?= =?UTF-8?q?list=20=E5=86=85=E7=BD=AE=E6=96=B9=E6=B3=95=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. _lookup_cross_module_cdefine:lookup_from_import 返回非 NUL 结尾内部指针, base_mod 未截断导致 rc_full_mod 每层累积整个 from_imports + ".base" (dump:ast.base.base.base 无限递归根因)。入口截断模块名 + 缓存查询/写入 key 统一用 base_mod;结果缓存到 perm_pool(XmodCdef 表)跨文件复用, CONST_INT 递归查找只做一次。 2. translate_call:obj.method 检测将 list[AST|CPtr] 特化 struct 误当普通结构体, 查 append 返回类型(函数/text.ll/vpi 均未命中)fail-fast。新增 _is_list_builtin_method 识别 list 内置方法,在 Ptr(Struct)/Ptr(Ptr)/ AnnotClassName 三条路径跳过,落到 list 内置分支。 3. HandlesType 新增 register/lookup_xmod_cdefine 模块级委托(Mixin 隔离)。 --- App/lib/core/Gen/CallGenMixin.vp | 182 ++++++++++++++++++---------- App/lib/core/Gen/ExprGenMixin.vp | 150 +++++++++++++++++++++-- App/lib/core/Gen/Generator.vp | 11 ++ App/lib/core/Gen/TypeGenMixin.vp | 87 +++++++++++++ App/lib/core/Handles/HandlesType.vp | 18 +++ 5 files changed, 374 insertions(+), 74 deletions(-) diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index e752574..b1194fd 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -2018,9 +2018,14 @@ class CallGenMixin: OOP 结构体:alloca → call __before_init__(ptr) → call __init__(ptr, args) 或 store 参数 → load 普通结构体:alloca → zero + defaults → store 参数 → load """ + # OOM 诊断:结构体构造入口 used(定位跨模块类构造语句暴涨段) + stdio.printf("[MEM] struct_ctor %s can=%d used=%zu chk=%zu\n", + class_name, can, pool.used_bytes(), pool.chk_depth) # 创建临时 alloca # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) + stdio.printf("[MEM] struct_ctor %s after_ensure used=%zu chk=%zu\n", + class_name, pool.used_bytes(), pool.chk_depth) tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) if tmp is None: return llvmlite.ConstZero(pool, struct_ty) @@ -2078,6 +2083,9 @@ class CallGenMixin: new_arg_node: ast.AST | t.CPtr = cargs.get(nai) if new_arg_node is None: continue + # OOM 诊断:__new__ 参数逐个打印 used(定位第几个参数暴涨) + stdio.printf("[MEM] struct_ctor %s newarg %d used=%zu chk=%zu\n", + class_name, nai, pool.used_bytes(), pool.chk_depth) new_av: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, new_arg_node, None, 0, trans) if new_av is not None: @@ -2541,6 +2549,9 @@ class CallGenMixin: node: 触发本次调用的 AST 节点(如 Subscript 下标节点),用于 fatal_error 准确报告出错行号(传 None 时行号显示 0,不利于定位)。""" + # OOM 诊断:方法调用入口 used(定位 __init__/__before_init__ 跨模块调用段) + stdio.printf("[MEM] method_on_ptr %s.%s used=%zu chk=%zu\n", + class_name, method_name, pool.used_bytes(), pool.chk_depth) if pool is None or builder is None or class_name is None or method_name is None: return None if self_ptr is None: @@ -3619,6 +3630,38 @@ class CallGenMixin: return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans) + # ============================================================ + # _is_list_builtin_method - 判断是否是 list 泛型内置容器方法 + # + # list[T] 是内置泛型容器(_list.vp),其方法(append/get/set/pop/clear/ + # __iter__/__len__/__getitem__/__setitem__)由 translate_call 的 list + # 内置分支内联 GEP 处理,不注册为方法函数。若 obj.method() 检测把 + # list 特化 struct(如 list[AST | CPtr])误当普通结构体走 + # _translate_method_call,会查不到 append 返回类型 → fail-fast + # (NewAstTest body.append 根因)。此判断用于跳过 method_call 路径。 + # + # 返回: 1=list 内置方法(应落到 list 内置分支),0=普通方法 + # ============================================================ + def _is_list_builtin_method(self, class_name: str, + method_name: str) -> int: + """判断 (class_name, method_name) 是否是 list 泛型内置方法""" + if class_name is None or method_name is None: + return 0 + # 类名必须以 "list" 开头(list / list[AST | CPtr] 特化名) + if string.strncmp(class_name, "list", 4) != 0: + return 0 + # 方法名必须是 list 内置方法集合 + if string.strcmp(method_name, "append") == 0: return 1 + if string.strcmp(method_name, "get") == 0: return 1 + if string.strcmp(method_name, "set") == 0: return 1 + if string.strcmp(method_name, "pop") == 0: return 1 + if string.strcmp(method_name, "clear") == 0: return 1 + if string.strcmp(method_name, "__iter__") == 0: return 1 + if string.strcmp(method_name, "__len__") == 0: return 1 + if string.strcmp(method_name, "__getitem__") == 0: return 1 + if string.strcmp(method_name, "__setitem__") == 0: return 1 + return 0 + # ============================================================ # _translate_method_call - 翻译方法调用 obj.method(args) # @@ -5644,9 +5687,16 @@ class CallGenMixin: class_nm: str = HandlesStruct.get_class_name_by_type( pool, obj_struct_ty) if class_nm is not None: - return self._translate_method_call( - pool, builder, mod, class_nm, func_name, - obj_var, cl, trans) + # list 泛型内置容器方法(append/get/set/pop/clear/__iter__/ + # __len__/__getitem__/__setitem__)由下方 list 内置分支 + # (L5848 起)内联 GEP 处理,不注册方法函数;若走 + # _translate_method_call 会查 append 返回类型(函数/text.ll/ + # vpi stub 均未命中)→ fail-fast。检测到 list 方法时 + # 跳过 method_call,落到内置分支(NewAstTest body.append 根因)。 + if self._is_list_builtin_method(class_nm, func_name) == 0: + return self._translate_method_call( + pool, builder, mod, class_nm, func_name, + obj_var, cl, trans) # 指针变量: alloca 类型是 Ptr(Ptr(Struct)) # load 获取实际指针 Ptr(Struct) 后再查找 match obj_struct_ty: @@ -5654,12 +5704,14 @@ class CallGenMixin: inner_nm: str = HandlesStruct.get_class_name_by_type( pool, inner_struct_ty) if inner_nm is not None: - obj_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, obj_struct_ty, obj_var) - if obj_ptr is not None: - return self._translate_method_call( - pool, builder, mod, inner_nm, func_name, - obj_ptr, cl, trans) + # list 内置方法同样跳过(同 Ptr(Struct) 分支) + if self._is_list_builtin_method(inner_nm, func_name) == 0: + obj_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, obj_struct_ty, obj_var) + if obj_ptr is not None: + return self._translate_method_call( + pool, builder, mod, inner_nm, func_name, + obj_ptr, cl, trans) # AnnotClassName 回退:联合类型简化为 Ptr(i8) 时, # 通过原始类型注解的类名查找结构体 var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( @@ -5681,60 +5733,66 @@ class CallGenMixin: mod_nm2[mod_len2] = '\0' # 检查结构体是否已注册 if HandlesStruct.get_struct_type(cls_nm2) is not None: - # 结构体已注册:load obj_ptr 走方法调用 - i8_ptr_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( - pool, llvmlite.Int8(pool)) - obj_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, i8_ptr_ty2, obj_var) - if obj_ptr2 is not None: - return self._translate_method_call( - pool, builder, mod, cls_nm2, func_name, - obj_ptr2, cl, trans) + # list 内置方法跳过 method_call(同 Ptr(Struct) 分支), + # 落到下方 list 内置分支 + if self._is_list_builtin_method(cls_nm2, func_name) == 0: + # 结构体已注册:load obj_ptr 走方法调用 + i8_ptr_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + obj_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty2, obj_var) + if obj_ptr2 is not None: + return self._translate_method_call( + pool, builder, mod, cls_nm2, func_name, + obj_ptr2, cl, trans) else: - # 结构体未注册(跨模块依赖未翻译): - # 用模块名查 SHA1 构造方法调用名 - sha1_fc: str = None - if mod_nm2 is not None: - sha1_fc = self._lookup_module_sha1(mod_nm2) - if sha1_fc is not None: - # 构造 "{sha1}.{cls_nm2}.{func_name}" 调用名 - mlen3: t.CSizeT = string.strlen(sha1_fc) + string.strlen(cls_nm2) + string.strlen(func_name) + 3 - mangled3: str = pool.alloc(mlen3) - if mangled3 is not None: - viperlib.snprintf(mangled3, mlen3, "%s.%s.%s", sha1_fc, cls_nm2, func_name) - # load obj_ptr (self) - i8_ptr_ty3: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( - pool, llvmlite.Int8(pool)) - obj_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, i8_ptr_ty3, obj_var) - if obj_ptr3 is not None: - # 构建参数链表: obj_ptr3 (self) → 其他参数 - llvmlite.value_set_next(obj_ptr3, None) - head3: llvmlite.Value | t.CPtr = obj_ptr3 - tail3: llvmlite.Value | t.CPtr = obj_ptr3 - for ai3 in range(can): - arg3: ast.AST | t.CPtr = cargs.get(ai3) - if arg3 is not None: - arg_val3: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( - builder, pool, mod, arg3, None, 0, trans) - if arg_val3 is not None: - llvmlite.value_set_next(arg_val3, None) - llvmlite.value_set_next(tail3, arg_val3) - tail3 = arg_val3 - total3: int = 1 + can - ret_ty3: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty( - pool, cls_nm2, func_name) - if ret_ty3 is None: - ret_ty3 = self._LookupMethodRetTyFromTextLL( - pool, sha1_fc, cls_nm2, func_name) - if ret_ty3 is None: - # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) - ret_ty3 = self._lookup_method_ret_ty_via_vpi( - pool, sha1_fc, cls_nm2, func_name) - if ret_ty3 is None: - ret_ty3 = self._fail_method_ret_ty(func_name) - self._ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) - return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) + # list 内置方法跳过(结构体未注册路径同样不能 + # 构造 {sha1}.{cls}.append 调用名——list 方法无此函数) + if self._is_list_builtin_method(cls_nm2, func_name) == 0: + # 结构体未注册(跨模块依赖未翻译): + # 用模块名查 SHA1 构造方法调用名 + sha1_fc: str = None + if mod_nm2 is not None: + sha1_fc = self._lookup_module_sha1(mod_nm2) + if sha1_fc is not None: + # 构造 "{sha1}.{cls_nm2}.{func_name}" 调用名 + mlen3: t.CSizeT = string.strlen(sha1_fc) + string.strlen(cls_nm2) + string.strlen(func_name) + 3 + mangled3: str = pool.alloc(mlen3) + if mangled3 is not None: + viperlib.snprintf(mangled3, mlen3, "%s.%s.%s", sha1_fc, cls_nm2, func_name) + # load obj_ptr (self) + i8_ptr_ty3: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + obj_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty3, obj_var) + if obj_ptr3 is not None: + # 构建参数链表: obj_ptr3 (self) → 其他参数 + llvmlite.value_set_next(obj_ptr3, None) + head3: llvmlite.Value | t.CPtr = obj_ptr3 + tail3: llvmlite.Value | t.CPtr = obj_ptr3 + for ai3 in range(can): + arg3: ast.AST | t.CPtr = cargs.get(ai3) + if arg3 is not None: + arg_val3: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, arg3, None, 0, trans) + if arg_val3 is not None: + llvmlite.value_set_next(arg_val3, None) + llvmlite.value_set_next(tail3, arg_val3) + tail3 = arg_val3 + total3: int = 1 + can + ret_ty3: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty( + pool, cls_nm2, func_name) + if ret_ty3 is None: + ret_ty3 = self._LookupMethodRetTyFromTextLL( + pool, sha1_fc, cls_nm2, func_name) + if ret_ty3 is None: + # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) + ret_ty3 = self._lookup_method_ret_ty_via_vpi( + pool, sha1_fc, cls_nm2, func_name) + if ret_ty3 is None: + ret_ty3 = self._fail_method_ret_ty(func_name) + self._ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) + return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) # ============================================================ # 检测表达式接收者方法调用: (expr).method(args) diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index d4fd199..490b4ef 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -80,6 +80,13 @@ class ExprGenMixin: DocValues: bytes DocCount: int + # --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) --- + DiagCDefineCount: int + # --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) --- + DiagNameValueCount: int + # --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) --- + DiagTranslateValueCount: int + # ============================================================ # P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数, # 本模块函数调用改 self.,外部模块调用保留模块前缀) @@ -919,6 +926,12 @@ class ExprGenMixin: """翻译值表达式为 LLVM Value""" if node is None: return None + # OOM 诊断:translate_value 调用计数(每 4096 次打印 kind+行号,定位 CONST_INT 循环调用源) + self.DiagTranslateValueCount += 1 + if (self.DiagTranslateValueCount % 4096) == 1: + stdio.printf("[MEM] trans_val %d kind=%d line=%d used=%zu chk=%zu\n", + self.DiagTranslateValueCount, node.kind(), node.lineno, + pool.used_bytes(), pool.chk_depth) k: int = node.kind() if k == ast.ASTKind.Constant: rv: llvmlite.Value | t.CPtr = self.translate_constant(builder, pool, mod, node, trans) @@ -2240,7 +2253,9 @@ class ExprGenMixin: def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy | t.CPtr, name: str, from_imports: str, - imported_modules: str = None) -> int: + imported_modules: str = None, + depth: int = 0, + node: ast.AST | t.CPtr = None) -> int: """跨模块查找 CDefine 常量,返回值或 -1(未找到) 查找顺序: @@ -2248,7 +2263,27 @@ class ExprGenMixin: 2. from_imports star 回退(from X import *) 3. imported_modules 遍历(import X):当通过 import X 导入模块时, 模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用) + + depth: 递归深度(子模块递归/imported_modules 遍历的内部调用递增)。 + node: 触发查找的 AST 节点(递归内传 None,line 0;顶层调用点传实际节点)。 """ + # 递归深度守卫:子模块递归(from .X import NAME re-export 链)与 + # imported_modules 遍历组合可能形成循环(模块间互相 from import), + # 无上限时无限递归 → 每次递归 pool.alloc 缓冲(rc_sub_buf/rc_full_mod/ + # rc_fi/im_buf/sub_fi)→ O(n²) 累积 → 主池 1.5GB OOM + # (NewAstTest CONST_INT 单次查找 14593 次调用,fi 恒为 CONST_INT:ast)。 + # 正常跨模块 CDefine 查找深度 < 8(如 ast → ast.base),32 上限 fail-fast + # 暴露循环 import 链(而非静默 OOM)。报错带 node 行号 + 当前递归模块链。 + if depth > 32: + err_buf: str = pool.alloc(256) + if err_buf is not None: + viperlib.snprintf(err_buf, 256, + "跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export): %s (depth=%d fi=%s)", + name, depth, + from_imports if from_imports is not None else "(nil)") + HandlesType.fatal_error(node, err_buf, "InternalCompilerError") + HandlesType.fatal_error(node, + "跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export)", "InternalCompilerError") # 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态 # 避免递归调用时前一次成功的标志残留导致误判 HandlesType.set_cdefine_found(0) @@ -2257,6 +2292,14 @@ class ExprGenMixin: if from_imports is None: from_imports = "" + # OOM 诊断:跨模块 CDefine 查找调用计数(每 256 次打印 used) + self.DiagCDefineCount += 1 + if (self.DiagCDefineCount % 256) == 1: + stdio.printf("[MEM] cdefine_lookup %d name=%s fi=%.16s used=%zu chk=%zu\n", + self.DiagCDefineCount, name, + from_imports if from_imports is not None else "(nil)", + pool.used_bytes(), pool.chk_depth) + # 1. 从 from_imports 查找名称对应的模块名 # 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导 mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0) @@ -2264,14 +2307,53 @@ class ExprGenMixin: # 精确匹配失败:尝试 star import 回退 mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1) - # 当前模块名(去前导点),用于构建子模块全名 base_mod.X(如 "hashlib" + "." + - # "__md5")。此前 base_mod 未赋值(未初始化变量)→ 子模块递归 strlen(NULL) - # 崩溃(ASan access-violation,Attribute count 新路径触发 _lookup_cross_module_cdefine)。 - base_mod: str = mod_name_raw - if base_mod is not None: - while base_mod[0] == '.': + # 截断模块名到 NUL 结尾:lookup_from_import 返回指向 from_imports 内部的 + # 指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast CONST_STR:ast ...")。 + # 不截断会污染 base_mod:strlen 读到整个 from_imports 末尾 → rc_full_mod + # 每层累积整个列表 + ".base"(dump:ast.base.base.base 无限递归根因), + # 且缓存 key 错乱("CONST_INT@ast CONST_STR:ast ...")无法命中。 + # 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。 + base_mod: str = None + if mod_name_raw is not None: + mbl: t.CSizeT = 0 + while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' \ + and mod_name_raw[mbl] != ':': + mbl += 1 + mb_buf: str = pool.alloc(mbl + 1) + if mb_buf is not None: + mi2: t.CSizeT + for mi2 in range(mbl): + mb_buf[mi2] = mod_name_raw[mi2] + mb_buf[mbl] = '\0' + base_mod = mb_buf + while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1 + # 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用): + # 首次查找成功后结果缓存,后续文件(含 reset_to(mark2)/模块切换)直接命中, + # 避免 CONST_INT 跨模块递归查找反复触发(NewAstTest OOM 根因—— + # 14593 次调用全部重复递归 ast → ast.base → ...)。 + # key = "name@源模块"(源模块维度,用去点后的 base_mod 保证一致), + # 保持模块隔离:不同源模块的同名 CDefine 值不同时不串值。 + # 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。 + xkey_buf: str = None + if base_mod is not None: + xk_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2 + xkey_buf = pool.alloc(xk_len) + if xkey_buf is not None: + viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod) + xval: str = HandlesType.lookup_xmod_cdefine(xkey_buf) + if HandlesType.is_cdefine_found() != 0: + if HandlesType.is_cdefine_str() != 0: + HandlesType.register_cdefine_string(pool, name, xval) + elif HandlesType.is_cdefine_double() != 0: + xd_v: double = string.atof(xval) + HandlesType.register_cdefine_double(pool, name, xd_v) + else: + xv_i: int = string.atoi(xval) + HandlesType.register_cdefine_constant(pool, name, xv_i) + return string.atoi(xval) + # 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6) # 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中, # 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules @@ -2313,7 +2395,7 @@ class ExprGenMixin: sub_fi[2 + mk2] = im_buf[mk2] mk2 += 1 sub_fi[2 + im_nlen] = '\0' - sub_val: int = self._lookup_cross_module_cdefine(pool, name, sub_fi, None) + sub_val: int = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: return sub_val return -1 @@ -2721,6 +2803,17 @@ class ExprGenMixin: continue viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf) + # 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼 + # .base → ast.base.base),说明子模块递归在重复追加同一模块段 + # (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归 + # (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。 + rc_blen: t.CSizeT = string.strlen(base_mod) + if rc_blen > rc_sub_len + 1: + rc_tail: str = base_mod + (rc_blen - rc_sub_len) + if rc_tail[0] == '.' and \ + string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0: + continue + # 构建临时 from_imports "name:full_mod" rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1 rc_fi: str = pool.alloc(rc_fi_len) @@ -2729,7 +2822,9 @@ class ExprGenMixin: viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod) # 递归调用查找子模块 - rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi) + stdio.printf("[MEM] cdef_rec d=%d name=%s mod=%s\n", + depth, name, rc_full_mod) + rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: result_val = rc_sub_val result_found = 1 @@ -2770,17 +2865,42 @@ class ExprGenMixin: sub_fi2[2 + mk4] = im2_buf[mk4] mk4 += 1 sub_fi2[2 + im2_nlen] = '\0' - sub_val2: int = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None) + sub_val2: int = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: result_val = sub_val2 result_found = 1 - # 10. 缓存到当前模块的 CDefine 表中 + # 10. 缓存到当前模块的 CDefine 表中,并写入跨模块 perm_pool 缓存 # 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存 # (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。 if result_found != 0: if res_is_double == 0: HandlesType.register_cdefine_constant(pool, name, result_val) + # 跨模块查找缓存(perm_pool 永久,跨文件复用):整数 + # key 与入口查询一致:"name@源模块"(用截断后的 base_mod, + # 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配) + if base_mod is not None: + ck_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2 + ckey_buf: str = pool.alloc(ck_len) + if ckey_buf is not None: + viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod) + cbuf_i: str = pool.alloc(32) + if cbuf_i is not None: + viperlib.snprintf(cbuf_i, 32, "%d", result_val) + HandlesType.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0) + else: + # 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度) + if base_mod is not None: + ck_len2: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2 + ckey2: str = pool.alloc(ck_len2) + if ckey2 is not None: + viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod) + dval_cc: double = HandlesType.lookup_cdefine_double(name) + if HandlesType.is_cdefine_double() != 0: + cbuf_d: str = pool.alloc(64) + if cbuf_d is not None: + stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc) + HandlesType.register_xmod_cdefine(ckey2, cbuf_d, 1, 0) HandlesType.set_cdefine_found(1) else: HandlesType.set_cdefine_found(0) @@ -2828,6 +2948,12 @@ class ExprGenMixin: nm_id: str = nm.id if nm_id is None: return None + # OOM 诊断:translate_name_value 调用计数(每 2048 次打印,定位 CONST_INT 循环调用源) + self.DiagNameValueCount += 1 + if (self.DiagNameValueCount % 2048) == 1: + stdio.printf("[MEM] name_value %d name=%s used=%zu chk=%zu\n", + self.DiagNameValueCount, nm_id, + pool.used_bytes(), pool.chk_depth) # CDefine 编译期常量: 返回常量值(浮点 CDefine → double 常量,整数 → i32) # NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表 @@ -2853,7 +2979,7 @@ class ExprGenMixin: if self._is_cdefine_name_pattern(nm_id) != 0: if trans is not None: cdef_val = self._lookup_cross_module_cdefine( - pool, nm_id, trans._from_imports, trans._imported_modules) + pool, nm_id, trans._from_imports, trans._imported_modules, 0, node) if HandlesType.is_cdefine_found() != 0: if HandlesType.is_cdefine_str() != 0: cdef_sv2: str = HandlesType.lookup_cdefine_string(nm_id) diff --git a/App/lib/core/Gen/Generator.vp b/App/lib/core/Gen/Generator.vp index f6bef13..1c00bc4 100644 --- a/App/lib/core/Gen/Generator.vp +++ b/App/lib/core/Gen/Generator.vp @@ -70,6 +70,11 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.CDefineIsDouble = 0 self.CDefineIsStr = 0 self.CDefineFound = 0 + # 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用) + self.XmodCdefNames = None + self.XmodCdefValues = None + self.XmodCdefDoubleFlags = None + self.XmodCdefStrFlags = None self.CurrentModuleSha1 = None self.CTypedefNames = None self.CTypedefTypeStrs = None @@ -84,6 +89,8 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.StdlibAlloc = 0 self.VisibleStructs = None self.StrictVisibility = 0 + # OOM 语句级诊断:translate_stmt 限频打印 used(定位单文件翻译暴涨起点) + self.DiagStmtCount = 0 # ---- CallGenMixin 字段 ---- self.CexportSha1Arr = None self.CexportFuncArr = None @@ -140,6 +147,10 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.DocKeys = None self.DocValues = None self.DocCount = 0 + # OOM 诊断计数:跨模块 CDefine 查找调用次数 + self.DiagCDefineCount = 0 + self.DiagNameValueCount = 0 + self.DiagTranslateValueCount = 0 # ============================================================ diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 9a23534..230a95f 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -74,6 +74,15 @@ class TypeGenMixin: CDefineIsStr: int CDefineFound: int + # --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) --- + # 2026-08-14:跨模块 CDefine 查找(_lookup_cross_module_cdefine)结果缓存到 + # perm_pool,reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用, + # CONST_INT 递归查找只做一次(NewAstTest OOM 根因)。 + XmodCdefNames: list[str] | t.CPtr + XmodCdefValues: list[str] | t.CPtr + XmodCdefDoubleFlags: list[int] | t.CPtr + XmodCdefStrFlags: list[int] | t.CPtr + # --- 当前模块 SHA1(原 _g_current_module_sha1) --- CurrentModuleSha1: str @@ -108,6 +117,10 @@ class TypeGenMixin: self.CDefineIsDouble = 0 self.CDefineIsStr = 0 self.CDefineFound = 0 + self.XmodCdefNames = None + self.XmodCdefValues = None + self.XmodCdefDoubleFlags = None + self.XmodCdefStrFlags = None self.CurrentModuleSha1 = None self.CTypedefNames = None self.CTypedefTypeStrs = None @@ -283,6 +296,80 @@ class TypeGenMixin: if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1) + # ============================================================ + # 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) + # ============================================================ + def register_xmod_cdefine(self, name: str, value_str: str, + is_double: int, is_str: int) -> None: + """注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用) + + Args: + name: CDefine 常量名(如 CONST_INT) + value_str: 值文本(十进制/浮点/字符串) + is_double: 1=浮点值 + is_str: 1=字符串值 + """ + if name is None or value_str is None: + return + # 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定) + if self.XmodCdefNames is not None: + x_n: t.CSizeT = len(self.XmodCdefNames) + x_i: t.CSizeT + for x_i in range(x_n): + x_nm: str = self.XmodCdefNames.get(x_i) + if x_nm is not None and string.strcmp(x_nm, name) == 0: + return + perm: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if self.XmodCdefNames is None: + self.XmodCdefNames = list[str](perm, 64) + self.XmodCdefValues = list[str](perm, 64) + self.XmodCdefDoubleFlags = list[int](perm, 64) + self.XmodCdefStrFlags = list[int](perm, 64) + # key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在 + # reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活 + klen: t.CSizeT = string.strlen(name) + kcopy: str = perm.alloc(klen + 1) + if kcopy is None: + return + string.strcpy(kcopy, name) + vlen: t.CSizeT = string.strlen(value_str) + vcopy: str = perm.alloc(vlen + 1) + if vcopy is None: + return + string.strcpy(vcopy, value_str) + self.XmodCdefNames.append(kcopy) + self.XmodCdefValues.append(vcopy) + if self.XmodCdefDoubleFlags is not None: + self.XmodCdefDoubleFlags.append(is_double) + if self.XmodCdefStrFlags is not None: + self.XmodCdefStrFlags.append(is_str) + + def lookup_xmod_cdefine(self, name: str) -> str: + """查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志 + + Returns: + 值文本(命中),未命中返回 None + """ + self.CDefineFound = 0 + self.CDefineIsDouble = 0 + self.CDefineIsStr = 0 + if name is None or self.XmodCdefNames is None: + return None + n: t.CSizeT = len(self.XmodCdefNames) + i: t.CSizeT + for i in range(n): + nm: str = self.XmodCdefNames.get(i) + if nm is not None and string.strcmp(nm, name) == 0: + val_str: str = self.XmodCdefValues.get(i) + if val_str is not None: + self.CDefineFound = 1 + if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1: + self.CDefineIsStr = 1 + elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1: + self.CDefineIsDouble = 1 + return val_str + return None + def lookup_cdefine_constant(self, name: str) -> int: """查找 CDefine 编译期常量 diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index bb0f17e..74d6cc7 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -249,6 +249,24 @@ def clear_cdefine_constants() -> None: g.clear_cdefine_constants() +def register_xmod_cdefine(name: str, value_str: str, + is_double: int, is_str: int) -> None: + """注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(委托到 gen.register_xmod_cdefine)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + g.register_xmod_cdefine(name, value_str, is_double, is_str) + + +def lookup_xmod_cdefine(name: str) -> str: + """查找跨模块 CDefine 缓存(委托到 gen.lookup_xmod_cdefine)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.lookup_xmod_cdefine(name) + return None + + def register_ctypedef(pool: memhub.MemBuddy | t.CPtr, name: str, type_str: str) -> None: """注册 CTypedef 到本地表(委托到 gen.register_ctypedef)"""