diff --git a/App/lib/Stub/StubMerger/StubMergerBuild.vp b/App/lib/Stub/StubMerger/StubMergerBuild.vp index d5b70d1..1c1fa77 100644 --- a/App/lib/Stub/StubMerger/StubMergerBuild.vp +++ b/App/lib/Stub/StubMerger/StubMergerBuild.vp @@ -25,695 +25,6 @@ import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo import lib.Stub.StubMerger.StubMergerIR as StubMergerIR import lib.Stub.StubMerger as StubMerger -# ============================================================ -# 已见符号表(BuildCombinedIR 去重 O(n²)→O(1)) -# -# 用定长槽位开放寻址哈希表记录 out_buf 中已活跃的 declare/global/type 行,替代全量扫描去重(O(1) 查找与定位)。 -# slot 布局(步长 _SEEN_SLOT):{u64 hash} {u64 pos} {u8 flags} {name[_SEEN_NAME_MAX]} {u8 nul};空槽判定 hash==0(memset 全零初始化)。 -# flags(仅类型表)bit0=1 表示活跃行为完整定义(= type { / = type <),bit0=0 表示仅 opaque 声明;函数/全局表恒为 0。 -# pos 为活跃行在 out_buf 中的起始位置(注释操作直接 out_buf[pos]=';');注释行不从表中删除——注释后必然立即追加新行(更新 pos),表保持「有活跃行」不变量。 -# 超长名(>= _SEEN_NAME_MAX)不登记,防御性回退;表满(count >= cap)fail-fast 报错:静默丢弃登记会让去重失效,重复行追加 → llc redefinition。 -# ============================================================ -_SEEN_NAME_MAX: t.CDefine = 256 -_SEEN_SLOT: t.CDefine = 274 # 8(hash)+8(pos)+1(flags)+256(name)+1(nul) -_SEEN_FUNC_CAP: t.CDefine = 16384 -_SEEN_GLOBAL_CAP: t.CDefine = 16384 -_SEEN_TYPE_CAP: t.CDefine = 4096 -_SEEN_FNV_OFFSET: uint64 = 0xcbf29ce484222325 -_SEEN_FNV_PRIME: uint64 = 0x100000001b3 - -_seen_funcs: bytes # 已见函数签名表(@"sha1.name"( 等) -_seen_funcs_count: int -_seen_globals: bytes # 已见全局变量名表(@name) -_seen_globals_count: int -_seen_types: bytes # 已见类型名表(%name) -_seen_types_count: int -_seen_key_scratch: bytes # 注册键暂存(跨 truncation 窗口复制) - -def _SeenHash(key: str) -> uint64: - """FNV-1a 64 位哈希(对齐 includes/hashtable.vp)""" - h: uint64 = _SEEN_FNV_OFFSET - p: str = key - while p[0] != 0: - h = h ^ uint64(p[0]) - h = h * _SEEN_FNV_PRIME - p += 1 - if h < 2: - h = 2 - return h - -def _SeenFind(tbl: bytes, cap: sizet, key: str, key_hash: uint64) -> sizet: - """已见表槽位查找:命中返回槽位下标;未命中返回 cap(哨兵)""" - if tbl is None or key is None: - return cap - mask: sizet = cap - 1 - idx: sizet = sizet(key_hash) & mask - i: sizet = 0 - while i < cap: - off: sizet = idx * _SEEN_SLOT - hp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + off) - sh: uint64 = hp[0] - if sh == 0: - return cap - if sh == key_hash: - nm: str = tbl + off + 17 - if string.strcmp(nm, key) == 0: - return idx - idx = (idx + 1) & mask - i += 1 - return cap - -def _SeenPresent(tbl: bytes, cap: sizet, key: str, key_hash: uint64) -> int: - """已见表查找:返回 0=不存在;>0=存在(返回值 = flags+1)""" - si: sizet = _SeenFind(tbl, cap, key, key_hash) - if si >= cap: - return 0 - return int(tbl[si * _SEEN_SLOT + 16]) + 1 - -def _SeenInsert(tbl: bytes, cap: sizet, count: int, key: str, key_hash: uint64, - flags: int, pos: sizet) -> int: - """已见表插入/更新:key 不存在则新登记(返回 count+1);已存在则 OR flags、更新 pos""" - if tbl is None or key is None: - return count - kl: sizet = string.strlen(key) - if kl >= _SEEN_NAME_MAX: - return count - if count >= cap: - VLogger.error(f"已见符号表满(容量 {cap}),新增符号登记被丢弃——去重失效,必须增大容量", "StubMerger") - return count - mask: sizet = cap - 1 - idx: sizet = sizet(key_hash) & mask - i: sizet = 0 - while i < cap: - off: sizet = idx * _SEEN_SLOT - hp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + off) - sh: uint64 = hp[0] - if sh == 0: - hp[0] = key_hash - pp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + off + 8) - pp[0] = uint64(pos) - tbl[off + 16] = char(flags) - string.strcpy(tbl + off + 17, key) - return count + 1 - if sh == key_hash: - nm: str = tbl + off + 17 - if string.strcmp(nm, key) == 0: - pp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + off + 8) - pp[0] = uint64(pos) - tbl[off + 16] = char(int(tbl[off + 16]) | flags) - return count - idx = (idx + 1) & mask - i += 1 - return count - -def _SeenCommentOut(tbl: bytes, cap: sizet, key: str, key_hash: uint64, - out_buf: bytes) -> int: - """注释 out_buf 中该符号的活跃行(行首改 ';'),返回 1=已注释 0=无""" - si: sizet = _SeenFind(tbl, cap, key, key_hash) - if si >= cap: - return 0 - if int(tbl[si * _SEEN_SLOT + 16]) != 0: - return 0 # 类型表 flags=1:活跃行为完整定义,无可注释的 opaque - pp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + si * _SEEN_SLOT + 8) - p: uint64 = pp[0] - if out_buf is not None: - out_buf[sizet(p)] = ';' - return 1 - -def _SeenGetPos(tbl: bytes, cap: sizet, key: str, key_hash: uint64) -> sizet: - """读取已见表条目记录的活跃行位置;不存在返回 cap(哨兵)""" - si: sizet = _SeenFind(tbl, cap, key, key_hash) - if si >= cap: - return cap - pp: uint64 | t.CPtr = (t.CPtr)(uint64(tbl) + si * _SEEN_SLOT + 8) - return sizet(pp[0]) - -def _RegisterStubLines(buf: bytes, buf_len: sizet, base_pos: sizet): - """把本地 stub(整体复制进 out_buf)中的 declare/global/type 全部登记到已见表 - - 本地 stub.ll 在 BuildCombinedIR 第 1 步整体 strcpy 进 out_buf,不做逐行 - 去重处理,若不同步登记,后续依赖的同名行查表未命中会重复追加 → - llc "redefinition of type/global"(7871cc1a... ll:758 根因)。 - 行位置 = base_pos + 行内偏移(复制是 1:1 保序),供注释操作定位。 - 同名重复行(如 61d41cf89...stub.ll 的 _g_gen 出现两次)在复制前将 - 行首改为 ';'(注释掉),表只指向首个活跃行(61d41cf89... ll:9853 根因); - 若先 opaque 后完整定义,则注释旧行、表升级指向完整定义行。 - """ - global _seen_funcs_count, _seen_globals_count, _seen_types_count - if buf is None: - return - pos: sizet = 0 - while pos < buf_len: - ls: sizet = pos - while pos < buf_len: - if buf[pos] == '\n': - break - pos += 1 - ll: sizet = pos - ls - if pos < buf_len: - pos += 1 - line_pos: sizet = base_pos + ls - # declare → 函数签名 @"name"( - if ll >= 8 and string.strncmp(buf + ls, "declare ", 8) == 0: - at: sizet = ls + 8 - while at < ls + ll: - if buf[at] == '@': - break - at += 1 - if at < ls + ll: - lp: sizet = at + 1 - while lp < ls + ll: - if buf[lp] == '(': - break - lp += 1 - if lp < ls + ll: - sv_f: char = buf[lp + 1] - buf[lp + 1] = '\0' - kh_f: uint64 = _SeenHash(buf + at) - if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, buf + at, kh_f) == 0: - _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, buf + at, kh_f, 0, line_pos) - else: - buf[ls] = ';' - buf[lp + 1] = sv_f - continue - # 全局 @name =(键 @name,截断于 '=' 前) - if ll > 0 and buf[ls] == '@': - # 字符串常量 @.str.{sha1}.{N}:本地 stub 的 external 声明是死代码 - # (本地 text.ll 有 internal constant 定义),注释掉避免与定义重复 - if ll >= 5 and buf[ls + 1] == '.' and buf[ls + 2] == 's' and buf[ls + 3] == 't' and buf[ls + 4] == 'r': - buf[ls] = ';' - continue - eq: sizet = ls + 1 - while eq < ls + ll: - if buf[eq] == '=': - break - eq += 1 - if eq < ls + ll: - sv_g: char = buf[eq] - buf[eq] = '\0' - kh_g: uint64 = _SeenHash(buf + ls) - if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, buf + ls, kh_g) == 0: - _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, buf + ls, kh_g, 0, line_pos) - else: - buf[ls] = ';' - buf[eq] = sv_g - continue - # 类型 %name = type ...(完整定义 flags=1,opaque flags=0) - if ll > 0 and buf[ls] == '%': - eq: sizet = ls + 1 - while eq < ls + ll: - if buf[eq] == '=': - break - eq += 1 - if eq < ls + ll: - sv_t: char = buf[eq] - buf[eq] = '\0' - kh_t: uint64 = _SeenHash(buf + ls) - is_full: int = 0 - if eq + 8 <= ls + ll: - if buf[eq + 2] == 't' and buf[eq + 3] == 'y' and buf[eq + 4] == 'p' and buf[eq + 5] == 'e' and buf[eq + 6] == ' ': - if buf[eq + 7] == '{' or buf[eq + 7] == '<': - is_full = 1 - prev_t: int = _SeenPresent(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t) - if prev_t == 0: - _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, is_full, line_pos) - elif is_full == 1 and prev_t == 1: - # 先 opaque 后完整定义:注释旧行,表升级指向新完整定义行 - old_pos: sizet = _SeenGetPos(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t) - if old_pos >= base_pos and old_pos < base_pos + buf_len: - buf[old_pos - base_pos] = ';' - _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, 1, line_pos) - else: - buf[ls] = ';' - buf[eq] = sv_t - continue - -# ============================================================ -# StubMergerBuild - BuildCombinedIR:组合本地 stub + 依赖 stubs + 本地 text -# -# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。 -# ============================================================ - -# ============================================================ -# _LoadAndAppendStub - 加载依赖 stub 并追加到 out_buf -# -# 读取 {temp_dir}/{sha1}.stub.ll,跳过 header(;, target, source_filename), -# 去重 declare/global,追加到 out_buf。返回新 out_pos。 -# ============================================================ -def _LoadAndAppendStub(temp_dir: str, td_len: sizet, dep_sha1: str, - dep_buf: bytes, out_buf: bytes, out_size: sizet, - out_pos: sizet) -> sizet: - """加载依赖 stub 并追加(跳过 header,去重)""" - global _seen_funcs_count, _seen_globals_count, _seen_types_count - if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: - return out_pos - - dep_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, dep_sha1, "stub.ll") - if dep_path is None: - return out_pos - df_ls: rawptr[fileio.File] = fileio.File(dep_path, fileio.MODE.R) - if df_ls.closed: - stdlib.free(dep_path) - # 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast), - # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 - if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None: - VLogger.error(f"依赖 stub 缺失: {dep_sha1}", "StubMerger") - return out_pos - dep_br_ls: int64 = df_ls.read_all(dep_buf, StubMerger.STUB_READ_BUF_SIZE) - df_ls.close() - stdlib.free(dep_path) - if dep_br_ls <= 0: - # 依赖 stub 为空:纯声明/纯常量模块(如 stdio.vp 全 pass、CDefine 模块) - # 无可声明符号,stub.ll 为空属合法形态,静默跳过 - return out_pos - if dep_br_ls < StubMerger.STUB_READ_BUF_SIZE: - dep_buf[dep_br_ls] = '\0' - - # 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要) - # 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@ - cfs_pos: sizet = 0 - while cfs_pos + 30 < sizet(dep_br_ls): - ext_p: t.CPtr = string.strstr(dep_buf + cfs_pos, "= external unnamed_addr constant") - if ext_p is None: - break - ext_off2: sizet = sizet(uint64(ext_p) - uint64(dep_buf)) - # 向前查找行首的 @ - ls: sizet = ext_off2 - while ls > 0: - if dep_buf[ls] == '\n': - ls += 1 - break - ls -= 1 - if ls < ext_off2 and dep_buf[ls] == '@': - dep_buf[ls] = ';' - cfs_pos = ext_off2 + 30 - - # 跳过 header,追加内容(去重 declare/global/type,已见符号表 O(1) 去重) - dep_pos: sizet = 0 - skipping: int = 1 - while dep_pos < dep_br_ls: - line_start: sizet = dep_pos - while dep_pos < dep_br_ls: - if dep_buf[dep_pos] == '\n': - break - dep_pos += 1 - line_len: sizet = dep_pos - line_start - if dep_pos < dep_br_ls: - dep_pos += 1 - if skipping != 0: - if line_len == 0: - continue - ch0: int = dep_buf[line_start] - if ch0 == ';': - continue - if ch0 == 't' and line_len >= 6: - if string.strncmp(dep_buf + line_start, "target", 6) == 0: - continue - if ch0 == 's' and line_len >= 15: - if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0: - continue - skipping = 0 - # 注册暂存(append 成功后才登记;reg_key 指向 scratch,insert 当轮完成) - reg_tbl: int = 0 - reg_key: str = None - reg_hash: uint64 = 0 - reg_flags: int = 0 - # 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句) - if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0: - at: sizet = line_start + 8 - while at < line_start + line_len: - if dep_buf[at] == '@': - break - at += 1 - if at < line_start + line_len: - lp: sizet = at + 1 - while lp < line_start + line_len: - if dep_buf[lp] == '(': - break - lp += 1 - if lp < line_start + line_len: - saved: char = dep_buf[lp + 1] - dep_buf[lp + 1] = '\0' - reg_hash = _SeenHash(dep_buf + at) - if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, dep_buf + at, reg_hash) != 0: - dep_buf[lp + 1] = saved - continue - string.strcpy(_seen_key_scratch, dep_buf + at) - reg_tbl = 1 - reg_key = _seen_key_scratch - reg_flags = 0 - dep_buf[lp + 1] = saved - # 去重:external global(键 @name,截断于 '=' 前) - if line_len > 0 and dep_buf[line_start] == '@': - # 字符串常量 @.str.{sha1}.{N}:依赖 stub 的 external 声明是死代码 - # (定义在模块自身 text.ll,合并 IR 中依赖方无需声明),跳过 - if line_len >= 5 and dep_buf[line_start + 1] == '.' and dep_buf[line_start + 2] == 's' \ - and dep_buf[line_start + 3] == 't' and dep_buf[line_start + 4] == 'r': - continue - eq: sizet = line_start + 1 - while eq < line_start + line_len: - if dep_buf[eq] == '=': - break - eq += 1 - if eq < line_start + line_len: - saved: char = dep_buf[eq] - dep_buf[eq] = '\0' - reg_hash = _SeenHash(dep_buf + line_start) - if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + line_start, reg_hash) != 0: - dep_buf[eq] = saved - continue - string.strcpy(_seen_key_scratch, dep_buf + line_start) - reg_tbl = 2 - reg_key = _seen_key_scratch - reg_flags = 0 - dep_buf[eq] = saved - # 去重:type 定义(如 %"sha1.Point" = type {i32, i32}) - # 区分完整定义(= type { ... } / = type < ... >)和 opaque 声明(= type opaque) - # 当本地 stub 有 opaque 声明而依赖 stub 有完整定义时,注释掉 opaque 行, - # 让完整定义被追加,避免 LLC "Cannot allocate unsized type" 错误。 - if line_len > 0 and dep_buf[line_start] == '%': - eq2: sizet = line_start + 1 - while eq2 < line_start + line_len: - if dep_buf[eq2] == '=': - break - eq2 += 1 - if eq2 < line_start + line_len: - saved2: char = dep_buf[eq2 + 1] - dep_buf[eq2 + 1] = '\0' - # 检查依赖行是否为完整定义(= type { 或 = type <) - dep_is_full_def: int = 0 - if eq2 + 8 <= line_start + line_len: - if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ': - if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<': - dep_is_full_def = 1 - saved_eq: char = dep_buf[eq2] - dep_buf[eq2] = '\0' - reg_hash = _SeenHash(dep_buf + line_start) - if dep_is_full_def == 1: - # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - _SeenCommentOut(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash, out_buf) - # 2. 检查是否已有完整定义(避免重复) - if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash) == 2: - dep_buf[eq2] = saved_eq - dep_buf[eq2 + 1] = saved2 - continue - # 3. 追加完整定义 - string.strcpy(_seen_key_scratch, dep_buf + line_start) - reg_tbl = 3 - reg_key = _seen_key_scratch - reg_flags = 1 - else: - # opaque 声明:已存在同名(任意形态)则跳过 - if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash) != 0: - dep_buf[eq2] = saved_eq - dep_buf[eq2 + 1] = saved2 - continue - string.strcpy(_seen_key_scratch, dep_buf + line_start) - reg_tbl = 3 - reg_key = _seen_key_scratch - reg_flags = 0 - dep_buf[eq2] = saved_eq - dep_buf[eq2 + 1] = saved2 - # 追加(append 成功后才登记已见表) - if out_pos + line_len + 2 < out_size: - reg_pos: sizet = out_pos - string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len) - out_pos += line_len - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - if reg_tbl == 1: - _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, reg_key, reg_hash, reg_flags, reg_pos) - elif reg_tbl == 2: - _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, reg_key, reg_hash, reg_flags, reg_pos) - elif reg_tbl == 3: - _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, reg_key, reg_hash, reg_flags, reg_pos) - return out_pos -# ============================================================ -# _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf -# -# 读取 {temp_dir}/{sha1}.text.ll,将 define 行转为 declare(提取函数签名), -# 跳过函数体,去重后追加到 out_buf。返回新 out_pos。 -# ============================================================ -def _LoadAndAppendTextDeclares(temp_dir: str, td_len: sizet, dep_sha1: str, - dep_buf: bytes, out_buf: bytes, out_size: sizet, - out_pos: sizet) -> sizet: - """从依赖 text.ll 提取 declare(define 转 declare),追加到 out_buf""" - global _seen_funcs_count, _seen_globals_count, _seen_types_count - if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: - return out_pos - - text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, dep_sha1, "text.ll") - if text_path is None: - return out_pos - tf_lt: rawptr[fileio.File] = fileio.File(text_path, fileio.MODE.R) - if tf_lt.closed: - stdlib.free(text_path) - # 依赖 text 缺失:includes 映射内的模块缺失属编译错误(fail-fast), - # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 - if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None: - VLogger.error(f"依赖 text 缺失: {dep_sha1}", "StubMerger") - return out_pos - text_br: int64 = tf_lt.read_all(dep_buf, StubMerger.STUB_READ_BUF_SIZE) - tf_lt.close() - stdlib.free(text_path) - if text_br <= 0: - # 依赖 text 为空:纯 stub 模块(如 stdio.vp 全 pass 声明)无函数体, - # text.ll 为空属合法形态,无可提取 declare,静默跳过 - return out_pos - if text_br < StubMerger.STUB_READ_BUF_SIZE: - dep_buf[text_br] = '\0' - - dep_pos_lt: sizet = 0 - in_body: int = 0 - while dep_pos_lt < text_br: - ls: sizet = dep_pos_lt - while dep_pos_lt < text_br: - if dep_buf[dep_pos_lt] == '\n': - break - dep_pos_lt += 1 - ll: sizet = dep_pos_lt - ls - if dep_pos_lt < text_br: - dep_pos_lt += 1 - - # 函数体跳过:直到单独的 } 行 - if in_body != 0: - if ll == 1 and dep_buf[ls] == '}': - in_body = 0 - continue - - # 跳过空行和 header - if ll == 0: - continue - c0: int = dep_buf[ls] - if c0 == ';': - continue - if c0 == 't' and ll >= 6 and string.strncmp(dep_buf + ls, "target", 6) == 0: - continue - if c0 == 's' and ll >= 15 and string.strncmp(dep_buf + ls, "source_filename", 15) == 0: - continue - - # 处理 define 行:转为 declare - if ll >= 7 and string.strncmp(dep_buf + ls, "define ", 7) == 0: - # 提取 @funcname( 用于去重 - at_lt: sizet = ls + 7 - while at_lt < ls + ll: - if dep_buf[at_lt] == '@': - break - at_lt += 1 - if at_lt >= ls + ll: - in_body = 1 - continue - lp_lt: sizet = at_lt + 1 - while lp_lt < ls + ll: - if dep_buf[lp_lt] == '(': - break - lp_lt += 1 - if lp_lt >= ls + ll: - in_body = 1 - continue - # 去重检查:用已见函数表(键 @"name"(,避免误匹配 call 语句) - saved_lt: char = dep_buf[lp_lt + 1] - dep_buf[lp_lt + 1] = '\0' - fh_lt: uint64 = _SeenHash(dep_buf + at_lt) - if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, dep_buf + at_lt, fh_lt) != 0: - dep_buf[lp_lt + 1] = saved_lt - in_body = 1 - continue - string.strcpy(_seen_key_scratch, dep_buf + at_lt) - dep_buf[lp_lt + 1] = saved_lt - - # 找行尾的 ' {' 并截断 - decl_end: sizet = ls + ll - if decl_end > ls + 1: - if dep_buf[decl_end - 1] == '{' and dep_buf[decl_end - 2] == ' ': - decl_end -= 2 - - # 剥掉行尾的**函数级元数据附加**(`!dbg !71`;可多组如 `!dbg !71 !prof !72`)。 - # declare **不接受**函数级元数据附加:llc 直接报 "expected '='"(实证 - # LLvmLiteTest 合并产物 949685246c8e241c.ll:898:104 — - # `declare void @"…GSList[Param].__init__"(…) !dbg !71`)。这条残留正是 - # "文本降级 define→declare"自身带出来的:源 define 头行形如 - # `define linkonce_odr void @"…"(…) !dbg !71 {`,上面只删掉了尾部 `" {"`, - # 其后的 ` !dbg !71` 被原样抄下。declare 无函数体,!dbg 本就无意义 - # (FunctionPrint 对 declare 从不打印 !dbg,见 llvmlite/__function.vp 的 - # IsDeclared==1 提前返回分支),故在此直接截断。 - # 判据:参数表之后、括号深度 0、且不在双引号内的 ` !`(元数据附加恒在行尾, - # 裸标识符不允许含 '!',唯一可能含 '!' 的是带引号的符号名 → 用引号态排除)。 - meta_pos_lt: sizet = ls - meta_depth_lt: int = 0 - meta_in_str_lt: int = 0 - meta_cut_lt: sizet = 0 - while meta_pos_lt < decl_end: - meta_ch_lt: int = dep_buf[meta_pos_lt] - if meta_ch_lt == '"': - meta_in_str_lt = 1 - meta_in_str_lt - elif meta_in_str_lt == 0: - if meta_ch_lt == '(' or meta_ch_lt == '[' or meta_ch_lt == '{': - meta_depth_lt += 1 - elif meta_ch_lt == ')' or meta_ch_lt == ']' or meta_ch_lt == '}': - if meta_depth_lt > 0: - meta_depth_lt -= 1 - elif meta_ch_lt == '!' and meta_depth_lt == 0 and meta_pos_lt > ls \ - and dep_buf[meta_pos_lt - 1] <= ' ': - meta_cut_lt = meta_pos_lt - break - meta_pos_lt += 1 - if meta_cut_lt != 0: - # 连同前导空白一起截掉,避免残留行尾空白 - # (`<= ' '` 同时覆盖空格与制表符,且不会误吃 ')' 等非空白字符) - while meta_cut_lt > ls and dep_buf[meta_cut_lt - 1] <= ' ': - meta_cut_lt -= 1 - decl_end = meta_cut_lt - - # 输出: declare + content(跳过 "define " 及其后所有 linkage 词,至多到 decl_end)。 - # 泛型特化方法 define 名为 "define linkonce_odr ..."(名含 '['):只跳 "define " 会拼出 "declare linkonce_odr ..." → - # llc 报 "invalid linkage for function declaration"(declare 只能 external),故循环剥掉全部 linkage 词。 - c_start_lt: sizet = ls + 7 - skipped_lt: int = 1 - while skipped_lt != 0: - skipped_lt = 0 - if c_start_lt + 13 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "linkonce_odr ", 13) == 0: - c_start_lt += 13 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "weak_odr ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "linkonce ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 6 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "weak ", 5) == 0: - c_start_lt += 5 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "internal ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 9 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "private ", 8) == 0: - c_start_lt += 8 - skipped_lt = 1 - copy_len_lt: sizet = decl_end - c_start_lt - if out_pos + 8 + copy_len_lt + 2 < out_size: - decl_pos_lt: sizet = out_pos - string.strncpy(out_buf + out_pos, "declare ", 8) - out_pos += 8 - string.strncpy(out_buf + out_pos, dep_buf + c_start_lt, copy_len_lt) - out_pos += copy_len_lt - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, _seen_key_scratch, fh_lt, 0, decl_pos_lt) - in_body = 1 - continue - - # 提取类型定义(%... = type { ... } 或 %... = type < ... >) - # stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义 - # 用于 alloca 和 getelementptr 等需要完整类型的指令 - if ll > 0 and dep_buf[ls] == '%': - eq_td: sizet = ls + 1 - while eq_td < ls + ll: - if dep_buf[eq_td] == '=': - break - eq_td += 1 - if eq_td < ls + ll: - # 检查是否为完整定义(= type { 或 = type <) - dep_is_full_td: int = 0 - if eq_td + 8 <= ls + ll: - if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \ - and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \ - and dep_buf[eq_td + 6] == ' ': - if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<': - dep_is_full_td = 1 - if dep_is_full_td == 1: - # 临时截断到 '=' 之前(不含 '='),键统一为 %name - saved_eq_td: char = dep_buf[eq_td] - dep_buf[eq_td] = '\0' - th_td: uint64 = _SeenHash(dep_buf + ls) - # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - _SeenCommentOut(_seen_types, _SEEN_TYPE_CAP, dep_buf + ls, th_td, out_buf) - # 2. 检查是否已有完整定义(避免重复) - if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + ls, th_td) == 2: - dep_buf[eq_td] = saved_eq_td - continue - string.strcpy(_seen_key_scratch, dep_buf + ls) - dep_buf[eq_td] = saved_eq_td - # 追加完整类型定义 - if out_pos + ll + 2 < out_size: - td_pos: sizet = out_pos - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, _seen_key_scratch, th_td, 1, td_pos) - continue - - # 提取全局变量定义(@... = ...) - # 依赖 text.ll 中的全局变量带初值定义(如 @StubMerger._mbuddy = global ...) - # stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义 - # 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义 - if ll > 0 and dep_buf[ls] == '@': - # 字符串常量 @.str.{sha1}.{N}:依赖 text.ll 中的 internal 定义是死代码 - # (定义模块自身 .obj 已有,合并 IR 中依赖方无需重复),跳过 - if ll >= 5 and dep_buf[ls + 1] == '.' and dep_buf[ls + 2] == 's' and dep_buf[ls + 3] == 't' \ - and dep_buf[ls + 4] == 'r': - continue - eq_gv: sizet = ls + 1 - while eq_gv < ls + ll: - if dep_buf[eq_gv] == '=': - break - eq_gv += 1 - if eq_gv < ls + ll: - # 截断到 '=' 之前(不含 '='),键统一为 @name - saved_gv: char = dep_buf[eq_gv] - dep_buf[eq_gv] = '\0' - gh_gv: uint64 = _SeenHash(dep_buf + ls) - # 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义)时跳过:跨模块全局(如 MemBuddy.vtable) - # 只需一份强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致 ld.lld duplicate symbol。 - # 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。 - if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + ls, gh_gv) == 0: - # 追加完整定义(无同名行时注释旧行是 no-op) - string.strcpy(_seen_key_scratch, dep_buf + ls) - dep_buf[eq_gv] = saved_gv - if out_pos + ll + 2 < out_size: - gv_pos: sizet = out_pos - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, _seen_key_scratch, gh_gv, 0, gv_pos) - else: - dep_buf[eq_gv] = saved_gv - continue - - # 跳过其他行 - return out_pos # ============================================================ # _IsAppModule - 模块名是否为 App 源文件(store 条目 incdir 为空) # @@ -1259,140 +570,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, stdlib.free(wl_pos) return reachable_count -# ============================================================ -# 类型定义前移(llc: "invalid type for function argument") -# -# by-value 结构体参数引用的具名类型必须在 declare 点之前定义,否则 llc 报 invalid type for function argument。 -# 本遍是纯置换(长度不变、行内容逐字不变):合并阶段 _seen_types(本文件 L1646-1667)已按类型名保证「活跃定义唯一」,重复行不会进入 out_buf。 -# ============================================================ -def _IsTypeDefLine(buf: bytes, ls: sizet, le: sizet) -> int: - """buf[ls, le)(不含换行)是否为 `%name = type ...` 类型定义行 - - 对齐 TPC L3064 的 `stripped.startswith('%') and '= type' in stripped`, - 但把裸子串判定收紧为词判定:'= type' 也会出现在函数体字符串常量里 - (如 c"= type\\00"),裸子串判定会把普通指令行当类型定义搬走 → 破坏 IR。 - """ - k: sizet = ls - while k < le and (buf[k] == ' ' or buf[k] == '\t'): - k += 1 - if k >= le or buf[k] != '%': - return 0 - j: sizet = k - while j + 6 <= le: - if string.strncmp(buf + j, "= type", 6) == 0: - if j + 6 == le: - return 1 - e: char = buf[j + 6] - # 'type' 后只能是空白 / '{' / '<'(= type { ... } / opaque / <{...}>) - if e == ' ' or e == '\t' or e == '{' or e == '<': - return 1 - j += 1 - return 0 - - -def _IsIrHeaderLine(buf: bytes, ls: sizet, le: sizet) -> int: - """buf[ls, le)(不含换行)是否属于 ll header(类型定义插在其后) - - 对齐 TPC L3076 的 header 判据:空行 / ';' 注释 / 'target ' / - 'source_filename' / '!' 元数据(行首空白先跳过,等价 TPC 的 stripped)。 - """ - k: sizet = ls - while k < le and (buf[k] == ' ' or buf[k] == '\t' or buf[k] == '\r'): - k += 1 - if k >= le: - return 1 - if buf[k] == ';' or buf[k] == '!': - return 1 - if le - k >= 7 and string.strncmp(buf + k, "target ", 7) == 0: - return 1 - if le - k >= 15 and string.strncmp(buf + k, "source_filename", 15) == 0: - return 1 - return 0 - - -def HoistTypeDefsBeforeDeclares(buf: bytes, length: sizet) -> sizet: - """把所有 `%name = type ...` 行上移到 IR header 之后(见上方注释) - - 纯文本行级重排:行内容逐字不变、类型定义之间相对顺序不变、非类型定义行 - 相对顺序不变;插入点与 TPC 一致(header 之后第一行;若首个非 header 行 - 就在文件首行或无 header 行,按 TPC 同规则追加末尾)。 - 幂等:已是"定义在 header 之后"时结果与输入逐字节相同。 - 返回重排后的长度(有类型定义行时恒为 length)。 - """ - if buf is None or length == 0: - return length - # 1. 统计类型定义行总长(整行含换行) - types_len: sizet = 0 - p: sizet = 0 - while p < length: - le: sizet = p - while le < length and buf[le] != '\n': - le += 1 - step: sizet = le - p - if le < length: - step += 1 - if _IsTypeDefLine(buf, p, le) != 0: - types_len += step - p += step - if types_len == 0: - return length - tmp: bytes = stdlib.malloc(types_len + 1) - if tmp is None: - # VLogger.error 直接终止进程(不静默降级为"未前移") - VLogger.error("类型定义前移暂存缓冲分配失败", "StubMerger") - return length - # 2. 类型定义行抽到 tmp、其余行原地压缩(只删行不增行,w <= p 恒成立) - tmp_pos: sizet = 0 - w: sizet = 0 - p = 0 - while p < length: - le = p - while le < length and buf[le] != '\n': - le += 1 - step = le - p - if le < length: - step += 1 - if _IsTypeDefLine(buf, p, le) != 0: - string.memmove(tmp + tmp_pos, buf + p, step) - tmp_pos += step - else: - if w != p: - string.memmove(buf + w, buf + p, step) - w += step - p += step - tmp[tmp_pos] = '\0' - # 3. 求插入点:过滤后文本里 header 之后的首行起始偏移 - ins: sizet = 0 - hit: int = 0 - q: sizet = 0 - while q < w: - qe: sizet = q - while qe < w and buf[qe] != '\n': - qe += 1 - if _IsIrHeaderLine(buf, q, qe) == 0: - ins = q - hit = 1 - break - if qe >= w: - break - q = qe + 1 - if hit == 0 or ins == 0: - # 对齐 TPC L3079-3080:insert_pos == len(other_lines) - ins = w - # 4. 腾出 tmp_pos 字节空隙并拷入(长度不变;右移用 memmove 保证重叠安全) - tail: sizet = w - ins - if tail > 0: - string.memmove(buf + ins + tmp_pos, buf + ins, tail) - string.memmove(buf + ins, tmp, tmp_pos) - new_len: sizet = w + tmp_pos - buf[new_len] = '\0' - stdlib.free(tmp) - return new_len - # ============================================================ # LoadFullIR - 读 {temp_dir}/{sha1}.full.ll 到 out_buf(④-c:合并期改用在线 FULL 产物) # -# 与 BuildCombinedIR 同契约:返回写入长度(sizet;0=失败),out_buf 以 '\0' 结尾。 +# 契约:返回写入长度(sizet;0=失败),out_buf 以 '\0' 结尾。 # full.ll 由翻译点在模型在线时 dump_ir(OUTPUT_FULL) 产出 —— 自足的单模块 IR # (类型定义 + external 声明 + 定义 + DI 一次齐全,类型定义天然在声明之前), # 无需再把 stub + 依赖 stub/text 文本合并。 @@ -1422,443 +603,6 @@ def LoadFullIR(temp_dir: str, sha1: str, out_buf[rd] = '\0' return sizet(rd) - -# ============================================================ -# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR -# -# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1, -# 仅加载实际使用的依赖 stub(而非全部扫描)。 -# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。 -# ============================================================ -def BuildCombinedIR(temp_dir: str, local_sha1: str, - out_buf: bytes, out_size: sizet) -> sizet: - """组合本地 stub + 依赖 stubs + 本地 text → out_buf(按需加载)""" - global _seen_funcs, _seen_funcs_count, _seen_globals, _seen_globals_count - global _seen_types, _seen_types_count, _seen_key_scratch - if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0: - return 0 - - td_len: sizet = string.strlen(temp_dir) - out_buf[0] = '\0' - out_pos: sizet = 0 - - # 已见符号表重置(跨构建复用缓冲,仅清零计数 + 全槽位清零) - if _seen_funcs is None: - _seen_funcs = stdlib.malloc(_SEEN_FUNC_CAP * _SEEN_SLOT) - if _seen_funcs is None: - VLogger.error("已见函数表分配失败", "StubMerger") - return 0 - if _seen_globals is None: - _seen_globals = stdlib.malloc(_SEEN_GLOBAL_CAP * _SEEN_SLOT) - if _seen_globals is None: - VLogger.error("已见全局表分配失败", "StubMerger") - return 0 - if _seen_types is None: - _seen_types = stdlib.malloc(_SEEN_TYPE_CAP * _SEEN_SLOT) - if _seen_types is None: - VLogger.error("已见类型表分配失败", "StubMerger") - return 0 - if _seen_key_scratch is None: - _seen_key_scratch = stdlib.malloc(_SEEN_NAME_MAX + 1) - if _seen_key_scratch is None: - VLogger.error("已见表键暂存分配失败", "StubMerger") - return 0 - string.memset(_seen_funcs, 0, _SEEN_FUNC_CAP * _SEEN_SLOT) - string.memset(_seen_globals, 0, _SEEN_GLOBAL_CAP * _SEEN_SLOT) - string.memset(_seen_types, 0, _SEEN_TYPE_CAP * _SEEN_SLOT) - _seen_funcs_count = 0 - _seen_globals_count = 0 - _seen_types_count = 0 - - # 1. 读取并追加本地 stub.ll(含 header) - stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "stub.ll") - if stub_path is None: - return 0 - sf: rawptr[fileio.File] = fileio.File(stub_path, fileio.MODE.R) - if sf.closed: - stdlib.free(stub_path) - return 0 - stub_content: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE) - if stub_content is None: - sf.close() - stdlib.free(stub_path) - return 0 - stub_br: int64 = sf.read_all(stub_content, StubMerger.STUB_READ_BUF_SIZE) - sf.close() - stdlib.free(stub_path) - if stub_br <= 0: - stdlib.free(stub_content) - return 0 - if stub_br < StubMerger.STUB_READ_BUF_SIZE: - stub_content[stub_br] = '\0' - slen: sizet = stub_br - # 修复本地 stub.ll 中字符串常量的非法 IR: - # llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。 - # 简单方案:搜索 "= external unnamed_addr constant" 并将 external 替换为 8 个空格。 - fix_pos: sizet = 0 - while fix_pos + 30 < slen: - ext_ptr: t.CPtr = string.strstr(stub_content + fix_pos, "= external unnamed_addr constant") - if ext_ptr is None: - break - # ext_ptr 指向 "= external ..." 中的 '=' - # 'external' 开始于 ext_ptr + 2 - ext_off: sizet = sizet(uint64(ext_ptr) - uint64(stub_content)) + 2 - if ext_off + 8 <= slen: - stub_content[ext_off] = ' ' - stub_content[ext_off + 1] = ' ' - stub_content[ext_off + 2] = ' ' - stub_content[ext_off + 3] = ' ' - stub_content[ext_off + 4] = ' ' - stub_content[ext_off + 5] = ' ' - stub_content[ext_off + 6] = ' ' - stub_content[ext_off + 7] = ' ' - fix_pos = ext_off + 8 - # 直接复制修复后的 stub 内容(复制前先登记其 declare/global/type, - # 保持已见表与 out_buf 一致,避免后续依赖重复追加 redefinition) - if out_pos + slen + 2 < out_size: - _RegisterStubLines(stub_content, slen, out_pos) - string.strcpy(out_buf + out_pos, stub_content) - out_pos += slen - if out_buf[out_pos - 1] != '\n': - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - stdlib.free(stub_content) - - # 2. 按需加载依赖 stubs(根据 deps.txt 过滤,而非扫描全部) - # 先加载依赖 stub(含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前 - # 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器) - sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) - mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) - inc_count: int = 0 - if sha1_arr is not None and mod_arr is not None: - inc_count = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len, sha1_arr, mod_arr) - - # 2b. 读取 deps.txt 获取导入模块名集合 - deps_buf: str = None - deps_loaded: int = 0 - deps_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "deps.txt") - if deps_path is not None: - df_deps: rawptr[fileio.File] = fileio.File(deps_path, fileio.MODE.R) - if not df_deps.closed: - deps_buf = stdlib.malloc(4096) - if deps_buf is not None: - deps_br: int64 = df_deps.read_all(deps_buf, 4096) - if deps_br > 0: - if deps_br < 4096: - deps_buf[deps_br] = '\0' - deps_loaded = 1 - elif deps_br == 0: - # 空 deps.txt:无 import 的包标记文件(如纯注释的 lib/core/.../__init__.vp)合法——deps_loaded 置 1 视为「已加载且无依赖」, - # 避免下方「deps.txt 缺失」误报;deps_buf 是 malloc 未初始化内存,必须显式置空串,否则后续 strlen 读到垃圾长度遍历越界。 - deps_buf[0] = '\0' - deps_loaded = 1 - df_deps.close() - stdlib.free(deps_path) - - # deps.txt 缺失但存在 includes 映射:依赖无法按需过滤,fail-fast 终止 - if deps_loaded == 0 and inc_count > 0: - VLogger.error("依赖文件 deps.txt 缺失,无法合并 includes stub", "StubMerger") - - # 2c. 递归扫描 temp_dir 中所有 .stub.ll,按需加载 - # 全目录扫描结果按 temp_dir 缓存(Phase B 对每个源文件调用一次 - # BuildCombinedIR,每次递归扫盘 O(N×S) 是 Phase B 慢的根因之一; - # 首次扫描后复用缓存,temp_dir 变化时自动重建) - cache_valid_sc: int = 0 - if StubMerger._stub_scan_cache_arr is not None and StubMerger._stub_scan_cache_dir is not None: - if string.strcmp(StubMerger._stub_scan_cache_dir, temp_dir) == 0: - cache_valid_sc = 1 - if cache_valid_sc == 0: - new_scan_arr: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17) - if new_scan_arr is not None: - string.memset(new_scan_arr, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) - new_scan_count: int = StubMergerCollect._collect_stub_sha1s( - temp_dir, td_len, Config.Sha1SliceLevel, new_scan_arr, 0, StubMerger.MAX_INCLUDES_SHA1) - if new_scan_count > 0: - # 缓存目录键(stdlib 深拷贝,跨调用存活) - dir_buf_sc: str = stdlib.malloc(td_len + 1) - if dir_buf_sc is not None: - string.strcpy(dir_buf_sc, temp_dir) - if StubMerger._stub_scan_cache_arr is not None: - stdlib.free(StubMerger._stub_scan_cache_arr) - StubMerger._stub_scan_cache_arr = None - if StubMerger._stub_scan_cache_dir is not None: - stdlib.free(StubMerger._stub_scan_cache_dir) - StubMerger._stub_scan_cache_dir = None - StubMerger._stub_scan_cache_arr = new_scan_arr - StubMerger._stub_scan_cache_dir = dir_buf_sc - StubMerger._stub_scan_cache_count = new_scan_count - else: - stdlib.free(new_scan_arr) - else: - stdlib.free(new_scan_arr) - all_sha1_arr: str = StubMerger._stub_scan_cache_arr - all_count: int = StubMerger._stub_scan_cache_count - if all_sha1_arr is not None and all_count > 0: - dep_buf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE) - if dep_buf is not None: - si: int = 0 - while si < all_count: - dep_sha1: str = all_sha1_arr + sizet(si) * 17 - if string.strcmp(dep_sha1, local_sha1) == 0: - si += 1 - continue - should_load: int = 0 - # 检查是否在 includes 映射中 - is_include: int = 0 - if inc_count > 0: - for ii in range(inc_count): - idx_ii: sizet = sizet(ii) * 17 - if string.strcmp(sha1_arr + idx_ii, dep_sha1) != 0: - continue - is_include = 1 - if deps_loaded != 0: - idx_mi: sizet = sizet(ii) * 64 - dep_mod_name: str = mod_arr + idx_mi - if GenApi.is_module_imported(deps_buf, dep_mod_name) != 0: - should_load = 1 - elif StubMergerIR._is_submodule_of_imported(deps_buf, dep_mod_name) != 0: - # 传递性依赖:deps.txt 只记录直接导入(如 ast),但包的 __init__.vp 内部 from .lexer import Lexer - # 会让 IR 引用 ast.lexer 子模块类型,故还需检查 dep_mod_name 是否为已导入包的子模块。 - should_load = 1 - else: - # 基名匹配:store 键是全路径模块名(Kernel.drivers.fs.fat32.fat32_types),而 deps.txt 记录 import 短名(fat32_types) - # → 精确/子模块均不命中 → 依赖 stub/text 不加载 → 跨模块类完整定义缺失(值类型 alloca 报 llc "Cannot allocate unsized type")。 - # 故取最后一个 '.' 之后的基名与 deps 词边界匹配(多加载无害,去重表兜底)。 - last_dot_bm: str = string.strrchr(dep_mod_name, '.') - if last_dot_bm is not None and last_dot_bm[1] != '\0': - if GenApi.is_module_imported(deps_buf, last_dot_bm + 1) != 0: - should_load = 1 - break - if is_include == 0: - # 非 includes stub(用户文件),总是加载 - should_load = 1 - if should_load != 0: - out_pos = _LoadAndAppendStub(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) - out_pos = _LoadAndAppendTextDeclares(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) - si += 1 - stdlib.free(dep_buf) - # all_sha1_arr 是模块级缓存(StubMerger._stub_scan_cache_arr),跨调用共享,不释放 - - # 2d. 验证 deps.txt 中所有依赖模块的 stub 文件都存在(fail-fast) - # 避免到 llc 才报 undefined value 错误。 - # 仅验证在 includes 映射中找到的模块(t/c 等内部库无 SHA1 映射,自动跳过)。 - if deps_loaded != 0 and inc_count > 0: - dep_total: sizet = string.strlen(deps_buf) - dp: sizet = 0 - while dp < dep_total: - # 跳过前导空格 - while dp < dep_total: - if deps_buf[dp] != ' ': - break - dp += 1 - if dp >= dep_total: - break - # 提取模块名(到下一个空格或末尾) - ms: sizet = dp - while dp < dep_total: - if deps_buf[dp] == ' ': - break - dp += 1 - ml: sizet = dp - ms - if ml == 0: - continue - # 复制模块名到临时缓冲区并 null 终止 - mod_nm: str = stdlib.malloc(ml + 1) - if mod_nm is None: - continue - string.strncpy(mod_nm, deps_buf + ms, ml) - mod_nm[ml] = '\0' - # 在 includes 映射中查找模块对应的 SHA1 - found_s: str = None - for mi2 in range(inc_count): - idx_m2: sizet = sizet(mi2) * 64 - if string.strcmp(mod_arr + idx_m2, mod_nm) == 0: - idx_s2: sizet = sizet(mi2) * 17 - found_s = sha1_arr + idx_s2 - break - if found_s is not None: - # 检查 stub 文件是否存在 - chk_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, found_s, "stub.ll") - if chk_path is not None: - chk_f: rawptr[fileio.File] = fileio.File(chk_path, fileio.MODE.R) - if chk_f.closed: - VLogger.error(f"依赖模块 '{mod_nm}' (sha1={found_s}) 的 stub 文件不存在: {chk_path},立即终止编译", "BuildCombinedIR") - chk_f.close() - stdlib.free(chk_path) - stdlib.free(mod_nm) - - # 3. 读取并追加本地 text.ll(行级去重,跳过 stub.ll 中已存在的定义) - text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "text.ll") - if text_path is None: - return out_pos - tf: rawptr[fileio.File] = fileio.File(text_path, fileio.MODE.R) - if tf.closed: - # 本地 text.ll 缺失:合并 IR 无法生成定义块,fail-fast 终止 - VLogger.error(f"本地 text.ll 缺失: {text_path}", "StubMerger") - stdlib.free(text_path) - else: - text_content: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE) - if text_content is None: - tf.close() - stdlib.free(text_path) - else: - text_br: int64 = tf.read_all(text_content, StubMerger.STUB_READ_BUF_SIZE) - tf.close() - stdlib.free(text_path) - if text_br > 0: - if text_br < StubMerger.STUB_READ_BUF_SIZE: - text_content[text_br] = '\0' - # 行级去重追加(与 _LoadAndAppendStub 相同逻辑) - tx_pos: sizet = 0 - tx_skip: int = 1 - while tx_pos < text_br: - tx_ls: sizet = tx_pos - while tx_pos < text_br: - if text_content[tx_pos] == '\n': - break - tx_pos += 1 - tx_ll: sizet = tx_pos - tx_ls - if tx_pos < text_br: - tx_pos += 1 - if tx_skip != 0: - if tx_ll == 0: - continue - tx_c0: int = text_content[tx_ls] - if tx_c0 == ';': - continue - if tx_c0 == 't' and tx_ll >= 6: - if string.strncmp(text_content + tx_ls, "target", 6) == 0: - continue - if tx_c0 == 's' and tx_ll >= 15: - if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0: - continue - tx_skip = 0 - # 注册暂存(append 成功后才登记;tx_key 指向 scratch,insert 当轮完成) - tx_reg: int = 0 - tx_key: str = None - tx_hash: uint64 = 0 - tx_flags: int = 0 - # 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句) - if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0: - tx_at: sizet = tx_ls + 8 - while tx_at < tx_ls + tx_ll: - if text_content[tx_at] == '@': - break - tx_at += 1 - if tx_at < tx_ls + tx_ll: - tx_lp: sizet = tx_at + 1 - while tx_lp < tx_ls + tx_ll: - if text_content[tx_lp] == '(': - break - tx_lp += 1 - if tx_lp < tx_ls + tx_ll: - tx_sv: char = text_content[tx_lp + 1] - text_content[tx_lp + 1] = '\0' - tx_hash = _SeenHash(text_content + tx_at) - if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, text_content + tx_at, tx_hash) != 0: - text_content[tx_lp + 1] = tx_sv - continue - string.strcpy(_seen_key_scratch, text_content + tx_at) - tx_reg = 1 - tx_key = _seen_key_scratch - tx_flags = 0 - text_content[tx_lp + 1] = tx_sv - # 去重:define(注释 out_buf 中已有 declare,避免 declare/define 冲突) - if tx_ll >= 7 and string.strncmp(text_content + tx_ls, "define ", 7) == 0: - tx_at_d: sizet = tx_ls + 7 - while tx_at_d < tx_ls + tx_ll: - if text_content[tx_at_d] == '@': - break - tx_at_d += 1 - if tx_at_d < tx_ls + tx_ll: - tx_lp_d: sizet = tx_at_d + 1 - while tx_lp_d < tx_ls + tx_ll: - if text_content[tx_lp_d] == '(': - break - tx_lp_d += 1 - if tx_lp_d < tx_ls + tx_ll: - tx_sv_d: char = text_content[tx_lp_d + 1] - text_content[tx_lp_d + 1] = '\0' - tx_hash = _SeenHash(text_content + tx_at_d) - _SeenCommentOut(_seen_funcs, _SEEN_FUNC_CAP, text_content + tx_at_d, tx_hash, out_buf) - string.strcpy(_seen_key_scratch, text_content + tx_at_d) - tx_reg = 1 - tx_key = _seen_key_scratch - tx_flags = 0 - text_content[tx_lp_d + 1] = tx_sv_d - # 处理:@global = ... - # stub 中可能是 external 声明或完整定义,以 text 中的定义为准 - # 注释 out_buf 中同名全局行,然后追加 text 中的定义(键 @name,截断于 '=' 前) - if tx_ll > 0 and text_content[tx_ls] == '@': - tx_eq: sizet = tx_ls + 1 - while tx_eq < tx_ls + tx_ll: - if text_content[tx_eq] == '=': - break - tx_eq += 1 - if tx_eq < tx_ls + tx_ll: - tx_sv2: char = text_content[tx_eq] - text_content[tx_eq] = '\0' - tx_hash = _SeenHash(text_content + tx_ls) - _SeenCommentOut(_seen_globals, _SEEN_GLOBAL_CAP, text_content + tx_ls, tx_hash, out_buf) - string.strcpy(_seen_key_scratch, text_content + tx_ls) - tx_reg = 2 - tx_key = _seen_key_scratch - tx_flags = 0 - text_content[tx_eq] = tx_sv2 - # 去重:%type = ...(键 %name,截断于 '=' 前) - if tx_ll > 0 and text_content[tx_ls] == '%': - tx_eq2: sizet = tx_ls + 1 - while tx_eq2 < tx_ls + tx_ll: - if text_content[tx_eq2] == '=': - break - tx_eq2 += 1 - if tx_eq2 < tx_ls + tx_ll: - tx_sv3: char = text_content[tx_eq2] - text_content[tx_eq2] = '\0' - tx_is_full: int = 0 - if tx_eq2 + 8 <= tx_ls + tx_ll: - if text_content[tx_eq2 + 2] == 't' and text_content[tx_eq2 + 3] == 'y' \ - and text_content[tx_eq2 + 4] == 'p' and text_content[tx_eq2 + 5] == 'e' \ - and text_content[tx_eq2 + 6] == ' ': - if text_content[tx_eq2 + 7] == '{' or text_content[tx_eq2 + 7] == '<': - tx_is_full = 1 - tx_hash = _SeenHash(text_content + tx_ls) - if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, text_content + tx_ls, tx_hash) != 0: - text_content[tx_eq2] = tx_sv3 - continue - string.strcpy(_seen_key_scratch, text_content + tx_ls) - tx_reg = 3 - tx_key = _seen_key_scratch - tx_flags = tx_is_full - text_content[tx_eq2] = tx_sv3 - # 追加(append 成功后才登记已见表) - if out_pos + tx_ll + 2 < out_size: - tx_reg_pos: sizet = out_pos - string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll) - out_pos += tx_ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - if tx_reg == 1: - _seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, tx_key, tx_hash, tx_flags, tx_reg_pos) - elif tx_reg == 2: - _seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, tx_key, tx_hash, tx_flags, tx_reg_pos) - elif tx_reg == 3: - _seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, tx_key, tx_hash, tx_flags, tx_reg_pos) - stdlib.free(text_content) - - if deps_buf is not None: - stdlib.free(deps_buf) - if sha1_arr is not None: - stdlib.free(sha1_arr) - if mod_arr is not None: - stdlib.free(mod_arr) - - # 类型定义前移:llc 要求 by-value 结构体参数引用的具名类型在 declare 点之前定义(否则 "invalid type for function argument")。 - # 落点:Phase2 的 write_ir_to_file 逐字节写本缓冲区(BuildPipeline.vp L289-314,无文本加工),故在此完成即覆盖 llc 的全部输入。 - out_pos = HoistTypeDefsBeforeDeclares(out_buf, out_pos) - return out_pos # ============================================================ # LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块 # diff --git a/App/lib/Stub/StubMerger/StubMergerCollect.vp b/App/lib/Stub/StubMerger/StubMergerCollect.vp index c7d5507..401b823 100644 --- a/App/lib/Stub/StubMerger/StubMergerCollect.vp +++ b/App/lib/Stub/StubMerger/StubMergerCollect.vp @@ -57,7 +57,7 @@ def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str: # 确保子目录存在: {temp_dir}/{subdir} # 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True), - # 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。 + # 进而读取端读不到文件返回 0("加载 IR 失败")。 dir_path_len: sizet = td_len + sub_len + 2 dir_path: bytes = stdlib.malloc(dir_path_len) if dir_path is not None: @@ -80,86 +80,7 @@ def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext) return path -# ============================================================ -# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1(从文件名提取) -# -# level=0 → 扫描 {base_dir}/*.stub.ll;level>0 → 扫描 {base_dir}/* 的子目录并对每个子目录递归调用。 -# -# Args: base_dir 当前扫描目录;bd_len base_dir 长度;level 剩余递归层数;out_arr 输出数组(每个 SHA1 17 字节 = 16字符 + null);out_count 当前已收集数量;max_count 最大数量 -# Returns: 收集后的总数量 -# ============================================================ -def _collect_stub_sha1s(base_dir: str, bd_len: sizet, level: int, - out_arr: str, out_count: int, max_count: int) -> int: - """递归扫描子目录,收集 .stub.ll 文件的 SHA1""" - if base_dir is None or out_arr is None: - return out_count - if out_count >= max_count: - return out_count - find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) - if find_data is None: - return out_count - string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__()) - - if level <= 0: - # 扫描 {base_dir}/*.stub.ll - pattern: bytes = stdlib.malloc(bd_len + 16) - if pattern is None: - stdlib.free(find_data) - return out_count - viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir) - handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) - stdlib.free(pattern) - if handle != win32base.INVALID_HANDLE_VALUE: - while True: - if out_count >= max_count: - break - fname: str = find_data.cFileName - if fname is not None: - if fname[0] != '\0': - # 从文件名提取 SHA1(前 16 字符) - string.strncpy(out_arr + out_count * 17, fname, 16) - out_arr[out_count * 17 + 16] = '\0' - out_count += 1 - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32file.FindClose(handle) - else: - # 扫描 {base_dir}/* 的子目录 - pattern = stdlib.malloc(bd_len + 4) - if pattern is None: - stdlib.free(find_data) - return out_count - viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir) - handle = win32file.FindFirstFileA(pattern, find_data) - stdlib.free(pattern) - if handle != win32base.INVALID_HANDLE_VALUE: - while True: - # 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16) - attrs: uint32 = find_data.dwFileAttributes - if (attrs & 16) != 0: - fname = find_data.cFileName - if fname is not None: - # 跳过 "." 和 ".." - is_dot: int = 0 - if fname[0] == '.': - if fname[1] == '\0': - is_dot = 1 - elif fname[1] == '.' and fname[2] == '\0': - is_dot = 1 - if is_dot == 0: - # 构建子目录路径并递归扫描 - sub_dir: str = Utils.JoinPath(base_dir, fname) - if sub_dir is not None: - sub_dir_len: sizet = string.strlen(sub_dir) - out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count) - stdlib.free(sub_dir) - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32file.FindClose(handle) - - stdlib.free(find_data) - return out_count # ============================================================ # _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1 # diff --git a/App/lib/Stub/StubMerger/__init__.vp b/App/lib/Stub/StubMerger/__init__.vp index 2af57b2..e720ed6 100644 --- a/App/lib/Stub/StubMerger/__init__.vp +++ b/App/lib/Stub/StubMerger/__init__.vp @@ -31,9 +31,9 @@ _mbuddy: memhub.MemBuddy* # 最大 includes SHA1 数(用于过滤) MAX_INCLUDES_SHA1: t.CDefine = 1024 -# stub 读取缓冲区大小 -# 1MB 不够:ast/parser.vp 的 text.ll >1MB,BuildCombinedIR 读取本地 text.ll -# 单次截断 → 合并 IR 尾部丢字(opt: "end of file in global variable name")。 +# stub/text 读取缓冲区大小 +# 1MB 不够:ast/parser.vp 的 text.ll >1MB,读取本地 text.ll 单次截断 +# → 尾部丢字(opt: "end of file in global variable name")。 STUB_READ_BUF_SIZE: t.CDefine = 8388608 # 最大 includes 条目数 MAX_INCLUDES: t.CDefine = 1024 @@ -56,12 +56,6 @@ _includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个 _includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个) _includes_deps_count: int # 条目数 -# BuildCombinedIR 全目录扫描缓存:Phase B 每个源文件调用一次 BuildCombinedIR,而 _collect_stub_sha1s 会递归枚举 -# temp_dir 全部切片子目录(O(N×S) 目录枚举很慢);按 temp_dir 缓存扫描结果,首次扫描后复用,temp_dir 变化时自动重建。 -_stub_scan_cache_dir: str # 缓存对应的 temp_dir(None=未初始化) -_stub_scan_cache_arr: str # 扫描结果数组(每 17 字节一个 SHA1,MAX_INCLUDES_SHA1 个) -_stub_scan_cache_count: int # 收集数量 - # 单条依赖字符串最大长度 MAX_DEPS_STR_LEN: t.CDefine = 512 diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 208f5e6..35b6e5c 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -1050,7 +1050,7 @@ novirtual class TranslatorGenMixin: vt_mangled: str = self.Gen.FuncGenBase._mangle_name_with_sha1(vt_sha1, vt_name_buf) if vt_mangled is not None: # 跨模块 vtable 只被本初始化器文本引用 ⇒ 模块内无对应 GlobalVariable ⇒ - # 单模块 FULL IR 缺该符号声明(旧架构靠 BuildCombinedIR 合并依赖 stub + # 单模块 FULL IR 缺该符号声明(旧架构靠文本合并依赖 stub # 补入 external 声明)。此处补声明,使单模块 IR 自足(对齐 # CallGenLeafKit._make_vtable_ref_i8 的跨模块分支)。 # 仅跨模块补:本模块类由 _generate_vtable 产出定义,补声明会重复定义。