feat: 引入 CompilerFactory 永久内存池,1a 每文件 reset 解 NewAstTest OOM

main 创建 512MB perm_pool(MemBuddy 第二实例,永不 reset)并注册
CompilerFactory 工厂(对齐 TPC Translator 装配模式,memhub 保持通用)。
各 Gen Mixin 跨 mark 持久表(struct/枚举/Renum/spec/vtable/全局函数/
闭包/函数指针/Cexport/Reexport/TraceFrames/Doc)全部迁移到 perm_pool,
替代 stdlib.malloc 深拷贝提升速度。

Phase1 1a 主循环(98 includes 全量 parse AST 驻留主 pool → 1GB 耗尽)
改为:StdlibAlloc=1 注册数据进 perm_pool + 每文件翻译完 reset_to(文件级
mark) 回收 AST;1b-pre vpi 预生成循环同样 reset_to(mark1)。同步修 7 处
reset 后悬空隐患:StructTable 表体、add_field DefaultVal、Renum 变体/
别名表内容、vtable full_names 内容、FuncptrRets 深拷贝。

CompilerFactory 类名与模块名同名(合法场景,TPC 已支持模块别名优先)。
This commit is contained in:
TermiNexus committed 2026-08-14 04:02:36 +08:00
1 parent c27a1ba347
commit 8d5dbeab89
14 files changed
+981 -115

No files matched your search

+68
View File
@@ -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
+23 -15
View File
@@ -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)
+83 -43
View File
@@ -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)
+3 -3
View File
@@ -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)
+1
View File
@@ -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
+4 -3
View File
@@ -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)
+35 -10
View File
@@ -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
+75 -34
View File
@@ -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)
+2 -1
View File
@@ -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)
+361
View File
@@ -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: <body> → 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 路径
#
+4 -3
View File
@@ -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
+178
View File
@@ -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()
+120 -2
View File
@@ -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 是否存在
+24 -1
View File
@@ -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