fix: Phase1 三个无 reset 的全量 parse 循环改每文件回收,解 mark1 前 OOM

NewAstTest 仍 1GB 主池耗尽:Phase1 1a 主循环已 reset,但此前还有
三处对全部 includes 全量 parse AST 且驻留到 mark1 的循环(可达 includes
数量多时 AST 累积超 1GB):
1. _ScanClassInheritance(类继承预扫描):每文件收集类名/父类/SHA1
   到扁平数组后 reset_to(文件级 mark) 回收 AST;
2. Phase 1a-pre import 依赖扫描:deps 写盘 + PopulateIncludesDeps
   (内部 stdlib 深拷贝)后 reset_to(文件级 mark);
3. 删除冗余的 Phase 1a-pre-generic(主 pool 全量 parse 可达泛型文件、
   无 reset)——已被 _PreRegisterAllGenericTemplates(文本扫描筛选 +
   perm_pool 注册,全覆盖)替代。

注:reset 只回收各文件的 AST/lexer/token/IR 等瞬时数据;注册表数据
(StructTable/Fields/Name/ModuleSha1/struct_ty 深拷贝/枚举/Renum/泛型
模板)已在前一轮迁入 perm_pool,不受 reset 影响。
This commit is contained in:
TermiNexus committed 2026-08-14 04:13:59 +08:00
1 parent 8d5dbeab89
commit 2ef6a55ead
1 file changed
+19 -86
+19 -86
View File
@@ -85,6 +85,11 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
class_count: int = 0
for i in range(result.Count):
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes
# 全量 parse 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因;
# class_names_buf/parent_names_buf/def_sha1s_buf 在调用前分配,
# 位于本 mark 之前,reset 不影响;generic_file_indices 同理)
scan_mark: t.CSizeT = mb.mark()
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is None or entry.Sha1 is None:
@@ -199,6 +204,8 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
class_count += 1
stdlib.free(src_buf)
# 回收本文件 AST/lexer/token(类名/父类/SHA1 已拷入扁平数组)
mb.reset_to(scan_mark)
return class_count
@@ -581,8 +588,12 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
file_count: int = 0
# sha1→file_index 映射(简单线性查找,文件数不多)
for i in range(result.Count):
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
# 文件级 mark:import 依赖收集完(deps 写盘 + PopulateIncludesDeps
# 内部 stdlib 深拷贝)后立即回收本文件 AST——98 includes 全量 parse
# 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因之一
scan_mark_a: t.CSizeT = mb.mark()
entry_addr_a: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry_a: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_a, t.CPtr))
if entry is None or entry.Sha1 is None:
continue
@@ -971,6 +982,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_failed: int = 0
for i in range(result.Count):
# 文件级 mark:import 依赖收集完(deps 写盘 + PopulateIncludesDeps
# 内部 stdlib 深拷贝)后立即回收本文件 AST——98 includes 全量 parse
# 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因之一
scan_mark_a: t.CSizeT = mb.mark()
entry_addr_a: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry_a: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_a, t.CPtr))
if entry_a is None:
@@ -1095,6 +1110,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(tr_a._nonlocal_names)
stdlib.free(src_buf_a)
# 回收本文件 AST/lexer/token(deps 已写盘、依赖存储器已深拷贝)
mb.reset_to(scan_mark_a)
if p1a_failed > 0:
fb_p1apre: str = VLogger.fmt_buf()
@@ -1206,90 +1223,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
topo_count = 0
# ============================================================
# Phase 1a-pre-generic: 预注册所有泛型类模板
#
# 在 Phase 1a(struct 注册)之前执行,确保所有泛型类模板
#(如 GSListNode[T]、GSList[T])已注册。这样当其他文件的
# 结构体字段使用 GSListNode[Value] 等特化类型时,模板能被找到。
# ============================================================
generic_file_count: int = 0
if generic_file_count_box is not None:
gfc_box_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr))
if gfc_box_ptr is not None:
generic_file_count = gfc_box_ptr[0]
if generic_file_count > 0 and generic_file_indices is not None:
fb_gpre: str = VLogger.fmt_buf()
if fb_gpre is not None:
viperlib.snprintf(fb_gpre, 1024, "预注册 %d 个泛型类模板文件", generic_file_count)
VLogger.info(fb_gpre, "Phase1")
for gi in range(generic_file_count):
gi_addr_p: t.CUInt64T = t.CUInt64T(generic_file_indices) + gi * 4
gi_ptr_p: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_p, t.CPtr))
if gi_ptr_p is None:
continue
file_idx_gen: int = gi_ptr_p[0]
gen_entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx_gen * entry_size
gen_entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(gen_entry_addr, t.CPtr))
if gen_entry is None or gen_entry.Sha1 is None:
continue
gen_path: str = gen_entry.Path
gen_f: fileio.File | t.CPtr = fileio.File(gen_path, fileio.MODE.R)
if gen_f.closed:
continue
gen_src: bytes = stdlib.malloc(SRC_BUF_SIZE)
if gen_src is None:
gen_f.close()
continue
gen_br: LONG = gen_f.read_all(gen_src, SRC_BUF_SIZE)
gen_f.close()
if gen_br <= 0:
stdlib.free(gen_src)
continue
if gen_br < SRC_BUF_SIZE:
gen_src[gen_br] = 0
else:
gen_src[SRC_BUF_SIZE - 1] = 0
gen_lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if gen_lx is None:
stdlib.free(gen_src)
continue
ast._lexer_init(gen_lx, gen_src, mb)
gen_tokens: ast.Token | t.CPtr = ast.tokenize(gen_lx)
gen_tree: ast.AST | t.CPtr = ast.parse_tokens(mb, gen_tokens)
if gen_tree is None:
stdlib.free(gen_src)
continue
gen_tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if gen_tr is None:
stdlib.free(gen_src)
continue
gen_tr.ModuleSha1 = gen_entry.Sha1
gen_tr._declare_only = 1
gen_tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, gen_entry.RelPath)
HandlesType.set_current_file(gen_path)
HandlesImports.set_current_file(gen_path)
HandlesType.set_current_module_sha1(gen_entry.Sha1)
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
HandlesStruct.reset_visible_structs(mb, 0)
gen_tr.translate(gen_tree)
if gen_tr._global_names is not None:
stdlib.free(gen_tr._global_names)
if gen_tr._nonlocal_names is not None:
stdlib.free(gen_tr._nonlocal_names)
stdlib.free(gen_src)
# ============================================================
# Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板
#