From 35f2070d3b5e6de3be7c85d4e6fecbe05274166f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 08:18:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(sidecar):=20meta=20=E5=A2=9E=E5=86=99=20ST?= =?UTF-8?q?RUCT/VTABLE=EF=BC=8CREPL=20=E5=B8=83=E5=B1=80=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E6=94=B9=E8=AF=BB=20meta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WriteModuleFuncMeta 更名 WriteModuleMeta:追加 STRUCT FIELDS n PACKED 0|1 (opaque → STRUCT OPAQUE)与 VTABLE SLOTS n;数据源改为 Module.NamedTypeHead / GlobalHead,不再有任何 IR 文本反推 - VpiReader REPL 布局改读 {sha1}.meta:新增 _meta_atoi / _repl_meta_scan_layout 按前缀精确匹配解析 STRUCT/VTABLE 行 - 删除旧 .stub.ll 文本解析辅助 _repl_stub_take_name / _repl_stub_body_count 及其 scratch 缓冲 ReplStubNameBuf / _REPL_STUB_NAME_CAP 去 IR 化步骤③;未编译验证。 --- App/lib/Phase/Phase1/Phase1Main.vp | 4 +- App/lib/Phase/Phase2/__init__.vp | 8 +- App/lib/Stub/StubGen/VpiReader.vp | 206 ++++++------------------ App/lib/Stub/StubMerger/StubMergerIR.vp | 74 +++++++-- 4 files changed, 123 insertions(+), 169 deletions(-) diff --git a/App/lib/Phase/Phase1/Phase1Main.vp b/App/lib/Phase/Phase1/Phase1Main.vp index 84a91cc..4676166 100644 --- a/App/lib/Phase/Phase1/Phase1Main.vp +++ b/App/lib/Phase/Phase1/Phase1Main.vp @@ -1140,8 +1140,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, stdlib.free(text_path_p1) stdlib.free(text_buf) - # save {sha1}.meta(结构化 sidecar:函数定义清单,替代从 text.ll 反推 declare-vs-define) - StubMergerIR.WriteModuleFuncMeta(tr.Module, temp_dir, dir_len_p1, sha1) + # save {sha1}.meta(结构化 sidecar:函数清单/结构体布局/虚表,替代从 IR 文本反推) + StubMergerIR.WriteModuleMeta(tr.Module, temp_dir, dir_len_p1, sha1) # 计时点 3:dump(stub+text)完成;输出单文件三段耗时 t_f3: uint64 = w32.win32base.GetTickCount64() diff --git a/App/lib/Phase/Phase2/__init__.vp b/App/lib/Phase/Phase2/__init__.vp index 1a50d54..6987ae3 100644 --- a/App/lib/Phase/Phase2/__init__.vp +++ b/App/lib/Phase/Phase2/__init__.vp @@ -1085,8 +1085,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, stdlib.free(text_path_a) stdlib.free(text_buf_a) - # save {sha1}.meta(结构化 sidecar:函数定义清单) - StubMergerIR.WriteModuleFuncMeta(tr_a.Module, temp_dir, td_len_pa, ent.Sha1) + # save {sha1}.meta(结构化 sidecar:函数清单/结构体布局/虚表) + StubMergerIR.WriteModuleMeta(tr_a.Module, temp_dir, td_len_pa, ent.Sha1) # save dependencies (_imported_modules) for Phase B (切片路径) deps_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "deps.txt") @@ -1476,8 +1476,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, itf.close() stdlib.free(inc_text_path) stdlib.free(inc_text_buf) - # save {sha1}.meta(结构化 sidecar:函数定义清单) - StubMergerIR.WriteModuleFuncMeta(tr_mi.Module, temp_dir, td_len_mi, inc_sha1_mi) + # save {sha1}.meta(结构化 sidecar:函数清单/结构体布局/虚表) + StubMergerIR.WriteModuleMeta(tr_mi.Module, temp_dir, td_len_mi, inc_sha1_mi) # 重试 BuildCombinedIR inc_combined_len_mi = StubMergerBuild.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) # OOM 清理:stub/text 已写盘,回收本 includes 文件翻译瞬时数据 diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index c161362..2316e42 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -29,7 +29,6 @@ import lib.Projectrans.Config as Config _REPL_STUB_CLS_MAX: t.CDefine = 1024 _REPL_STUB_FIELD_MAX: int = 128 _REPL_STUB_VT_MAX: int = 128 -_REPL_STUB_NAME_CAP: sizet = 256 _REPL_STUB_TXT_CAP: sizet = 4194304 _REPL_STUB_WARN_MAX: int = 10 _REPL_STUB_FINISH_PASSES: int = 8 @@ -3686,7 +3685,6 @@ ReplStubWarnLeft: int = 0 ReplStubTmpCount: int = -1 ReplStubTmpPacked: int = 0 ReplStubTmpArity: int = 0 -ReplStubNameBuf: str = None def _repl_stub_dupstr(pool: memhub.MemBuddy*, src: str) -> str: @@ -3761,105 +3759,31 @@ def _repl_stub_readtext(path: str) -> str: return buf -# 从 txt[pos] 起取一个标识符(跳过 '%' / '@' 前缀与可选引号)写进 ReplStubNameBuf; -# 返回名字之后的位置(0 = 失败/超长;合法调用至少前进 2 字符,故 0 可作失败哨兵) -def _repl_stub_take_name(txt: str, pos: sizet) -> sizet: - global ReplStubNameBuf - if txt is None or ReplStubNameBuf is None: - return 0 - n: sizet = string.strlen(txt) - i: sizet = pos - if i < n and (txt[i] == '%' or txt[i] == '@'): +# 从 txt[start, end) 解析十进制整数(跳过前导空格;无数字返回 0) +def _meta_atoi(txt: str, start: sizet, end: sizet) -> int: + i: sizet = start + while i < end and (txt[i] == ' ' or txt[i] == '\t'): i += 1 - if i >= n: - return 0 - if txt[i] == '\"': + neg: int = 0 + if i < end and txt[i] == '-': + neg = 1 i += 1 - k: sizet = 0 - while i < n and txt[i] != '\"': - if k + 1 >= _REPL_STUB_NAME_CAP: - return 0 - ReplStubNameBuf[k] = txt[i] - k += 1 - i += 1 - if i >= n: - return 0 - ReplStubNameBuf[k] = 0 - return i + 1 - k2: sizet = 0 - while i < n: - ch: char = txt[i] - if ch == ' ' or ch == '\t' or ch == '=' or ch == ',' or ch == '{' or ch == '}': - break - if ch == '[' or ch == ']' or ch == '(' or ch == ')' or ch == '*' or ch == '<' or ch == '>': - break - if k2 + 1 >= _REPL_STUB_NAME_CAP: - return 0 - ReplStubNameBuf[k2] = ch - k2 += 1 + v: int = 0 + while i < end and txt[i] >= '0' and txt[i] <= '9': + v = v * 10 + (int(txt[i]) - 48) i += 1 - if k2 == 0: - return 0 - ReplStubNameBuf[k2] = 0 - return i + if neg != 0: + return -v + return v -# 从 txt[pos](应指向 '{')起数顶层字段数:{} / [] / () / <> / "..." 内的逗号不计; -# 返回字段数(-1 = 未闭合/格式异常) -def _repl_stub_body_count(txt: str, pos: sizet) -> int: - if txt is None: - return -1 - n: sizet = string.strlen(txt) - i: sizet = pos - if i >= n or txt[i] != '{': - return -1 - i += 1 - depth: int = 0 - in_str: int = 0 - cnt: int = 0 - has: int = 0 - while i < n: - ch: char = txt[i] - if in_str != 0: - if ch == '\"': - in_str = 0 - i += 1 - continue - if ch == '\"': - in_str = 1 - has = 1 - i += 1 - continue - if ch == 0: - return -1 - if ch == ',': - if depth == 0: - cnt += 1 - i += 1 - continue - if ch == '{' or ch == '[' or ch == '(' or ch == '<': - depth += 1 - has = 1 - i += 1 - continue - if ch == '}' or ch == ']' or ch == ')' or ch == '>': - if ch == '}' and depth == 0: - if has == 0: - return 0 - return cnt + 1 - depth -= 1 - i += 1 - continue - if ch != ' ' and ch != '\t' and ch != '\n' and ch != '\r': - has = 1 - i += 1 - return -1 - - -# 扫描存根文本,取 `%"" = type ...` 与 `@....vtable = ...` 两条元数据, -# 结果写全局 scratch:ReplStubTmpCount(字段数 / -1 无此类 / -2 opaque)、 -# ReplStubTmpPacked、ReplStubTmpArity(.vtable 槽数,0 = 无该全局) -def _repl_stub_scan_meta(txt: str, key: str) -> None: +# 扫描 {sha1}.meta 文本,取 `STRUCT FIELDS PACKED <0|1>`(或 `STRUCT OPAQUE`) +# 与 `VTABLE SLOTS ` 两条元数据,结果写全局 scratch: +# ReplStubTmpCount(字段数 / -1 无此类 / -2 opaque)、ReplStubTmpPacked、 +# ReplStubTmpArity(虚表槽数,0 = 无该虚表)。key = "{sha1}.{类名}"。 +# 逐行、按前缀 strncmp 精确匹配,且 name 之后必须紧跟空格(避免前缀误命中); +# 不走 take_name —— 类名可能含泛型下标 [ ] 等字符。 +def _repl_meta_scan_layout(txt: str, key: str) -> None: global ReplStubTmpCount, ReplStubTmpPacked, ReplStubTmpArity ReplStubTmpCount = -1 ReplStubTmpPacked = 0 @@ -3868,6 +3792,8 @@ def _repl_stub_scan_meta(txt: str, key: str) -> None: return n: sizet = string.strlen(txt) key_len: sizet = string.strlen(key) + if key_len == 0: + return i: sizet = 0 while i < n: ls: sizet = i @@ -3876,61 +3802,37 @@ def _repl_stub_scan_meta(txt: str, key: str) -> None: le: sizet = i if i < n: i += 1 - p: sizet = ls - while p < le and (txt[p] == ' ' or txt[p] == '\t' or txt[p] == '\r'): - p += 1 - if p >= le: - continue - c0: char = txt[p] - if c0 == '%': - # 类型定义行 - q: sizet = _repl_stub_take_name(txt, p) - if q == 0 or q > le: + if le > ls and txt[le - 1] == '\r': + le -= 1 + if ls + 7 <= le and string.strncmp(txt + ls, "STRUCT ", 7) == 0: + ns: sizet = ls + 7 + ne: sizet = ns + key_len + if ne > le or string.strncmp(txt + ns, key, key_len) != 0: continue - if string.strcmp(ReplStubNameBuf, key) != 0: + if ne >= le or txt[ne] != ' ': continue - # 找 "= type" - j: sizet = q - while j + 6 < le and string.strncmp(txt + j, "= type", 6) != 0: - j += 1 - if j + 6 >= le: - continue - j += 6 - while j < le and (txt[j] == ' ' or txt[j] == '\t'): - j += 1 - if j + 6 <= le and string.strncmp(txt + j, "opaque", 6) == 0: + # OPAQUE + if ne + 7 <= le and string.strncmp(txt + ne + 1, "OPAQUE", 6) == 0: ReplStubTmpCount = -2 continue - if j < le and txt[j] == '<': - ReplStubTmpPacked = 1 - j += 1 - if j < le and txt[j] == '{': - ReplStubTmpCount = _repl_stub_body_count(txt, j) + # FIELDS PACKED <0|1> + if ne + 8 <= le and string.strncmp(txt + ne + 1, "FIELDS ", 7) == 0: + ReplStubTmpCount = _meta_atoi(txt, ne + 8, le) + ps: sizet = ne + 8 + while ps < le and txt[ps] >= '0' and txt[ps] <= '9': + ps += 1 + if ps + 8 <= le and string.strncmp(txt + ps + 1, "PACKED ", 7) == 0: + ReplStubTmpPacked = _meta_atoi(txt, ps + 8, le) continue - if c0 == '@': - # 全局变量行:只关心 .vtable - q2: sizet = _repl_stub_take_name(txt, p) - if q2 == 0 or q2 > le: + if ls + 7 <= le and string.strncmp(txt + ls, "VTABLE ", 7) == 0: + vs: sizet = ls + 7 + ve: sizet = vs + key_len + if ve > le or string.strncmp(txt + vs, key, key_len) != 0: continue - nm_len: sizet = string.strlen(ReplStubNameBuf) - if nm_len != key_len + 7: + if ve >= le or txt[ve] != ' ': continue - if string.strncmp(ReplStubNameBuf, key, key_len) != 0: - continue - if string.strncmp(ReplStubNameBuf + key_len, ".vtable", 7) != 0: - continue - j2: sizet = q2 - while j2 < le and txt[j2] != '=': - j2 += 1 - if j2 >= le: - continue - j2 += 1 - while j2 < le and txt[j2] != '{': - j2 += 1 - if j2 < le and txt[j2] == '{': - ar: int = _repl_stub_body_count(txt, j2) - if ar > 0: - ReplStubTmpArity = ar + if ve + 7 <= le and string.strncmp(txt + ve + 1, "SLOTS ", 6) == 0: + ReplStubTmpArity = _meta_atoi(txt, ve + 7, le) continue return @@ -3939,7 +3841,7 @@ def ReplStubLayoutBegin(temp_dir: str) -> int: """开启会话级"存根布局恢复"(Repl.vp 在 includes 类注册前调用一次);0=成功""" global ReplStubLayoutMode, ReplStubPool, ReplStubTempDir, ReplStubTable global ReplStubCount, ReplStubOk, ReplStubVtOk, ReplStubRefused, ReplStubNoStub - global ReplStubWarnLeft, ReplStubNameBuf + global ReplStubWarnLeft global ReplStubRefusedNoStub, ReplStubRefusedNoClass, ReplStubRefusedOpaque, ReplStubRefusedParent pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool if pool is None or temp_dir is None: @@ -3952,10 +3854,6 @@ def ReplStubLayoutBegin(temp_dir: str) -> int: if ReplStubTable is None: return 1 string.memset(ReplStubTable, 0, 8 * _REPL_STUB_CLS_MAX) - ReplStubNameBuf = pool.alloc(_REPL_STUB_NAME_CAP) - if ReplStubNameBuf is None: - return 1 - ReplStubNameBuf[0] = 0 ReplStubCount = 0 ReplStubOk = 0 ReplStubVtOk = 0 @@ -4508,7 +4406,7 @@ def _repl_stub_add_class(pool: memhub.MemBuddy*, # 存根元数据(缺存根时 LayoutCount 保持"未找到"哨兵;两种成因由 have_stub 区分) e.LayoutCount = _REPL_STUB_NOCOUNT_NOCLASS if txt is not None: - _repl_stub_scan_meta(txt, key) + _repl_meta_scan_layout(txt, key) e.LayoutCount = ReplStubTmpCount e.VtArity = ReplStubTmpArity if ReplStubTmpPacked != 0: @@ -4564,9 +4462,9 @@ def _repl_stub_collect_module(pool: memhub.MemBuddy*, return 0 if src_sha1 is None or src_sha1[0] == '\0' or ReplStubTable is None: return 0 - # 存根文本(缺失:带父类/虚表的类将因"数据缺失"被明确拒绝,逐个可见) + # 布局元数据({sha1}.meta;缺失:带父类/虚表的类将因"数据缺失"被明确拒绝,逐个可见) txt: str = None - sp: str = _repl_stub_path(src_sha1, "stub.ll") + sp: str = _repl_stub_path(src_sha1, "meta") if sp is not None: txt = _repl_stub_readtext(sp) stdlib.free(sp) @@ -4577,8 +4475,8 @@ def _repl_stub_collect_module(pool: memhub.MemBuddy*, ReplStubNoStub += 1 if ReplStubWarnLeft > 0: ReplStubWarnLeft -= 1 - stdio.printf("includes 类布局: 模块 %s 无 .stub.ll 存根(数据缺失)—— 该模块" - "带父类/虚表的类将逐个按『数据缺失』明确拒绝(读不到存根 ⇒ 无" + stdio.printf("includes 类布局: 模块 %s 无 .meta 存根(数据缺失)—— 该模块" + "带父类/虚表的类将逐个按『数据缺失』明确拒绝(读不到布局元数据 ⇒ 无" "字段数可比,不是字段数不符)。用同一工程先跑一次批处理生成存根," "再 `--project .\\project.vpj --repl`(TransPyV 目录)即可复用\n", src_sha1) diff --git a/App/lib/Stub/StubMerger/StubMergerIR.vp b/App/lib/Stub/StubMerger/StubMergerIR.vp index 70e3007..c9b9a2c 100644 --- a/App/lib/Stub/StubMerger/StubMergerIR.vp +++ b/App/lib/Stub/StubMerger/StubMergerIR.vp @@ -31,21 +31,25 @@ import lib.Stub.StubMerger as StubMerger # ============================================================ # ============================================================ -# WriteModuleFuncMeta - 落盘 {sha1}.meta 的函数定义清单(结构化 sidecar) +# WriteModuleMeta - 落盘 {sha1}.meta(模块结构化 sidecar:函数清单 + 结构体布局 + 虚表) # -# 每行 "FUNC DEF|DECL":DEF = 本模块定义了该符号(有函数体,等同 .text.ll 的 define 行), -# DECL = 仅声明(等同 .stub.ll 的 declare)。数据源为翻译期模型(Module.FuncHead 链表 + -# Function.IsDeclared),与 dump_ir 的 stub/text 判据同源(STUB=IsDeclared==1、TEXT==0), -# 取代「从 IR 文本反推 declare-vs-define」。 +# 行格式(每行一条,纯文本、无嵌套括号): +# FUNC DEF|DECL DEF=本模块定义了该符号(有函数体,等同 .text.ll 的 define 行) +# STRUCT .<类名> FIELDS PACKED <0|1> 命名结构体字段数/是否 packed +# STRUCT .<类名> OPAQUE 前向声明(opaque,无 body) +# VTABLE .<类名> SLOTS 虚表槽数(全局 @<类名>.vtable 的元素数) +# 数据源均为翻译期模型(Module.FuncHead / NamedTypeHead / GlobalHead),与 dump_ir 同源, +# 取代「从 IR 文本反推 declare-vs-define / 结构体布局 / 虚表槽数」。 # -# 写不出(路径/打开失败)即 fail-fast:缺 meta 会使 Phase B+/C 的「声明文件判定」失效。 +# 写不出(路径/打开失败)即 fail-fast:缺 meta 会使 Phase B+/C 的声明文件判定与 +# REPL 的布局恢复失效。 # # Args: mod 已翻译完成的 LLVM 模块;temp_dir 基础目录;td_len temp_dir 长度;sha1 模块 SHA1 # Returns: 0=成功(失败经 VLogger.error 终止进程) # ============================================================ -def WriteModuleFuncMeta(mod: llvmlite.LLVMModule*, temp_dir: str, - td_len: sizet, sha1: str) -> int: - """把本模块的函数定义清单写入 {sha1}.meta""" +def WriteModuleMeta(mod: llvmlite.LLVMModule*, temp_dir: str, + td_len: sizet, sha1: str) -> int: + """把本模块的函数定义/结构体布局/虚表清单写入 {sha1}.meta""" if mod is None or temp_dir is None or sha1 is None: VLogger.error("meta 写出:参数为空", "Meta") return 1 @@ -78,6 +82,58 @@ def WriteModuleFuncMeta(mod: llvmlite.LLVMModule*, temp_dir: str, mf.write(line, string.strlen(line)) stdlib.free(line) cur = llvmlite.function_get_next(cur) + # 2) 结构体布局:STRUCT FIELDS PACKED <0|1>(opaque → STRUCT OPAQUE) + nt: llvmlite.NamedTypeNode* = mod.NamedTypeHead + nt_steps: sizet = 0 + while nt is not None: + nt_steps += 1 + if nt_steps > 100000: + VLogger.error("meta 写出:命名类型链表成环/超限", "Meta") + break + tn: str = llvmlite.get_struct_name(nt.Ty) + if tn is not None and tn[0] != '\0': + tl: sizet = string.strlen(tn) + lb: bytes = stdlib.malloc(tl + 48) + if lb is None: + VLogger.error("meta 写出:行缓冲分配失败", "Meta") + break + fc: int = llvmlite.get_struct_fcount(nt.Ty) + if fc < 0: + viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn) + else: + viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked) + mf.write(lb, string.strlen(lb)) + stdlib.free(lb) + nt = nt.Next + # 3) 虚表槽数:VTABLE SLOTS (全局名 .vtable,Ty 匿名结构体 fcount=槽数) + gv: llvmlite.GlobalVariable* = mod.GlobalHead + gv_steps: sizet = 0 + while gv is not None: + gv_steps += 1 + if gv_steps > 100000: + VLogger.error("meta 写出:全局变量链表成环/超限", "Meta") + break + gn: str = gv.Name + if gn is not None: + gnl: sizet = string.strlen(gn) + if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0: + base_len: sizet = gnl - 7 + base: bytes = stdlib.malloc(base_len + 1) + if base is not None: + string.strncpy(base, gn, base_len) + base[base_len] = '\0' + ar: int = llvmlite.get_struct_fcount(gv.Ty) + if ar < 0: + ar = 0 + lb2: bytes = stdlib.malloc(base_len + 40) + if lb2 is None: + VLogger.error("meta 写出:行缓冲分配失败", "Meta") + else: + viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar) + mf.write(lb2, string.strlen(lb2)) + stdlib.free(lb2) + stdlib.free(base) + gv = gv.Next mf.close() stdlib.free(mpath) return 0