diff --git a/App/lib/core/CompilerFactory.vp b/App/lib/core/CompilerFactory.vp new file mode 100644 index 0000000..af03dbf --- /dev/null +++ b/App/lib/core/CompilerFactory.vp @@ -0,0 +1,68 @@ +import t, c +import memhub + + +# ============================================================ +# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式) +# +# TPC 的 Translator 在 __init__ 集中创建编译器各子对象(Handler/ +# SymbolTable/Exportable 等),是编译器的"装配器/工厂";TPV 的 main +# 目前手动创建 mb/gen 并逐模块注入(_mbuddy/set_global_gen),装配 +# 逻辑散落。本工厂集中持有编译器级共享内存对象: +# +# - Pool :主内存池(MemBuddy,瞬时数据,受 mark/reset_to 回收) +# - PermPool :永久内存池(MemBuddy,跨 mark 持久数据专用,从不 reset) +# +# 各模块经模块级委托 CompilerFactory.get_perm_pool() 获取永久池, +# 避免把编译器专属单例塞进通用内存库 memhub(memhub 保持通用)。 +# includes 层(llvmlite/__types.vp 的 DeepCopyType)不依赖本工厂: +# 深拷贝的分配源由调用方以 pool 参数传入(传 PermPool 即持久)。 +# 类名与模块名同名(均为 CompilerFactory):合法场景,TPC 已支持 +# 模块别名优先解析——`X.func()` 中 X 是 import 别名(IsModuleAlias) +# 时按模块函数处理,`X.X(args)` 的类构造按 FuncAttr==类名 走 struct +# 路径,二者互不冲突(勿再改类名规避,见用户明确要求)。 +# ============================================================ +@t.NoVTable +class CompilerFactory: + """编译器结构体工厂(持有主池/永久池等共享对象)""" + + Pool: memhub.MemBuddy | t.CPtr # 主内存池(瞬时数据,mark/reset 回收) + PermPool: memhub.MemBuddy | t.CPtr # 永久内存池(持久数据,从不 reset) + + def __init__(self, pool: memhub.MemBuddy | t.CPtr, + perm_pool: memhub.MemBuddy | t.CPtr): + if self is None: + return + self.Pool = pool + self.PermPool = perm_pool + + +# ============================================================ +# 全局工厂单例 +# ============================================================ +_g_factory: CompilerFactory | t.CPtr = None + + +def set_factory(factory: CompilerFactory | t.CPtr) -> None: + """注册全局工厂单例(main 创建后注册)""" + global _g_factory + _g_factory = factory + + +def get_factory() -> CompilerFactory | t.CPtr: + """获取全局工厂单例(未注册返回 None)""" + return _g_factory + + +def get_pool() -> memhub.MemBuddy | t.CPtr: + """获取主内存池(瞬时数据分配源)""" + if _g_factory is not None: + return _g_factory.Pool + return None + + +def get_perm_pool() -> memhub.MemBuddy | t.CPtr: + """获取永久内存池(跨 mark 持久数据分配源)""" + if _g_factory is not None: + return _g_factory.PermPool + return None diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index a96147d..e752574 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -23,6 +23,7 @@ import lib.core.Handles.HandlesFunctions as HandlesFunctions import lib.core.StubMerger as StubMerger import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign +import lib.core.CompilerFactory as CompilerFactory # ============================================================ @@ -152,8 +153,8 @@ class CallGenMixin: if sha1 is None or func_name is None: return 0 if self.CexportSha1Arr is None: - self.CexportSha1Arr = stdlib.malloc(_CEXPORT_MAX * 17) - self.CexportFuncArr = stdlib.malloc(_CEXPORT_MAX * 64) + self.CexportSha1Arr = CompilerFactory.get_perm_pool().alloc(_CEXPORT_MAX * 17) + self.CexportFuncArr = CompilerFactory.get_perm_pool().alloc(_CEXPORT_MAX * 64) if self.CexportSha1Arr is None or self.CexportFuncArr is None: return 0 string.memset(self.CexportSha1Arr, 0, _CEXPORT_MAX * 17) @@ -7097,7 +7098,7 @@ class CallGenMixin: """确保闭包名表已分配,0 失败""" if self.ClosureNames is not None: return 1 - self.ClosureNames = stdlib.malloc(64 * MAX_CLOSURE_NAMES) + self.ClosureNames = CompilerFactory.get_perm_pool().alloc(64 * MAX_CLOSURE_NAMES) if self.ClosureNames is None: return 0 string.memset(self.ClosureNames, 0, 64 * MAX_CLOSURE_NAMES) @@ -7130,8 +7131,8 @@ class CallGenMixin: """确保函数指针签名表已分配(stdlib.malloc,懒初始化)""" if self.FuncptrNames is not None: return 1 - self.FuncptrNames = stdlib.malloc(64 * MAX_FUNC_PTR_SIGS) - self.FuncptrRets = stdlib.malloc(8 * MAX_FUNC_PTR_SIGS) + self.FuncptrNames = CompilerFactory.get_perm_pool().alloc(64 * MAX_FUNC_PTR_SIGS) + self.FuncptrRets = CompilerFactory.get_perm_pool().alloc(8 * MAX_FUNC_PTR_SIGS) if self.FuncptrNames is None or self.FuncptrRets is None: return 0 string.memset(self.FuncptrNames, 0, 64 * MAX_FUNC_PTR_SIGS) @@ -7149,7 +7150,14 @@ class CallGenMixin: idx: t.CSizeT = t.CSizeT(self.FuncptrCount) * 64 # 槽位定长 64 字节,与 register_closure_name 一致用 strncpy 钳制防越界 string.strncpy(self.FuncptrNames + idx, name, 63) - self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_ty) + # ret_ty 是调用方 pool 分配的裸 LLVMType,1a 每文件 reset 后悬空; + # 深拷贝到 perm_pool(lookup_func_ptr_ret 跨文件读取) + ret_copy_fp: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), ret_ty) + if ret_copy_fp is not None: + self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_copy_fp) + else: + self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_ty) self.FuncptrCount += 1 return 0 @@ -7794,7 +7802,7 @@ class CallGenMixin: if self.GlobalFuncs is not None: return 1 size: t.CSizeT = GLOBAL_FUNC_MAX * HandlesExprCall.FuncEntry.__sizeof__() - self.GlobalFuncs = stdlib.malloc(size) + self.GlobalFuncs = CompilerFactory.get_perm_pool().alloc(size) if self.GlobalFuncs is None: return 0 string.memset(self.GlobalFuncs, 0, size) @@ -7840,7 +7848,7 @@ class CallGenMixin: # 深拷贝到 stdlib 区(TypeStr 跨文件永久存活) ts_len: t.CSizeT = string.strlen(ty_buf) - ts_copy: str = stdlib.malloc(ts_len + 1) + ts_copy: str = CompilerFactory.get_perm_pool().alloc(ts_len + 1) if ts_copy is None: return None string.strcpy(ts_copy, ty_buf) @@ -7869,7 +7877,7 @@ class CallGenMixin: # Name 深拷贝到 stdlib 区:全局表跨文件永久存活,pool 内名字会被 1b 每文件 # reset_to(mark1) 回收导致悬空(vtable 生成 find_func_global 后 strcmp 崩溃) name_len: t.CSizeT = string.strlen(name) - name_copy: str = stdlib.malloc(name_len + 1) + name_copy: str = CompilerFactory.get_perm_pool().alloc(name_len + 1) if name_copy is None: return 1 string.strcpy(name_copy, name) @@ -7921,7 +7929,7 @@ class CallGenMixin: if self.GlobalDefaults is not None: return 1 size: t.CSizeT = GLOBAL_DEFAULTS_MAX * HandlesExprCall.FuncDefaultsEntry.__sizeof__() - self.GlobalDefaults = stdlib.malloc(size) + self.GlobalDefaults = CompilerFactory.get_perm_pool().alloc(size) if self.GlobalDefaults is None: return 0 string.memset(self.GlobalDefaults, 0, size) @@ -7951,7 +7959,7 @@ class CallGenMixin: entry: HandlesExprCall.FuncDefaultsEntry | t.CPtr = (HandlesExprCall.FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) # Name 深拷贝到 stdlib(全局表跨文件永久存活) nm_len: t.CSizeT = string.strlen(name) - nm_copy: str = stdlib.malloc(nm_len + 1) + nm_copy: str = CompilerFactory.get_perm_pool().alloc(nm_len + 1) if nm_copy is None: return 1 string.strcpy(nm_copy, name) @@ -7961,9 +7969,9 @@ class CallGenMixin: entry.ParamCount = param_count # 提取常量快照到 stdlib if default_count > 0: - kinds: t.CInt | t.CPtr = stdlib.malloc(default_count * 4) - ints: t.CInt64T | t.CPtr = stdlib.malloc(default_count * 8) - strs: t.CSizeT | t.CPtr = stdlib.malloc(default_count * 8) + kinds: t.CInt | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 4) + ints: t.CInt64T | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 8) + strs: t.CSizeT | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 8) if kinds is not None and ints is not None and strs is not None: string.memset(kinds, 0, default_count * 4) string.memset(ints, 0, default_count * 8) @@ -7978,7 +7986,7 @@ class CallGenMixin: ints[di] = dcn.int_val if dcn.const_kind == ast.CONST_STR and dcn.str_val is not None: slen: t.CSizeT = string.strlen(dcn.str_val) - scopy: str = stdlib.malloc(slen + 1) + scopy: str = CompilerFactory.get_perm_pool().alloc(slen + 1) if scopy is not None: string.strcpy(scopy, dcn.str_val) strs[di] = t.CSizeT(scopy) diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index 4bd8994..b899896 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -16,6 +16,7 @@ import lib.core.Handles.HandlesEnum as HandlesEnum import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign import lib.core.Handles.HandlesImports as HandlesImports import lib.core.StubMerger as StubMerger +import lib.core.CompilerFactory as CompilerFactory # ============================================================ @@ -381,8 +382,8 @@ class ClassGenMixin: return # 懒初始化 if self.MethodMetaKeys is None: - self.MethodMetaKeys = stdlib.malloc(META_MAX * META_KEY_LEN) - self.MethodMetaFlags = stdlib.malloc(META_MAX * 4) + self.MethodMetaKeys = CompilerFactory.get_perm_pool().alloc(META_MAX * META_KEY_LEN) + self.MethodMetaFlags = CompilerFactory.get_perm_pool().alloc(META_MAX * 4) if self.MethodMetaKeys is None or self.MethodMetaFlags is None: return string.memset(self.MethodMetaKeys, 0, META_MAX * META_KEY_LEN) @@ -1241,8 +1242,13 @@ class ClassGenMixin: return 0 # Phase 1b 新注册的 union struct_ty 深拷贝到 stdlib(跨文件 reset 存活) - if trans._declare_only == 0: - union_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, union_ty) + # StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc 预注册 + # includes 时 declare_only=1,若仍用 pool struct_ty,reset_to(mark2) 后 + # entry.Ty 悬空 → 后续文件 opaque 扫描跨模块引用遍历悬空 fields 崩溃 + # (LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。 + if trans._declare_only == 0 or HandlesStruct.get_stdlib_alloc() != 0: + union_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), union_ty) if union_copy is not None: union_ty = union_copy @@ -1510,8 +1516,10 @@ class ClassGenMixin: # Phase 1b 新注册的 REnum struct_ty 深拷贝到 stdlib(跨文件 reset 存活, # 与 entry.Ty 保持同一指针,find_struct_by_type 匹配不失效) - if trans._declare_only == 0: - renum_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, renum_ty) + # StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:同 union 的悬空根因。 + if trans._declare_only == 0 or HandlesStruct.get_stdlib_alloc() != 0: + renum_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), renum_ty) if renum_copy is not None: renum_ty = renum_copy @@ -1580,10 +1588,10 @@ class ClassGenMixin: # 若用 pool 会被 reset_to(mark1) 回收导致悬空(LLVMType 的 match 在 # parser.vp 等后续文件翻译时访问已回收的表 → 崩溃)。 if self.RenumClassNames is None: - self.RenumClassNames = stdlib.malloc(8 * RENUM_CLASS_MAX) - self.RenumClassVariantCounts = stdlib.malloc(8 * RENUM_CLASS_MAX) - self.RenumVariantNames = stdlib.malloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) - self.RenumVariantTags = stdlib.malloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + self.RenumClassNames = CompilerFactory.get_perm_pool().alloc(8 * RENUM_CLASS_MAX) + self.RenumClassVariantCounts = CompilerFactory.get_perm_pool().alloc(8 * RENUM_CLASS_MAX) + self.RenumVariantNames = CompilerFactory.get_perm_pool().alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + self.RenumVariantTags = CompilerFactory.get_perm_pool().alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) if (self.RenumClassNames is None or self.RenumClassVariantCounts is None or self.RenumVariantNames is None or self.RenumVariantTags is None): self.RenumClassNames = None @@ -1604,9 +1612,15 @@ class ClassGenMixin: if self.RenumClassCount >= RENUM_CLASS_MAX: return - # 注册类名 + # 注册类名(strcpy 到 perm_pool:1a 每文件 reset 后 AST 裸指针悬空, + # _lookup_renum_variant/_is_renum_class_name 的 strcmp 会读毒化内存) idx: int = self.RenumClassCount - self.RenumClassNames[idx] = t.CSizeT(class_name) + cn_len_rv: t.CSizeT = string.strlen(class_name) + cn_buf_rv: str = CompilerFactory.get_perm_pool().alloc(cn_len_rv + 1) + if cn_buf_rv is None: + return + string.strcpy(cn_buf_rv, class_name) + self.RenumClassNames[idx] = t.CSizeT(cn_buf_rv) self.RenumClassVariantCounts[idx] = variant_count # 遍历 children,提取嵌套变体类名并注册 @@ -1632,7 +1646,13 @@ class ClassGenMixin: if vcd is None or vcd.name is None: continue name_offset: t.CSizeT = idx * RENUM_VARIANT_PER_CLASS + tag - self.RenumVariantNames[name_offset] = t.CSizeT(vcd.name) + # 变体名同样 strcpy 到 perm_pool(1a 每文件 reset 后 vcd.name 悬空) + vn_len_rv: t.CSizeT = string.strlen(vcd.name) + vn_buf_rv: str = CompilerFactory.get_perm_pool().alloc(vn_len_rv + 1) + if vn_buf_rv is None: + break + string.strcpy(vn_buf_rv, vcd.name) + self.RenumVariantNames[name_offset] = t.CSizeT(vn_buf_rv) self.RenumVariantTags[name_offset] = tag tag += 1 @@ -1693,11 +1713,12 @@ class ClassGenMixin: if payload_idx < 1: return - # 懒初始化表 + # 懒初始化表(perm_pool:1a 每文件 reset 后表本体不被回收) if self.RenumAliasClass is None: - self.RenumAliasClass = pool.alloc(8 * RENUM_ALIAS_MAX) - self.RenumAliasField = pool.alloc(8 * RENUM_ALIAS_MAX) - self.RenumAliasIndex = pool.alloc(8 * RENUM_ALIAS_MAX) + alias_pool_rfa: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + self.RenumAliasClass = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) + self.RenumAliasField = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) + self.RenumAliasIndex = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) if (self.RenumAliasClass is None or self.RenumAliasField is None or self.RenumAliasIndex is None): self.RenumAliasClass = None @@ -1710,18 +1731,29 @@ class ClassGenMixin: if self.RenumAliasCount >= RENUM_ALIAS_MAX: return - # 检查是否已注册(避免重复)— 用 CSizeT 指针值比较,避免 CSizeT→str 转换问题 + # 检查是否已注册(避免重复)— class 用 CSizeT 指针值比较(契约不变), + # field 槽位是 perm_pool 拷贝、key 是原指针,指针比较恒不相等 → 改 strcmp cls_key: t.CSizeT = t.CSizeT(class_name) - fld_key: t.CSizeT = t.CSizeT(field_name) for i in range(self.RenumAliasCount): cls_existing: t.CSizeT = self.RenumAliasClass[i] - fld_existing: t.CSizeT = self.RenumAliasField[i] - if cls_existing == cls_key and fld_existing == fld_key: - return + if cls_existing == cls_key: + fld_existing_ptr: str = (str | t.CPtr)( + t.CVoid(self.RenumAliasField[i], t.CPtr)) + if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: + return idx: int = self.RenumAliasCount self.RenumAliasClass[idx] = cls_key - self.RenumAliasField[idx] = fld_key + # field_name 存 AST 裸指针,1a 每文件 reset 后悬空;_lookup_renum_field_alias + # 用 strcmp 内容比较 → strcpy 到 perm_pool 不影响查找。 + # class_name 槽位保持原指针:_update_renum_alias_class_ptr 会统一为 + # entry.Name(perm_pool 拷贝),指针值比较契约不变。 + fn_len_rfa: t.CSizeT = string.strlen(field_name) + fn_buf_rfa: str = CompilerFactory.get_perm_pool().alloc(fn_len_rfa + 1) + if fn_buf_rfa is None: + return + string.strcpy(fn_buf_rfa, field_name) + self.RenumAliasField[idx] = t.CSizeT(fn_buf_rfa) self.RenumAliasIndex[idx] = t.CSizeT(payload_idx) self.RenumAliasCount = idx + 1 @@ -1784,7 +1816,7 @@ class ClassGenMixin: self.GenericClassSha1s = list[str](pool, 8) self.GenericClassFiles = list[str](pool, 8) if self.GenericClassTpParams is None: - self.GenericClassTpParams = stdlib.malloc(GENERIC_TEMPLATE_MAX * 8) + self.GenericClassTpParams = CompilerFactory.get_perm_pool().alloc(GENERIC_TEMPLATE_MAX * 8) if self.GenericClassTpParams is None: return string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8) @@ -1826,7 +1858,7 @@ class ClassGenMixin: tp_name: str = tp.get(ti) if tp_name is not None: total += string.strlen(tp_name) + 1 - buf: str = stdlib.malloc(total) + buf: str = CompilerFactory.get_perm_pool().alloc(total) if buf is None: return None buf[0] = '\0' @@ -2154,8 +2186,8 @@ class ClassGenMixin: if spec_key is None or spec_name is None: return if self.SpecKeys is None: - self.SpecKeys = stdlib.malloc(SPEC_CACHE_MAX * 8) - self.SpecNames = stdlib.malloc(SPEC_CACHE_MAX * 8) + self.SpecKeys = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8) + self.SpecNames = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8) if self.SpecKeys is None or self.SpecNames is None: return string.memset(self.SpecKeys, 0, SPEC_CACHE_MAX * 8) @@ -2166,8 +2198,8 @@ class ClassGenMixin: # 深拷贝字符串到 stdlib 区 key_len: t.CSizeT = string.strlen(spec_key) name_len: t.CSizeT = string.strlen(spec_name) - key_copy: str = stdlib.malloc(key_len + 1) - name_copy: str = stdlib.malloc(name_len + 1) + key_copy: str = CompilerFactory.get_perm_pool().alloc(key_len + 1) + name_copy: str = CompilerFactory.get_perm_pool().alloc(name_len + 1) if key_copy is None or name_copy is None: return string.strcpy(key_copy, spec_key) @@ -2481,13 +2513,14 @@ class ClassGenMixin: # _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表 # 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType # 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。 - ty_deep: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, existing.Ty) + ty_deep: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), existing.Ty) if ty_deep is not None: existing.Ty = ty_deep old_name: str = llvmlite.get_struct_name(existing.Ty) if old_name is not None: on_len: t.CSizeT = string.strlen(old_name) - on_copy: str = stdlib.malloc(on_len + 1) + on_copy: str = CompilerFactory.get_perm_pool().alloc(on_len + 1) if on_copy is not None: string.strcpy(on_copy, old_name) c.DerefAs(existing.Ty, llvmlite.LLVMType.Struct( @@ -2797,7 +2830,7 @@ class ClassGenMixin: type_name = name_buf if trans._declare_only == 0 and type_name is not None: tn_len: t.CSizeT = string.strlen(type_name) - tn_copy: str = stdlib.malloc(tn_len + 1) + tn_copy: str = CompilerFactory.get_perm_pool().alloc(tn_len + 1) if tn_copy is not None: string.strcpy(tn_copy, type_name) type_name = tn_copy @@ -2816,8 +2849,13 @@ class ClassGenMixin: # 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树 # 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty # 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。 - if trans._declare_only == 0: - st_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, struct_ty) + # StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1) + # 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 → + # 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields + # 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。 + if trans._declare_only == 0 or HandlesStruct.get_stdlib_alloc() != 0: + st_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), struct_ty) if st_copy is not None: struct_ty = st_copy type_name = llvmlite.get_struct_name(struct_ty) @@ -2857,7 +2895,7 @@ class ClassGenMixin: # find_struct(ParentName) strcmp 解引用 → 用 stdlib 拷贝保证跨文件存活 if HandlesStruct.get_stdlib_alloc() != 0: pn_len: t.CSizeT = string.strlen(parent_name) - pn_buf: str = stdlib.malloc(pn_len + 1) + pn_buf: str = CompilerFactory.get_perm_pool().alloc(pn_len + 1) if pn_buf is not None: string.strcpy(pn_buf, parent_name) entry.ParentName = pn_buf @@ -2865,7 +2903,7 @@ class ClassGenMixin: entry.ParentName = parent_name # 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活) if parents_count > 0 and parents_buf is not None: - HandlesStruct.set_parent_names(class_name, parents_buf, parents_count) + HandlesStruct.set_parent_names(pool, class_name, parents_buf, parents_count) # 添加字段信息 # 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始 @@ -3016,10 +3054,10 @@ class ClassGenMixin: # 1. 收集虚方法信息 # stdlib 分配:method_names_buf 存到 StructEntry.VTableMethods(跨文件永久存活), # 必须迁出 mb 池 reset 区间,否则 1b 每文件 reset_to(mark1) 会回收导致悬空。 - full_names_buf: t.CSizeT | t.CPtr = stdlib.malloc(8 * VTBL_METHOD_MAX) - method_names_buf: t.CSizeT | t.CPtr = stdlib.malloc(8 * VTBL_METHOD_MAX) + full_names_buf: t.CSizeT | t.CPtr = CompilerFactory.get_perm_pool().alloc(8 * VTBL_METHOD_MAX) + method_names_buf: t.CSizeT | t.CPtr = CompilerFactory.get_perm_pool().alloc(8 * VTBL_METHOD_MAX) # 记录每个方法的父类 SHA1(非 0=继承方法用父模块 SHA1 mangling,0=当前类方法) - method_parent_sha1s: t.CSizeT | t.CPtr = stdlib.malloc(8 * VTBL_METHOD_MAX) + method_parent_sha1s: t.CSizeT | t.CPtr = CompilerFactory.get_perm_pool().alloc(8 * VTBL_METHOD_MAX) if full_names_buf is None or method_names_buf is None or method_parent_sha1s is None: return 0 string.memset(full_names_buf, 0, 8 * VTBL_METHOD_MAX) @@ -3109,13 +3147,13 @@ class ClassGenMixin: def_class = anc break mro_i += 1 - pfname_buf: str = pool.alloc(128) + pfname_buf: str = CompilerFactory.get_perm_pool().alloc(128) if pfname_buf is not None: viperlib.snprintf(pfname_buf, 128, "%s.%s", def_class, pmethod_name) full_names_buf[method_count] = t.CSizeT(pfname_buf) # 复制方法名(短名) pmname_len: t.CSizeT = string.strlen(pmethod_name) - pmname_buf: str = stdlib.malloc(pmname_len + 1) + pmname_buf: str = CompilerFactory.get_perm_pool().alloc(pmname_len + 1) if pmname_buf is not None: string.strcpy(pmname_buf, pmethod_name) method_names_buf[method_count] = t.CSizeT(pmname_buf) @@ -3169,14 +3207,16 @@ class ClassGenMixin: if already_in_vt == 1: continue # 构造 full_name = "ClassName.method_name" - fname_buf: str = pool.alloc(128) + # 内容必须落在 perm_pool:1a(mark_only=1)收集的 VTableMethods 被 + # Phase 1b 依赖模块翻译跨文件读取,1a 每文件 reset 后 pool 裸字符串悬空 + fname_buf: str = CompilerFactory.get_perm_pool().alloc(128) if fname_buf is not None: viperlib.snprintf(fname_buf, 128, "%s.%s", class_name, mname) full_names_buf[method_count] = t.CSizeT(fname_buf) # 复制方法名 mname_len: t.CSizeT = string.strlen(mname) - mname_buf: str = stdlib.malloc(mname_len + 1) + mname_buf: str = CompilerFactory.get_perm_pool().alloc(mname_len + 1) if mname_buf is not None: string.strcpy(mname_buf, mname) method_names_buf[method_count] = t.CSizeT(mname_buf) diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 569f379..d4fd199 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -8,6 +8,7 @@ import stdlib import stdio import viperlib import w32.fileio as fileio +import lib.core.CompilerFactory as CompilerFactory import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar @@ -25,7 +26,6 @@ import lib.core.VLogger as VLogger import lib.Projectrans.Config as Config import lib.Projectrans.Utils as Utils - # ============================================================ # docstring 登记表容量(__doc__ 魔法属性支持) # @@ -3847,8 +3847,8 @@ class ExprGenMixin: if string.strlen(doc) == 0: return if self.DocKeys is None: - self.DocKeys = stdlib.malloc(DOC_MAX * DOC_KEY_SZ) - self.DocValues = stdlib.malloc(DOC_MAX * DOC_VAL_SZ) + self.DocKeys = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_KEY_SZ) + self.DocValues = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_VAL_SZ) if self.DocKeys is None or self.DocValues is None: return string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ) diff --git a/App/lib/core/Gen/Generator.vp b/App/lib/core/Gen/Generator.vp index adc997f..f6bef13 100644 --- a/App/lib/core/Gen/Generator.vp +++ b/App/lib/core/Gen/Generator.vp @@ -119,6 +119,7 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.GenericClassNames = None self.GenericClassNodes = None self.GenericClassSha1s = None + self.GenericClassFiles = None self.GenericClassTpParams = None self.VtFullNames = None self.VtMethodNames = None diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 5960216..1c86939 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -9,6 +9,7 @@ import stdio import sys import w32.fileio as fileio import lib.core.StubMerger as StubMerger +import lib.core.CompilerFactory as CompilerFactory import lib.core.Handles.HandlesType as HandlesType @@ -301,9 +302,9 @@ class ImportsGenMixin: return -1 # 懒初始化 if self.ReexportSha1s is None: - self.ReexportSha1s = stdlib.malloc(MAX_REEXPORT * 17) - self.ReexportFuncs = stdlib.malloc(MAX_REEXPORT * 64) - self.ReexportSrcs = stdlib.malloc(MAX_REEXPORT * 64) + self.ReexportSha1s = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 17) + self.ReexportFuncs = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 64) + self.ReexportSrcs = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 64) if self.ReexportSha1s is None or self.ReexportFuncs is None or self.ReexportSrcs is None: return -1 string.memset(self.ReexportSha1s, 0, MAX_REEXPORT * 17) diff --git a/App/lib/core/Gen/StmtGenMixin.vp b/App/lib/core/Gen/StmtGenMixin.vp index e8085db..6cf6bec 100644 --- a/App/lib/core/Gen/StmtGenMixin.vp +++ b/App/lib/core/Gen/StmtGenMixin.vp @@ -4,6 +4,7 @@ import ast import llvmlite import memhub import string +import stdlib import viperlib import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT @@ -18,8 +19,10 @@ import lib.core.Handles.HandlesIf as HandlesIf import lib.core.Handles.HandlesWhile as HandlesWhile import lib.core.Handles.HandlesFor as HandlesFor import lib.core.Handles.HandlesReturn as HandlesReturn +import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesMatch as HandlesMatch import lib.core.Handles.HandlesEnum as HandlesEnum +import lib.core.CompilerFactory as CompilerFactory import lib.Projectrans.Config as Config @@ -66,7 +69,10 @@ class StmtGenMixin: """ # --- 编译期 CIf 求值状态(原 HandlesIf._g_cif_eval_ok) --- - CifEvalOk: int + # 用 8 字节类型:Mixin 独立布局与 Generator 组合布局的字段对齐必须一致 + # (i32 首字段在组合布局中导致 EnumTable 相对偏移 4 vs 独立布局 8 的错位, + # this_adjust 无法同时修正,枚举表错位到相邻字段被覆盖——ASTCtx 丢失根因)。 + CifEvalOk: t.CSizeT # --- 枚举注册表(原 HandlesEnum._enum_table/_enum_count) --- EnumTable: HandlesEnum.EnumEntry | t.CPtr @@ -1089,12 +1095,17 @@ class StmtGenMixin: # ============================================================ def init_enum_table(self, pool: memhub.MemBuddy | t.CPtr) -> int: - """初始化枚举注册表,返回 1 成功""" + """初始化枚举注册表,返回 1 成功 + + 表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是 + 跨文件持久表,pool 分配会被 reset 回收导致 Phase B 读垃圾, + enum_count 出现 512 等垃圾值 / entry.Name 解引用崩溃)。 + """ if self.EnumTable is not None: return 1 entry_size: t.CSizeT = HandlesEnum.EnumEntry.__sizeof__() - self.EnumTable = pool.alloc(entry_size * ENUM_MAX) + self.EnumTable = CompilerFactory.get_perm_pool().alloc(entry_size * ENUM_MAX) if self.EnumTable is None: return 0 string.memset(self.EnumTable, 0, entry_size * ENUM_MAX) @@ -1141,21 +1152,35 @@ class StmtGenMixin: if entry is None: return None - # 分配成员数组 + # 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化) member_size: t.CSizeT = HandlesEnum.EnumMember.__sizeof__() - entry.Members = pool.alloc(member_size * ENUM_MEMBER_MAX) + entry.Members = CompilerFactory.get_perm_pool().alloc(member_size * ENUM_MEMBER_MAX) if entry.Members is None: return None string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX) - # 复制类名 + # 复制类名(永久池深拷贝) name_len: t.CSizeT = string.strlen(name) - name_buf: str = pool.alloc(name_len + 1) + name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, name) entry.Name = name_buf - entry.BaseTy = base_ty + # BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1(Phase2 mark2 后) + # 时 reset 回收悬空 → Phase B 枚举访问 ConstInt(悬空 BaseTy) 生成 void + # 值(DecoratorMarkerTest `store void 2` llc 报错根因)。Phase1 1a + # (StdlibAlloc=0,mark1 前)保持 pool 存活,无需拷贝。 + # 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接 + # self.StdlibAlloc(NoVTable 按本 Mixin 字段布局编译),走模块级委托。 + if HandlesStruct.get_stdlib_alloc() != 0: + base_ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), base_ty) + if base_ty_copy is not None: + entry.BaseTy = base_ty_copy + else: + entry.BaseTy = base_ty + else: + entry.BaseTy = base_ty entry.MemberCount = 0 self.EnumCount += 1 @@ -1182,9 +1207,9 @@ class StmtGenMixin: if me is None: return -1 - # 复制成员名 + # 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活) name_len: t.CSizeT = string.strlen(member_name) - name_buf: str = pool.alloc(name_len + 1) + name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, member_name) me.Name = name_buf diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 2e146cd..7cb79a2 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -8,6 +8,7 @@ import stdio import ast import hashtable import viperlib +import lib.core.CompilerFactory as CompilerFactory import lib.core.VLogger as VLogger import lib.core.Handles.HandlesStruct as HandlesStruct @@ -121,12 +122,22 @@ class StructGenMixin: return self.VisibleStructs.__contains__(name) def init_struct_table(self, pool: memhub.MemBuddy | t.CPtr) -> int: - """初始化结构体注册表,返回 1 成功""" + """初始化结构体注册表,返回 1 成功 + + StdlibAlloc=1 时表体分配在 perm_pool:1a 每文件 reset 后表本体 + 不被回收(否则首次 register_struct 若发生在文件级 mark 之后, + reset 会把 StructTable 连根回收,全局 find_struct 悬空)。 + """ if self.StructTable is not None: return 1 entry_size: t.CSizeT = HandlesStruct.StructEntry.__sizeof__() - self.StructTable = pool.alloc(entry_size * STRUCT_MAX) + table_pool: memhub.MemBuddy | t.CPtr = pool + if self.StdlibAlloc != 0: + table_pool = CompilerFactory.get_perm_pool() + if table_pool is None: + table_pool = pool + self.StructTable = table_pool.alloc(entry_size * STRUCT_MAX) if self.StructTable is None: return 0 string.memset(self.StructTable, 0, entry_size * STRUCT_MAX) @@ -182,22 +193,30 @@ class StructGenMixin: if entry is None: return None - # 分配字段数组(1b 阶段用 stdlib,避免 reset 回收后 Fields 悬空) + # 分配字段数组(1b 阶段用永久池,避免 reset 回收后 Fields 悬空) field_size: t.CSizeT = HandlesStruct.FieldEntry.__sizeof__() if self.StdlibAlloc != 0: - entry.Fields = stdlib.malloc(field_size * FIELD_MAX) + perm_sg: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if perm_sg is None: + perm_sg = pool + entry.Fields = perm_sg.alloc(field_size * FIELD_MAX) else: entry.Fields = pool.alloc(field_size * FIELD_MAX) if entry.Fields is None: return None string.memset(entry.Fields, 0, field_size * FIELD_MAX) - # 复制类名(统一 stdlib:1a 阶段注册的 entry 需跨 1b reset 存活。 - # 此前 1a 用 pool,1b 阶段 find_struct_by_module 的 strcmp 读悬空 - # Name 越界读 → G3 LinkedNode.prepend 挂起根因。stdlib 分配不随 - # pool reset 回收,多遍去重(Ty 比较)避免重复分配。) + # 复制类名(1b/Phase2(StdlibAlloc=1)用永久池跨 reset 存活; + # 1a(StdlibAlloc=0)用主 pool,mark1 前不回收同样存活。 + # 此前 1a 用 pool、1b 用 stdlib,find_struct_by_module 的 strcmp + # 曾读悬空 Name 越界 → G3 LinkedNode.prepend 挂起根因) + name_pool: memhub.MemBuddy | t.CPtr = pool + if self.StdlibAlloc != 0: + name_pool = CompilerFactory.get_perm_pool() + if name_pool is None: + name_pool = pool name_len: t.CSizeT = string.strlen(name) - name_buf: str = stdlib.malloc(name_len + 1) + name_buf: str = name_pool.alloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, name) entry.Name = name_buf @@ -206,10 +225,10 @@ class StructGenMixin: entry.FieldCount = 0 # 注册时直接设置 SHA1(避免 set_struct_sha1 跨模块同名时找错 entry) - # SHA1 同样统一 stdlib(跨 1a/1b 存活,防 strcmp 悬空) + # SHA1 同样按 StdlibAlloc 选分配源(跨 1a/1b 存活,防 strcmp 悬空) if sha1 is not None: sha1_len: t.CSizeT = string.strlen(sha1) - sha1_buf: str = stdlib.malloc(sha1_len + 1) + sha1_buf: str = name_pool.alloc(sha1_len + 1) if sha1_buf is not None: string.strcpy(sha1_buf, sha1) entry.ModuleSha1 = sha1_buf @@ -225,9 +244,14 @@ class StructGenMixin: entry: HandlesStruct.StructEntry | t.CPtr = self.find_struct(class_name) if entry is None: return 0 - # 复制 SHA1 字符串(统一 stdlib,跨 1a/1b 存活防 strcmp 悬空) + # 复制 SHA1 字符串(StdlibAlloc=1 用永久池,跨 1a/1b 存活防 strcmp 悬空) sha1_len: t.CSizeT = string.strlen(sha1) - sha1_buf: str = stdlib.malloc(sha1_len + 1) + sha1_pool: memhub.MemBuddy | t.CPtr = pool + if self.StdlibAlloc != 0: + sha1_pool = CompilerFactory.get_perm_pool() + if sha1_pool is None: + sha1_pool = pool + sha1_buf: str = sha1_pool.alloc(sha1_len + 1) if sha1_buf is None: return 0 string.strcpy(sha1_buf, sha1) @@ -269,11 +293,16 @@ class StructGenMixin: if fe is None: return -1 - # 复制字段名(统一 stdlib:1a 注册的字段名跨 1b reset 存活, + # 复制字段名(StdlibAlloc=1 用永久池跨 reset 存活, # 否则 lookup_field 的 strcmp(fe.Name, ...) 读悬空越界挂起, # 与 register_struct 的 Name 悬空同根因) + fname_pool: memhub.MemBuddy | t.CPtr = pool + if self.StdlibAlloc != 0: + fname_pool = CompilerFactory.get_perm_pool() + if fname_pool is None: + fname_pool = pool name_len: t.CSizeT = string.strlen(field_name) - name_buf: str = stdlib.malloc(name_len + 1) + name_buf: str = fname_pool.alloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, field_name) fe.Name = name_buf @@ -286,20 +315,24 @@ class StructGenMixin: # AnnotClassName 回退、_apply_struct_defaults 的 build_gep_struct 用 fe.Ty # 作 GEP 结果类型)解引用悬空内存 → 崩溃点漂移(json/string/builtins 之间)。 if self.StdlibAlloc != 0: - ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, field_ty) + dp_pool_sg: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if dp_pool_sg is None: + dp_pool_sg = pool + ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(dp_pool_sg, field_ty) if ty_copy is not None: fe.Ty = ty_copy else: fe.Ty = field_ty if annot_class_name is not None: annot_len: t.CSizeT = string.strlen(annot_class_name) - annot_buf: str = stdlib.malloc(annot_len + 1) + annot_buf: str = CompilerFactory.get_perm_pool().alloc(annot_len + 1) if annot_buf is not None: string.strcpy(annot_buf, annot_class_name) fe.AnnotClassName = annot_buf # DefaultVal:跨文件结构体构造(_translate_struct_ctor→_apply_struct_defaults) # 在调用方文件翻译时读取字段默认值,原 pool AST 已 reset 回收 → 常量深拷贝 - fe.DefaultVal = self._deep_copy_default_ast(default_val) + # 到 perm_pool(1a 每文件 reset 后 DefaultVal 同样必须跨文件存活) + fe.DefaultVal = self._deep_copy_default_ast(dp_pool_sg, default_val) else: fe.Ty = field_ty fe.AnnotClassName = annot_class_name @@ -308,8 +341,9 @@ class StructGenMixin: struct_entry.FieldCount = idx + 1 return idx - def _deep_copy_default_ast(self, default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr: - """深拷贝字段默认值 AST 到 stdlib(仅常量节点),跨 1b reset 存活。 + def _deep_copy_default_ast(self, pool: memhub.MemBuddy | t.CPtr, + default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr: + """深拷贝字段默认值 AST 到永久池(仅常量节点),跨 1b reset 存活。 字段默认值(fe.DefaultVal)除定义文件内 _generate_before_init 使用外, 跨文件结构体构造(_translate_struct_ctor → _apply_struct_defaults)会在 @@ -322,12 +356,12 @@ class StructGenMixin: if default_val is None: return None if default_val.kind() == ast.ASTKind.List: - return self._deep_copy_list_ast(default_val) + return self._deep_copy_list_ast(pool, default_val) if default_val.kind() != ast.ASTKind.Constant: return None src_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(default_val) dst_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)( - stdlib.malloc(ast.Constant.__sizeof__())) + pool.alloc(ast.Constant.__sizeof__())) if dst_c is None: return None string.memset(dst_c, 0, ast.Constant.__sizeof__()) @@ -346,22 +380,23 @@ class StructGenMixin: dst_c.float_val = src_c.float_val if src_c.str_val is not None: s_len: t.CSizeT = string.strlen(src_c.str_val) - s_copy: str = stdlib.malloc(s_len + 1) + s_copy: str = pool.alloc(s_len + 1) if s_copy is not None: string.strcpy(s_copy, src_c.str_val) dst_c.str_val = s_copy return (ast.AST | t.CPtr)(dst_c) - def _deep_copy_list_ast(self, default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr: - """深拷贝 List 节点(列表字面量默认值)到 stdlib,跨 1b reset 存活。 + def _deep_copy_list_ast(self, pool: memhub.MemBuddy | t.CPtr, + default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr: + """深拷贝 List 节点(列表字面量默认值)到永久池,跨 1b reset 存活。 - elts 容器手动在 stdlib 构造(list 的 __pool__ 置 None:深拷贝的 elts + elts 容器手动在永久池构造(list 的 __pool__ 置 None:深拷贝的 elts 只被只读访问 get/__len__/迭代,不 append,不会触发 pool 扩容)。 元素递归调用 _deep_copy_default_ast(支持 Constant 与嵌套 List)。 """ src_l: ast.List | t.CPtr = (ast.List | t.CPtr)(default_val) dst_l: ast.List | t.CPtr = (ast.List | t.CPtr)( - stdlib.malloc(ast.List.__sizeof__())) + pool.alloc(ast.List.__sizeof__())) if dst_l is None: return None string.memset(dst_l, 0, ast.List.__sizeof__()) @@ -380,14 +415,14 @@ class StructGenMixin: if src_elts_l is not None: cnt_l: t.CSizeT = len(src_elts_l) lst_copy_l: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)( - stdlib.malloc(list[ast.AST | t.CPtr].__sizeof__())) + pool.alloc(list[ast.AST | t.CPtr].__sizeof__())) if lst_copy_l is not None: string.memset(lst_copy_l, 0, list[ast.AST | t.CPtr].__sizeof__()) lst_copy_l.__count__ = cnt_l lst_copy_l.__capacity__ = cnt_l lst_copy_l.__elem_size__ = 8 lst_copy_l.__pool__ = None - data_l: t.CVoid | t.CPtr = stdlib.malloc(cnt_l * 8) + data_l: t.CVoid | t.CPtr = pool.alloc(cnt_l * 8) if data_l is not None: string.memset(data_l, 0, cnt_l * 8) lst_copy_l.__data__ = data_l @@ -395,7 +430,7 @@ class StructGenMixin: for li_l in range(cnt_l): elem_src_l: ast.AST | t.CPtr = src_elts_l.get(li_l) if elem_src_l is not None: - elem_dst_l: ast.AST | t.CPtr = self._deep_copy_default_ast(elem_src_l) + elem_dst_l: ast.AST | t.CPtr = self._deep_copy_default_ast(pool, elem_src_l) if elem_dst_l is not None: slot_l: ast.AST | t.CPtr = (ast.AST | t.CPtr)( t.CVoid(t.CUInt64T(data_l) + li_l * 8, t.CPtr)) @@ -873,9 +908,10 @@ class StructGenMixin: entry.ParentName = parent_name return 1 - def set_parent_names(self, name: str, names_ptr: t.CSizeT | t.CPtr, + def set_parent_names(self, pool: memhub.MemBuddy | t.CPtr, + name: str, names_ptr: t.CSizeT | t.CPtr, count: int) -> int: - """设置结构体的父类名数组(stdlib 深拷贝,跨文件永久存活)""" + """设置结构体的父类名数组(永久池深拷贝,跨文件永久存活)""" if name is None: return 0 entry: HandlesStruct.StructEntry | t.CPtr = self.find_struct(name) @@ -887,7 +923,12 @@ class StructGenMixin: return 1 if count > PARENT_MAX: count = PARENT_MAX - arr: t.CSizeT | t.CPtr = stdlib.malloc(8 * count) + pn_pool: memhub.MemBuddy | t.CPtr = pool + if self.StdlibAlloc != 0: + pn_pool = CompilerFactory.get_perm_pool() + if pn_pool is None: + pn_pool = pool + arr: t.CSizeT | t.CPtr = pn_pool.alloc(8 * count) if arr is None: return 0 for i in range(count): @@ -897,7 +938,7 @@ class StructGenMixin: src: str = (str | t.CPtr)(t.CVoid(addr, t.CPtr)) if src is not None: slen: t.CSizeT = string.strlen(src) - dup: str = stdlib.malloc(slen + 1) + dup: str = pn_pool.alloc(slen + 1) if dup is not None: string.strcpy(dup, src) arr[i] = t.CSizeT(dup) diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 318dc18..9a23534 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -9,6 +9,7 @@ import sys import stdlib import viperlib import w32.fileio as fileio +import lib.core.CompilerFactory as CompilerFactory import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesImports as HandlesImports @@ -127,7 +128,7 @@ class TypeGenMixin: if self.TraceFrames is not None: return 1 entry_size: t.CSizeT = HandlesType.TraceFrame.__sizeof__() - self.TraceFrames = stdlib.malloc(entry_size * TRACE_MAX) + self.TraceFrames = CompilerFactory.get_perm_pool().alloc(entry_size * TRACE_MAX) if self.TraceFrames is None: return 0 string.memset(self.TraceFrames, 0, entry_size * TRACE_MAX) diff --git a/App/lib/core/Handles/HandlesMatch.vp b/App/lib/core/Handles/HandlesMatch.vp index bb4200b..4453bac 100644 --- a/App/lib/core/Handles/HandlesMatch.vp +++ b/App/lib/core/Handles/HandlesMatch.vp @@ -86,6 +86,12 @@ class MatchHandle(HandlesBase.Mixin): subject_node: ast.AST | t.CPtr = m_node.subject if subject_node is None: return 0 + # 序列匹配(match (a, b): case (x, y): ...)走独立路径: + # Tuple/List subject 无法用 translate_value 求值(会返回 None), + # 提前分派到 _handle_sequence_match,避免 fail-fast 误报。 + sk_seq: int = subject_node.kind() + if sk_seq == ast.ASTKind.Tuple or sk_seq == ast.ASTKind.List: + return self._handle_sequence_match(m_node, subject_node) subject_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, trans.Module, subject_node, trans._funcs, trans._func_count, trans) @@ -617,6 +623,361 @@ class MatchHandle(HandlesBase.Mixin): trans.SymTab, bind_name, elem_ptr) j = j + 1 + # ============================================================ + # _seq_cmp_eq - 序列匹配元素相等比较 + # + # 对齐 lhs/rhs(指针统一 ptrtoint i64,整数位宽统一到较宽者)后 + # 生成 icmp eq,返回 i1 条件。 + # ============================================================ + def _seq_cmp_eq(self, builder: llvmlite.IRBuilder | t.CPtr, + lhs: llvmlite.Value | t.CPtr, + rhs: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr: + """对齐 lhs/rhs 后生成 icmp eq(指针统一 ptrtoint i64)""" + if lhs is None or rhs is None or builder is None: + return None + pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool + if HandlesExpr.is_ptr_type(lhs.Ty) != 0 \ + or HandlesExpr.is_ptr_type(rhs.Ty) != 0: + i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + if HandlesExpr.is_ptr_type(lhs.Ty) != 0: + lhs = llvmlite.build_ptrtoint(builder, lhs, i64_ty) + if HandlesExpr.is_ptr_type(rhs.Ty) != 0: + rhs = llvmlite.build_ptrtoint(builder, rhs, i64_ty) + lb: int = HandlesExpr.get_llvm_type_bits(lhs.Ty) + rb: int = HandlesExpr.get_llvm_type_bits(rhs.Ty) + if lb != rb and lb != 0 and rb != 0: + if lb < rb: + if lb == 1: + lhs = llvmlite.build_zext(builder, lhs, rhs.Ty) + else: + lhs = llvmlite.build_sext(builder, lhs, rhs.Ty) + else: + if rb == 1: + rhs = llvmlite.build_zext(builder, rhs, lhs.Ty) + else: + rhs = llvmlite.build_sext(builder, rhs, lhs.Ty) + return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, lhs, rhs) + + # ============================================================ + # _handle_sequence_match - 序列匹配(match (a, b): case (x, y): ...) + # + # subject 是 Tuple/List、case 是 MatchSequence 时走此路径(对齐 Python + # 语义,按声明顺序取首个全部元素匹配且 guard 为真的 case 执行): + # 元素 pattern: + # MatchValue v → subject[i] == v + # MatchAs(pattern=None) → _ 通配(恒真) + # MatchStar → 剩余元素通配(恒真) + # IR 结构(check/body 链): + # entry → check0 + # check_i: 匹配条件 → body_i(不满足 → check_{i+1}) + # body_i: → end(c.NoBreak 时 fallthrough 到下一 case body) + # 全不匹配 → default → end + # ============================================================ + def _handle_sequence_match(self, node: ast.Match | t.CPtr, + subject_node: ast.AST | t.CPtr) -> int: + trans: HT.Translator | t.CPtr = self.Trans + pool: memhub.MemBuddy | t.CPtr = trans.Pool + builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + func: llvmlite.Function | t.CPtr = trans._cur_func + if builder is None or func is None: + return 0 + + # 1. 求值 subject 各元素 + elts: list[ast.AST | t.CPtr] | t.CPtr = None + if subject_node.kind() == ast.ASTKind.Tuple: + st: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(subject_node) + if st is not None: + elts = st.elts + elif subject_node.kind() == ast.ASTKind.List: + sl2: ast.List | t.CPtr = (ast.List | t.CPtr)(subject_node) + if sl2 is not None: + elts = sl2.elts + if elts is None: + return 0 + ecount: t.CSizeT = len(elts) + if ecount == 0: + return 0 + subj_vals: t.CSizeT | t.CPtr = pool.alloc(8 * ecount) + if subj_vals is None: + return 0 + for ei in range(ecount): + elt: ast.AST | t.CPtr = elts.get(ei) + sv: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, trans.Module, elt, + trans._funcs, trans._func_count, trans) + if sv is None: + HandlesType.fatal_error( + elt, f"match 序列元素 {ei} 翻译失败", "TypeError") + subj_vals[ei] = t.CSizeT(sv) + + # 2. 创建 default/end BB + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + name_buf: str = pool.alloc(32) + if name_buf is None: + return 0 + viperlib.snprintf(name_buf, 32, "match.seq.default.%d", cnt) + default_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "match.seq.end.%d", cnt) + end_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, name_buf) + + # 3. 第一遍:收集 case 信息(check/body BB、default 标记、nobreak、guard) + cases_list: list[ast.AST | t.CPtr] | t.CPtr = node.cases + if cases_list is None: + return 0 + case_count: t.CSizeT = len(cases_list) + check_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES) + body_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) + guard_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES) + if check_arr is None or body_arr is None or def_arr is None \ + or nb_arr is None or guard_arr is None: + return 0 + collected: int = 0 + has_default: int = 0 + first_check: int = -1 + i: int = 0 + while i < case_count: + case_node: ast.AST | t.CPtr = cases_list.get(i) + if case_node is None or case_node.kind() != ast.ASTKind.MatchCase: + i = i + 1 + continue + mc: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(case_node) + pat: ast.AST | t.CPtr = mc.pattern + is_def: int = 0 + if pat is not None: + pk: int = pat.kind() + if pk == ast.ASTKind.MatchAs: + ma0: ast.MatchAs | t.CPtr = (ast.MatchAs | t.CPtr)(pat) + if ma0.pattern is None: + is_def = 1 + elif pk != ast.ASTKind.MatchSequence: + # fail-fast:序列匹配只接受 MatchSequence / 顶层 _ 通配 + HandlesType.fatal_error( + pat, "序列匹配 case 只支持 (元素) 或 _ 模式", "TypeError") + if is_def == 1: + has_default = 1 + check_arr[collected] = 0 + body_arr[collected] = t.CSizeT(default_bb) + else: + if collected >= MAX_MATCH_CASES: + break + viperlib.snprintf(name_buf, 32, "match.seq.check.%d.%d", cnt, i) + cbb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "match.seq.body.%d.%d", cnt, i) + bbb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, name_buf) + check_arr[collected] = t.CSizeT(cbb) + body_arr[collected] = t.CSizeT(bbb) + if first_check < 0: + first_check = collected + body_list: list[ast.AST | t.CPtr] | t.CPtr = mc.children + nb: int = self._has_no_break(body_list) + def_arr[collected] = is_def + nb_arr[collected] = nb + guard_arr[collected] = t.CSizeT(mc.guard) + collected = collected + 1 + i = i + 1 + + # 4. 入口 → 第一个 check(或 default/end) + if first_check >= 0: + head_bb: llvmlite.BasicBlock | t.CPtr = ( + llvmlite.BasicBlock | t.CPtr)( + t.CVoid(check_arr[first_check], t.CPtr)) + llvmlite.build_br(builder, head_bb) + elif has_default == 1: + llvmlite.build_br(builder, default_bb) + else: + llvmlite.build_br(builder, end_bb) + + # 5. 发射 check 链 + ci: int = 0 + while ci < collected: + if def_arr[ci] == 1: + ci = ci + 1 + continue + cbb2: llvmlite.BasicBlock | t.CPtr = ( + llvmlite.BasicBlock | t.CPtr)( + t.CVoid(check_arr[ci], t.CPtr)) + bbb2: llvmlite.BasicBlock | t.CPtr = ( + llvmlite.BasicBlock | t.CPtr)( + t.CVoid(body_arr[ci], t.CPtr)) + llvmlite.position_at_end(builder, cbb2) + + # 计算匹配条件(元素比较 && guard) + cond: llvmlite.Value | t.CPtr = None + cnode: ast.AST | t.CPtr = cases_list.get(ci) + mc2: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(cnode) + ms: ast.MatchSequence | t.CPtr = ( + ast.MatchSequence | t.CPtr)(mc2.pattern) + pats: list[ast.AST | t.CPtr] | t.CPtr = None + if ms is not None: + pats = ms.patterns + pcount: t.CSizeT = 0 + if pats is not None: + pcount = len(pats) + if pcount != ecount: + # 元素数量不匹配 → 恒假(对齐 Python 语义) + cond = llvmlite.const_int(pool, 1, 0) + else: + for pi in range(ecount): + pat1: ast.AST | t.CPtr = pats.get(pi) + if pat1 is None: + continue + pk1: int = pat1.kind() + if pk1 == ast.ASTKind.MatchValue: + mv: ast.MatchValue | t.CPtr = ( + ast.MatchValue | t.CPtr)(pat1) + pv: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, trans.Module, mv.value, + trans._funcs, trans._func_count, trans) + if pv is None: + HandlesType.fatal_error( + pat1, f"序列模式元素 {pi} 翻译失败", "TypeError") + sv1: llvmlite.Value | t.CPtr = ( + llvmlite.Value | t.CPtr)( + t.CVoid(subj_vals[pi], t.CPtr)) + c1: llvmlite.Value | t.CPtr = self._seq_cmp_eq( + builder, sv1, pv) + if c1 is None: + HandlesType.fatal_error( + pat1, "序列模式元素比较失败", "TypeError") + if cond is None: + cond = c1 + else: + cond = llvmlite.build_and(builder, cond, c1) + elif pk1 == ast.ASTKind.MatchAs: + ma2: ast.MatchAs | t.CPtr = ( + ast.MatchAs | t.CPtr)(pat1) + if ma2.pattern is None: + continue # _ 通配:恒真 + if ma2.name is not None: + # fail-fast:序列模式变量绑定暂不支持 + HandlesType.fatal_error( + pat1, "序列模式变量绑定暂不支持", "NotImplementedError") + elif pk1 == ast.ASTKind.MatchStar: + continue # 剩余元素通配:恒真 + else: + HandlesType.fatal_error( + pat1, "序列模式不支持的 pattern 类型", "TypeError") + # guard(若有):匹配成立且 guard 非零 + g_addr: t.CSizeT = guard_arr[ci] + if g_addr != 0: + gnode: ast.AST | t.CPtr = ( + ast.AST | t.CPtr)(t.CVoid(g_addr, t.CPtr)) + gv: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, trans.Module, gnode, + trans._funcs, trans._func_count, trans) + if gv is None: + HandlesType.fatal_error( + gnode, "match guard 翻译失败", "TypeError") + gbits: int = HandlesExpr.get_llvm_type_bits(gv.Ty) + if gbits == 1: + g1: llvmlite.Value | t.CPtr = gv + else: + gzero: llvmlite.Value | t.CPtr = llvmlite.const_int( + pool, gbits if gbits > 0 else 32, 0) + g1 = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, gv, gzero) + if cond is None: + cond = g1 + else: + cond = llvmlite.build_and(builder, cond, g1) + if cond is None: + cond = llvmlite.const_int(pool, 1, 1) + + # 下一个 check(或 default/end) + nxt: llvmlite.BasicBlock | t.CPtr = None + nj: int = ci + 1 + while nj < collected and def_arr[nj] == 1: + nj = nj + 1 + if nj < collected: + nxt = (llvmlite.BasicBlock | t.CPtr)( + t.CVoid(check_arr[nj], t.CPtr)) + elif has_default == 1: + nxt = default_bb + else: + nxt = end_bb + llvmlite.build_cond_br(builder, cond, bbb2, nxt) + ci = ci + 1 + + # 6. 翻译 case body + ci = 0 + while ci < collected: + if def_arr[ci] == 1: + ci = ci + 1 + continue + bbb3: llvmlite.BasicBlock | t.CPtr = ( + llvmlite.BasicBlock | t.CPtr)( + t.CVoid(body_arr[ci], t.CPtr)) + llvmlite.position_at_end(builder, bbb3) + cnode2: ast.AST | t.CPtr = cases_list.get(ci) + mc3: ast.MatchCase | t.CPtr = ( + ast.MatchCase | t.CPtr)(cnode2) + HandlesVar.enter_scope(trans.SymTab, SCOPE_MATCH) + body_list2: list[ast.AST | t.CPtr] | t.CPtr = mc3.children + if body_list2 is not None: + bc: t.CSizeT = len(body_list2) + bi: int = 0 + while bi < bc: + stmt: ast.AST | t.CPtr = body_list2.get(bi) + if stmt is not None: + HandlesBody.translate_stmt(trans, stmt) + bi = bi + 1 + HandlesVar.exit_scope(trans.SymTab) + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + if nb_arr[ci] == 0: + llvmlite.build_br(builder, end_bb) + else: + # c.NoBreak:fallthrough 到下一个 case body(跳过模式检查) + nj2: int = ci + 1 + while nj2 < collected and def_arr[nj2] == 1: + nj2 = nj2 + 1 + if nj2 < collected: + nbb: llvmlite.BasicBlock | t.CPtr = ( + llvmlite.BasicBlock | t.CPtr)( + t.CVoid(body_arr[nj2], t.CPtr)) + llvmlite.build_br(builder, nbb) + elif has_default == 1: + llvmlite.build_br(builder, default_bb) + else: + llvmlite.build_br(builder, end_bb) + ci = ci + 1 + + # 7. default 块 + llvmlite.position_at_end(builder, default_bb) + if has_default == 1: + di: int = 0 + while di < collected: + if def_arr[di] == 1: + dnode: ast.AST | t.CPtr = cases_list.get(di) + if dnode is not None: + dmc: ast.MatchCase | t.CPtr = ( + ast.MatchCase | t.CPtr)(dnode) + HandlesVar.enter_scope(trans.SymTab, SCOPE_MATCH) + dbody: list[ast.AST | t.CPtr] | t.CPtr = dmc.children + if dbody is not None: + dn: t.CSizeT = len(dbody) + dj: int = 0 + while dj < dn: + stmt: ast.AST | t.CPtr = dbody.get(dj) + if stmt is not None: + HandlesBody.translate_stmt(trans, stmt) + dj = dj + 1 + HandlesVar.exit_scope(trans.SymTab) + break + di = di + 1 + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, end_bb) + + # 8. 定位到 end + llvmlite.position_at_end(builder, end_bb) + return 0 + # ============================================================ # _handle_simple_match - 非 REnum 路径 # diff --git a/App/lib/core/Handles/HandlesStruct.vp b/App/lib/core/Handles/HandlesStruct.vp index 154e31e..aea8c4f 100644 --- a/App/lib/core/Handles/HandlesStruct.vp +++ b/App/lib/core/Handles/HandlesStruct.vp @@ -449,13 +449,14 @@ def set_parent_name(name: str, parent_name: str) -> int: return 0 -def set_parent_names(name: str, names_ptr: t.CSizeT | t.CPtr, +def set_parent_names(pool: memhub.MemBuddy | t.CPtr, + name: str, names_ptr: t.CSizeT | t.CPtr, count: int) -> int: - """设置结构体的父类名数组(stdlib 深拷贝,委托到 gen.set_parent_names)""" + """设置结构体的父类名数组(永久池深拷贝,委托到 gen.set_parent_names)""" 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.set_parent_names(name, names_ptr, count) + return g.set_parent_names(pool, name, names_ptr, count) return 0 diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index 0f47e2b..2fc31a2 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -19,6 +19,7 @@ import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.IncludesScanner as IncludesScanner import lib.core.StubMerger as StubMerger import lib.core.Gen.Generator as GenGenerator +import lib.core.CompilerFactory as CompilerFactory import lib.core.BuildPipeline as BuildPipeline import lib.Projectrans.Config as Config import lib.StubGen.Converter as StubConverter @@ -202,6 +203,149 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, return class_count +# ============================================================ +# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记 +# +# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。 +# 逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。 +# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部 +# 98 个 includes 导致 AST 驻留 mark1 前把 pool 推到 OOM +# (NewAstTest [52/68] pool 耗尽根因:1a-pre 已 parse 全部驻留, +# 预注册再全量 parse 等于翻倍)。 +# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件); +# 漏报有害(泛型模板未预注册 → 悬空复发),模式保守匹配行内单行形式 +# (TPV 源码泛型类均为单行 `class X[T]:`,grep 确认)。 +# ============================================================ +def _BufHasGenericClassMarker(buf: bytes, blen: t.CSizeT) -> int: + """文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0""" + if buf is None or blen <= 6: + return 0 + pos: t.CSizeT = 0 + while pos + 5 < blen: + if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' \ + and buf[pos + 3] == 's' and buf[pos + 4] == 's': + # class 前是词边界(行首或非标识符字符) + prev_ok: int = 1 + if pos > 0: + pc: t.CChar = buf[pos - 1] + if (pc >= 'a' and pc <= 'z') or (pc >= 'A' and pc <= 'Z') \ + or (pc >= '0' and pc <= '9') or pc == '_': + prev_ok = 0 + if prev_ok != 0: + p: t.CSizeT = pos + 5 + # 跳过空白 + while p < blen and (buf[p] == ' ' or buf[p] == '\t'): + p += 1 + # 读类名标识符 + if p < blen and ((buf[p] >= 'a' and buf[p] <= 'z') + or (buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'): + while p < blen and ((buf[p] >= 'a' and buf[p] <= 'z') + or (buf[p] >= 'A' and buf[p] <= 'Z') + or (buf[p] >= '0' and buf[p] <= '9') + or buf[p] == '_'): + p += 1 + # 类名后非空白是 '[' → 泛型类 + while p < blen and (buf[p] == ' ' or buf[p] == '\t'): + p += 1 + if p < blen and buf[p] == '[': + return 1 + pos += 1 + else: + pos += 1 + return 0 + + +# ============================================================ +# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前) +# +# 背景:泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/ +# GenericClassFiles + 模板节点)是懒分配的跨文件持久表,首次注册必须落在 +# mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2), +# mark2 > mark1)存活。现有 1a-pre-generic 只注册"可达"泛型文件;FuncTest +# 这类可达集合无泛型的项目,模板要等 Phase2 Phase A-pre-inc(mark2 之后) +# 翻译全部 includes 时才注册 → reset 回收表与节点 → _find_generic_template +# 遍历悬空内存 strcmp 崩溃。 +# 本函数先文本扫描(_BufHasGenericClassMarker,不 parse、不驻留)筛选出 +# 含泛型类的文件(全量 parse 会把 98 个 includes 的 AST 驻留 mark1 前 → +# NewAstTest [52/68] pool 耗尽 OOM),仅对命中的文件用**永久池(perm_pool)** +# 解析 → 模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool。 +# 重复注册由 _register_generic_template 内部查重跳过。 +# ============================================================ +def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr, + result: IncludesScanner.ScanResult | t.CPtr) -> int: + """预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量""" + if result is None or mb is None: + return 0 + perm: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if perm is None: + return 0 + p1e_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + handled_count: int = 0 + for i in range(result.Count): + entry_addr_pg: t.CUInt64T = t.CUInt64T(result.Entries) + i * p1e_size + entry_pg: IncludesScanner.FileEntry | t.CPtr = ( + IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_pg, t.CPtr)) + if entry_pg is None or entry_pg.Sha1 is None: + continue + # 读取文件 + file_path_pg: str = entry_pg.Path + f_pg: fileio.File | t.CPtr = fileio.File(file_path_pg, fileio.MODE.R) + if f_pg.closed: + continue + src_buf_pg: bytes = stdlib.malloc(SRC_BUF_SIZE) + if src_buf_pg is None: + f_pg.close() + continue + bytes_read_pg: LONG = f_pg.read_all(src_buf_pg, SRC_BUF_SIZE) + f_pg.close() + if bytes_read_pg <= 0: + stdlib.free(src_buf_pg) + continue + if bytes_read_pg < SRC_BUF_SIZE: + src_buf_pg[bytes_read_pg] = 0 + else: + src_buf_pg[SRC_BUF_SIZE - 1] = 0 + # 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM) + if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0: + stdlib.free(src_buf_pg) + continue + # 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活) + lx_pg: ast.Lexer | t.CPtr = ast.new_lexer(perm) + if lx_pg is None: + stdlib.free(src_buf_pg) + continue + ast._lexer_init(lx_pg, src_buf_pg, perm) + tokens_pg: ast.Token | t.CPtr = ast.tokenize(lx_pg) + tree_pg: ast.AST | t.CPtr = ast.parse_tokens(perm, tokens_pg) + if tree_pg is None: + stdlib.free(src_buf_pg) + continue + # 遍历顶层 ClassDef,注册泛型模板(type_params 非 None) + ch_pg: list[ast.AST | t.CPtr] | t.CPtr = tree_pg.children + if ch_pg is not None: + cn_pg: t.CSizeT = len(ch_pg) + for ci_pg in range(cn_pg): + child_pg: ast.AST | t.CPtr = ch_pg.get(ci_pg) + if child_pg is None or child_pg.kind() != ast.ASTKind.ClassDef: + continue + cd_pg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child_pg) + if cd_pg is None or cd_pg.name is None: + continue + tp_pg: list[str] | t.CPtr = cd_pg.type_params + if tp_pg is None: + continue + HandlesType.set_current_file(file_path_pg) + HandlesClassDef._register_generic_template(perm, cd_pg, entry_pg.Sha1) + handled_count += 1 + stdlib.free(src_buf_pg) + if handled_count > 0: + fb_gpre2: str = VLogger.fmt_buf() + if fb_gpre2 is not None: + viperlib.snprintf(fb_gpre2, 1024, "mark1 前永久池预注册 %d 个 includes 泛型类模板", handled_count) + VLogger.info(fb_gpre2, "Phase1") + return handled_count + + # ============================================================ # _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1 # @@ -1146,9 +1290,37 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(gen_tr._nonlocal_names) stdlib.free(gen_src) + # ============================================================ + # Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板 + # + # 1a-pre-generic 只覆盖"可达"泛型文件;非可达泛型文件(如 FuncTest + # 场景的 linkedlist/llvmlite/_list)的模板要等 Phase2 Phase A-pre-inc + # (mark2 之后)才注册 → reset 回收表/节点 → _find_generic_template + # strcmp 悬空崩溃。此处对全部 includes 轻量预注册,使模板表与节点 + # 都落在 mark1 之前(mark2 > mark1,reset_to(mark2) 不回收)。 + # ============================================================ + _PreRegisterAllGenericTemplates(mb, result) + + # ============================================================ + # Phase 1a: 注册 struct/enum/union(mark1 前,全量 includes) + # + # OOM 治理:1a 对全部 includes parse 完整 AST(主 pool 分配),若 + # 驻留到 mark1(循环后)会累积 98 文件 × 每文件 AST 树 → 1GB 主池 + # 耗尽(NewAstTest alloc 失败 0xBE 根因)。现改为: + # 1) StdlibAlloc=1:1a 注册的跨文件持久数据(StructTable 表、 + # Fields/Name/ModuleSha1、struct_ty 深拷贝、枚举/Renum/泛型 + # 模板等)全部落在 perm_pool(永不 reset),不占主 pool; + # 2) 每文件翻译完 reset_to(文件级 mark):仅回收该文件的 AST/ + # lexer/token/IR 等瞬时数据,注册数据不受影响。 + # mark1 仍在 1a 结束后打,1b 每文件 reset_to(mark1) 语义不变。 + # ============================================================ + HandlesStruct.set_stdlib_alloc(1) + # 遍历文件:优先使用拓扑顺序,回退到字母序 iter_count: int = topo_count if topo_count > 0 else result.Count for i in range(iter_count): + # 每文件级 mark:翻译完回收该文件 AST(注册数据在 perm_pool) + p1a_mark: t.CSizeT = mb.mark() # 获取文件索引:拓扑顺序或字母序 file_idx: int = i if topo_count > 0: @@ -1245,6 +1417,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(tr_r._nonlocal_names) stdlib.free(src_buf_r) + # 回收本文件 AST/lexer/token/IR 等瞬时数据(注册数据已在 perm_pool, + # reset 不影响;IncludesScanner result 等在 p1a_mark 前分配同样保留) + mb.reset_to(p1a_mark) + if p1a_fl > 0: fb_p1ar: str = VLogger.fmt_buf() if fb_p1ar is not None: @@ -1362,6 +1538,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, else: p1bpre_failed += 1 stdlib.free(src_buf_pv) + # 回收本文件 AST(vpi 已写盘,AST 不再需要;mark1 后无其他持久数据) + mb.reset_to(mark1) if p1bpre_failed > 0: fb_p1bpre: str = VLogger.fmt_buf() diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index a74a86c..ce8e2bc 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -16,10 +16,13 @@ import argparse import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesType as HandlesType import lib.core.BuildPipeline as BuildPipeline import lib.core.IncludesScanner as IncludesScanner +import lib.core.CompilerFactory as CompilerFactory import lib.core.StubMerger as StubMerger import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config @@ -30,8 +33,10 @@ _mbuddy: memhub.MemBuddy | t.CPtr # 源代码缓冲区大小(1MB) SRC_BUF_SIZE: t.CDefine = 1048576 -# 最大源文件数(递归扫描子目录后文件数增多,增大到 64) -MAX_SRC_FILES: t.CDefine = 64 +# 最大源文件数(递归扫描子目录后文件数增多:App 现有 65 个 .vp, +# 2026-08-14 CompilerFactory.vp 加入后超出 64 被截断 → 链接 undefined symbol +# CompilerFactory.get_perm_pool。增大到 128 留余量) +MAX_SRC_FILES: t.CDefine = 128 # 全局栈金丝雀(避免局部变量改变栈布局) _g_canary: t.CUInt32T @@ -165,6 +170,85 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr, return file_count +# ============================================================ +# _PreRegisterAppGenericTemplates - mark2 前预注册 App 源文件泛型类模板 +# +# 与 Phase1 的 includes 全量预注册同理:泛型模板表(GenericClassNames 等 +# 4 张 + 模板节点)是 pool 懒分配的跨文件持久表,若在 mark2 之后 +# (Phase A-pre 翻译 App 文件时)才首次注册,reset_to(mark2) 会回收表与 +# 节点 → 后续查重/特化遍历悬空内存崩溃。App 文件数量少,mark2 前预注册 +# 产生的 AST 驻留内存可忽略。重复注册由 _register_generic_template 查重跳过。 +# ============================================================ +def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy | t.CPtr, + entries: SrcFileEntry | t.CPtr, + file_count: int, + entry_size: t.CSizeT) -> int: + """mark2 前预注册 App 源文件的泛型类模板,返回处理到的泛型类数量""" + if mb is None or entries is None or file_count <= 0: + return 0 + handled: int = 0 + for i in range(file_count): + ea_pa: t.CUInt64T = t.CUInt64T(entries) + i * entry_size + ent_pa: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pa, t.CPtr)) + if ent_pa is None or ent_pa.Path is None or ent_pa.Sha1 is None: + continue + # 读取文件 + f_pa: fileio.File | t.CPtr = fileio.File(ent_pa.Path, fileio.MODE.R) + if f_pa.closed: + continue + src_buf_pa: bytes = stdlib.malloc(SRC_BUF_SIZE) + if src_buf_pa is None: + f_pa.close() + continue + bytes_read_pa: LONG = f_pa.read_all(src_buf_pa, SRC_BUF_SIZE) + f_pa.close() + if bytes_read_pa <= 0: + stdlib.free(src_buf_pa) + continue + if bytes_read_pa < SRC_BUF_SIZE: + src_buf_pa[bytes_read_pa] = 0 + else: + src_buf_pa[SRC_BUF_SIZE - 1] = 0 + # 解析 AST(只解析,不完整翻译;用永久池使模板节点跨 mark2 reset 存活) + perm_pa: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if perm_pa is None: + perm_pa = mb + lx_pa: ast.Lexer | t.CPtr = ast.new_lexer(perm_pa) + if lx_pa is None: + stdlib.free(src_buf_pa) + continue + ast._lexer_init(lx_pa, src_buf_pa, perm_pa) + tokens_pa: ast.Token | t.CPtr = ast.tokenize(lx_pa) + tree_pa: ast.AST | t.CPtr = ast.parse_tokens(perm_pa, tokens_pa) + if tree_pa is None: + stdlib.free(src_buf_pa) + continue + # 遍历顶层 ClassDef,注册泛型模板(type_params 非 None) + ch_pa: list[ast.AST | t.CPtr] | t.CPtr = tree_pa.children + if ch_pa is not None: + cn_pa: t.CSizeT = len(ch_pa) + for ci_pa in range(cn_pa): + child_pa: ast.AST | t.CPtr = ch_pa.get(ci_pa) + if child_pa is None or child_pa.kind() != ast.ASTKind.ClassDef: + continue + cd_pa: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child_pa) + if cd_pa is None or cd_pa.name is None: + continue + tp_pa: list[str] | t.CPtr = cd_pa.type_params + if tp_pa is None: + continue + HandlesType.set_current_file(ent_pa.Path) + HandlesClassDef._register_generic_template(perm_pa, cd_pa, ent_pa.Sha1) + handled += 1 + stdlib.free(src_buf_pa) + if handled > 0: + fb_pg3: str = VLogger.fmt_buf() + if fb_pg3 is not None: + viperlib.snprintf(fb_pg3, 1024, "mark2 前预注册 %d 个 App 泛型类模板", handled) + VLogger.info(fb_pg3, "PhaseA") + return handled + + def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, source_dir: str, temp_dir: str, output_dir: str, cc_cmd: str, cc_flags: str, @@ -211,6 +295,24 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 初始化 AST 表 ast._init_tables(mb) + # mark2 之前预注册 App 源文件的泛型类模板:泛型模板表/节点是 pool 懒分配, + # 必须在 mark2 之前注册才能跨 reset_to(mark2) 存活(与 Phase1 的 includes + # 全量预注册同理),否则 Phase A-pre 首次注册后 reset 回收 → 后续查重/特化 + # 遍历悬空内存崩溃(1.log FuncTest ASAN access-violation 同类根因)。 + _PreRegisterAppGenericTemplates(mb, entries, file_count, entry_size) + + # OOM 清理(对齐 Phase1 1b 的 mark1/reset_to 模式): + # Phase2 的 includes 预注册(Phase A-pre-inc)、App 源文件预注册(Phase A-pre)、 + # App 全量翻译(Phase A)、缺失 includes 翻译(Phase B+)都会产生大量瞬时数据 + # (AST/tokens/lexer/IR/Handler/dump 临时)。无回收时 98 个 includes 的 AST/IR + # 全量累积 pool → 1GB OOM(NewAstTest 依赖 ast 模块场景)。 + # 此处打 mark2,各处翻译完成后 reset_to(mark2) 回收该文件瞬时数据; + # 跨文件持久表(struct/enum/docstring/CDefine/函数注册表等)已 stdlib 化 + # 或落在 mark2 之前,reset 不受影响(与 Phase1 1b 同翻译路径,已验证安全)。 + mark2: t.CSizeT = mb.mark() + # mark2 之后新注册的 struct/enum 条目改 stdlib 分配(跨 reset 存活,对齐 Phase1) + HandlesStruct.set_stdlib_alloc(1) + # 确保 temp/output 目录存在 BuildPipeline.ensure_dir(temp_dir) BuildPipeline.ensure_dir(output_dir) @@ -361,10 +463,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if inc_fail_flags is not None: inc_fail_flags[si_inc] = 0 # 依赖填充只在第一遍执行(PopulateIncludesDeps 内部有去重) + # 注意:必须在 reset_to(mark2) 之前调用——内部深拷贝到 stdlib if pass_inc_i == 0 and tr_inc_pre._imported_modules is not None \ and StubMerger.PopulateIncludesDeps( inc_sha1_pre, tr_inc_pre._imported_modules) == 0: deps_filled_pre = deps_filled_pre + 1 + # OOM 清理:回收本 includes 文件预注册翻译的瞬时数据 + # (AST/tokens/lexer/IR/Handler);struct 注册表条目 stdlib 分配不受影响 + mb.reset_to(mark2) struct_count_after_inc: int = HandlesStruct.get_struct_count() # 收敛检查:本遍没有新结构体注册 → 所有类已注册 if struct_count_after_inc == struct_count_before_inc: @@ -412,6 +518,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if fb is not None: viperlib.snprintf(fb, 1024, "预注册失败: %s", ent_pre.Path) VLogger.error(fb, "PhaseA") + # OOM 清理:回收本 App 文件预注册翻译的瞬时数据 + mb.reset_to(mark2) struct_count_after: int = HandlesStruct.get_struct_count() # 收敛检查:本遍没有新结构体注册 → 所有类已注册 if struct_count_after == struct_count_before: @@ -465,6 +573,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, tr_a: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa, 0, temp_dir) if tr_a is None: # 翻译失败(语法/解析错误):记录待下遍重试,收敛检查统一报错 + # OOM 清理:失败翻译也已产生 AST/tokens 瞬时数据,立即回收 + mb.reset_to(mark2) continue # 收集 _imported_modules 到 app_deps_buf(供 Phase B+ 按需编译)。 # 依赖在翻译早期已填充,即使 _deps_pending 也收集。 @@ -484,6 +594,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, viperlib.snprintf(fb_dp, 1024, "Phase A 依赖未就绪,下遍重试: %s", ent.Path) VLogger.warning(fb_dp, "PhaseA-DBG") + # OOM 清理:本文件翻译已产生瞬时数据(下遍会重新翻译),立即回收 + mb.reset_to(mark2) continue # dump stub IR (declarations only) @@ -533,6 +645,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, done_flags[i] = 1 completed_this_pass += 1 + # OOM 清理:stub/text/deps 已写盘,回收本文件翻译瞬时数据 + # (AST/tokens/lexer/IR/Handler);app_deps_buf 已深拷贝依赖,不受影响 + mb.reset_to(mark2) # 收敛检查:所有文件完成则结束 all_done: int = 1 for i_ck in range(file_count): @@ -877,6 +992,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(inc_text_buf) # 重试 BuildCombinedIR inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) + # OOM 清理:stub/text 已写盘,回收本 includes 文件翻译瞬时数据 + # (AST/tokens/lexer/IR/Handler);BuildCombinedIR 只读文件,不受影响 + mb.reset_to(mark2) if inc_combined_len_mi == 0: # 诊断:检查 stub.ll/text.ll 是否存在 diff --git a/App/main.vp b/App/main.vp index c3d5491..1a87803 100644 --- a/App/main.vp +++ b/App/main.vp @@ -39,6 +39,7 @@ import lib.core.IncludesScanner as IncludesScanner import lib.core.StubMerger as StubMerger import lib.core.Phase1 as Phase1 import lib.core.Phase2 as Phase2 +import lib.core.CompilerFactory as CompilerFactory import lib.core.Gen.Generator as GenGenerator import lib.Projectrans.Config as Config import lib.Projectrans.Utils as Utils @@ -50,9 +51,17 @@ import hashlib # 内存大小: fl_bytes=264 (33*8), POOL_SIZE-fl_bytes 必须是 2 的幂以避免浪费 # 1073742088 - 264 = 1073741824 (1024MB usable = 2^30) -# 注意: POOL_SIZE 必须是 2^30+264=1073742088,否则 _largest_pow2_le 会取 2^29=512MB +# 注意: POOL_SIZE 必须是 2^N+264,否则 _largest_pow2_le 会取低一级 2 的幂 # Phase1 翻译 87 个 includes 文件 + Phase2 翻译 30 个测试文件,512MB 不足导致 Phase2 崩溃 +# 2026-08-13: NewAstTest 依赖 ast 模块(98 includes),Phase2 的 Phase A-pre-inc/Phase A +# 无 mark/reset 回收,98 个 includes 的 AST/IR 全量累积 pool 超 1GB → 曾增大到 2GB。 +# → 已在 Phase2 对齐 Phase1 的 mark1/reset_to 清理机制(每文件翻译后回收瞬时数据), +# 峰值回落至单文件水平,恢复 1GB(1073742088 - 264 = 1073741824 = 2^30) POOL_SIZE: t.CDefine = 1073742088 +# 永久内存池大小:2^29 + 264 = 512MB usable(存跨 mark 持久数据:泛型模板 +# 表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存等)。 +# 与主 pool 独立(MemBuddy 第二实例),永不 reset_to,不占主 pool 配额。 +PERM_POOL_SIZE: t.CDefine = 536871176 # 语言编码页 CODE_PAGE: t.CDefine = 65001 # 源代码缓冲区大小(1MB) @@ -77,6 +86,20 @@ def main() -> int: stdio.printf("FAIL: MemBuddy init failed\n") return 1 + # 创建全局永久内存池(perm_pool,512MB):存放跨 mark/reset 持久数据 + # (泛型模板表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存 + # 等),替代 stdlib.malloc 深拷贝(buddy 分配更快),永不 reset,不占 + # 主 pool 配额。经 CompilerFactory 单例分发(不改通用库 memhub)。 + perm_arena: bytes = stdlib.malloc(PERM_POOL_SIZE) + if perm_arena is None: + stdio.printf("FAIL: malloc for perm_pool arena failed\n") + return 1 + perm_pool: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE) + if perm_pool is None: + stdio.printf("FAIL: perm_pool init failed\n") + return 1 + CompilerFactory.set_factory(CompilerFactory.CompilerFactory(mb, perm_pool)) + # 设置全局 mbuddy 指针(sys 和 argparse 都需要) sys._mbuddy = mb argparse._mbuddy = mb