1589 lines
80 KiB
Plaintext
1589 lines
80 KiB
Plaintext
import lib.core.Gen.GenApi as GenApi
|
||
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import llvmlite
|
||
import w32.win32file as win32file
|
||
import w32.win32base as win32base
|
||
import w32.fileio as fileio
|
||
import viperlib
|
||
import sys
|
||
import ast
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.IncludesScanner as IncludesScanner
|
||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||
import lib.Phase.BuildPipeline as BuildPipeline
|
||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||
import lib.Projectrans.Utils as Utils
|
||
import lib.Projectrans.Config as Config
|
||
|
||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||
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))
|
||
#
|
||
# Phase B 对每个源文件调用 BuildCombinedIR 组合 IR:原实现对每行
|
||
# declare/global/type 都在不断增长的 out_buf 全量扫描去重
|
||
# (_IsFuncDeclaredOrDefined 逐字节 strncmp、strstr 等),依赖最多时
|
||
# (CallGenCall)单文件 merge 达 12s。改用定长槽位开放寻址哈希表
|
||
# 记录 out_buf 中已活跃的行:去重查找 O(1)、注释行 O(1) 定位。
|
||
#
|
||
# slot 布局(步长 _SEEN_SLOT):
|
||
# {u64 hash} {u64 pos} {u8 flags} {name[_SEEN_NAME_MAX]} {u8 nul}
|
||
# 空槽判定:hash==0(memset 全零初始化;FNV-1a 保证 >=2)。
|
||
# 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(88410e59e3e5f98b .str 常量根因,
|
||
# 该模块合并 IR 实测 4486 个全局行 > 旧容量 4096,故全局表设 16384)。
|
||
# ============================================================
|
||
_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: t.CUInt64T = 0xcbf29ce484222325
|
||
_SEEN_FNV_PRIME: t.CUInt64T = 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) -> t.CUInt64T:
|
||
"""FNV-1a 64 位哈希(对齐 includes/hashtable.vp)"""
|
||
h: t.CUInt64T = _SEEN_FNV_OFFSET
|
||
p: str = key
|
||
while p[0] != 0:
|
||
h = h ^ t.CUInt64T(p[0])
|
||
h = h * _SEEN_FNV_PRIME
|
||
p += 1
|
||
if h < 2:
|
||
h = 2
|
||
return h
|
||
|
||
def _SeenFind(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT:
|
||
"""已见表槽位查找:命中返回槽位下标;未命中返回 cap(哨兵)"""
|
||
if tbl is None or key is None:
|
||
return cap
|
||
mask: t.CSizeT = cap - 1
|
||
idx: t.CSizeT = t.CSizeT(key_hash) & mask
|
||
i: t.CSizeT = 0
|
||
while i < cap:
|
||
off: t.CSizeT = idx * _SEEN_SLOT
|
||
hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off)
|
||
sh: t.CUInt64T = 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: t.CSizeT, key: str, key_hash: t.CUInt64T) -> int:
|
||
"""已见表查找:返回 0=不存在;>0=存在(返回值 = flags+1)"""
|
||
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
|
||
if si >= cap:
|
||
return 0
|
||
return t.CInt(tbl[si * _SEEN_SLOT + 16]) + 1
|
||
|
||
def _SeenInsert(tbl: bytes, cap: t.CSizeT, count: int, key: str, key_hash: t.CUInt64T,
|
||
flags: int, pos: t.CSizeT) -> int:
|
||
"""已见表插入/更新:key 不存在则新登记(返回 count+1);已存在则 OR flags、更新 pos"""
|
||
if tbl is None or key is None:
|
||
return count
|
||
kl: t.CSizeT = string.strlen(key)
|
||
fb: str = VLogger.fmtbuffer
|
||
if kl >= _SEEN_NAME_MAX:
|
||
return count
|
||
if count >= cap:
|
||
viperlib.snprintf(fb, 1024, "已见符号表满(容量 %d),新增符号登记被丢弃——去重失效,必须增大容量", cap)
|
||
VLogger.error(fb, "StubMerger")
|
||
return count
|
||
mask: t.CSizeT = cap - 1
|
||
idx: t.CSizeT = t.CSizeT(key_hash) & mask
|
||
i: t.CSizeT = 0
|
||
while i < cap:
|
||
off: t.CSizeT = idx * _SEEN_SLOT
|
||
hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off)
|
||
sh: t.CUInt64T = hp[0]
|
||
if sh == 0:
|
||
hp[0] = key_hash
|
||
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8)
|
||
pp[0] = t.CUInt64T(pos)
|
||
tbl[off + 16] = t.CChar(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: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8)
|
||
pp[0] = t.CUInt64T(pos)
|
||
tbl[off + 16] = t.CChar(t.CInt(tbl[off + 16]) | flags)
|
||
return count
|
||
idx = (idx + 1) & mask
|
||
i += 1
|
||
return count
|
||
|
||
def _SeenCommentOut(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T,
|
||
out_buf: bytes) -> int:
|
||
"""注释 out_buf 中该符号的活跃行(行首改 ';'),返回 1=已注释 0=无"""
|
||
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
|
||
if si >= cap:
|
||
return 0
|
||
if t.CInt(tbl[si * _SEEN_SLOT + 16]) != 0:
|
||
return 0 # 类型表 flags=1:活跃行为完整定义,无可注释的 opaque
|
||
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8)
|
||
p: t.CUInt64T = pp[0]
|
||
if out_buf is not None:
|
||
out_buf[t.CSizeT(p)] = ';'
|
||
return 1
|
||
|
||
def _SeenGetPos(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT:
|
||
"""读取已见表条目记录的活跃行位置;不存在返回 cap(哨兵)"""
|
||
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
|
||
if si >= cap:
|
||
return cap
|
||
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8)
|
||
return t.CSizeT(pp[0])
|
||
|
||
def _RegisterStubLines(buf: bytes, buf_len: t.CSizeT, base_pos: t.CSizeT):
|
||
"""把本地 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: t.CSizeT = 0
|
||
while pos < buf_len:
|
||
ls: t.CSizeT = pos
|
||
while pos < buf_len:
|
||
if buf[pos] == '\n':
|
||
break
|
||
pos += 1
|
||
ll: t.CSizeT = pos - ls
|
||
if pos < buf_len:
|
||
pos += 1
|
||
line_pos: t.CSizeT = base_pos + ls
|
||
# declare → 函数签名 @"name"(
|
||
if ll >= 8 and string.strncmp(buf + ls, "declare ", 8) == 0:
|
||
at: t.CSizeT = ls + 8
|
||
while at < ls + ll:
|
||
if buf[at] == '@':
|
||
break
|
||
at += 1
|
||
if at < ls + ll:
|
||
lp: t.CSizeT = at + 1
|
||
while lp < ls + ll:
|
||
if buf[lp] == '(':
|
||
break
|
||
lp += 1
|
||
if lp < ls + ll:
|
||
sv_f: t.CChar = buf[lp + 1]
|
||
buf[lp + 1] = '\0'
|
||
kh_f: t.CUInt64T = _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: t.CSizeT = ls + 1
|
||
while eq < ls + ll:
|
||
if buf[eq] == '=':
|
||
break
|
||
eq += 1
|
||
if eq < ls + ll:
|
||
sv_g: t.CChar = buf[eq]
|
||
buf[eq] = '\0'
|
||
kh_g: t.CUInt64T = _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: t.CSizeT = ls + 1
|
||
while eq < ls + ll:
|
||
if buf[eq] == '=':
|
||
break
|
||
eq += 1
|
||
if eq < ls + ll:
|
||
sv_t: t.CChar = buf[eq]
|
||
buf[eq] = '\0'
|
||
kh_t: t.CUInt64T = _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: t.CSizeT = _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 - StubMerger 拆分(阶段 E):StubMergerBuild
|
||
#
|
||
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
|
||
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
|
||
# StubMerger._xxx 访问(main.vp 先例)。
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# _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: t.CSizeT, dep_sha1: str,
|
||
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
|
||
out_pos: t.CSizeT) -> t.CSizeT:
|
||
"""加载依赖 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: fileio.File | t.CPtr = fileio.File(dep_path, fileio.MODE.R)
|
||
fb: str = VLogger.fmtbuffer
|
||
if df_ls.closed:
|
||
stdlib.free(dep_path)
|
||
# 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast),
|
||
# 非映射模块(如 t/c 内部库、用户文件)按设计跳过
|
||
if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None:
|
||
viperlib.snprintf(fb, 1024, "依赖 stub 缺失: %s", dep_sha1)
|
||
VLogger.error(fb, "StubMerger")
|
||
return out_pos
|
||
dep_br_ls: t.CInt64T = 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'
|
||
else:
|
||
dep_buf[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||
|
||
# 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要)
|
||
# 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@
|
||
cfs_pos: t.CSizeT = 0
|
||
while cfs_pos + 30 < t.CSizeT(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: t.CSizeT = t.CSizeT(t.CUInt64T(ext_p) - t.CUInt64T(dep_buf))
|
||
# 向前查找行首的 @
|
||
ls: t.CSizeT = 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: t.CSizeT = 0
|
||
skipping: int = 1
|
||
while dep_pos < dep_br_ls:
|
||
line_start: t.CSizeT = dep_pos
|
||
while dep_pos < dep_br_ls:
|
||
if dep_buf[dep_pos] == '\n':
|
||
break
|
||
dep_pos += 1
|
||
line_len: t.CSizeT = 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: t.CUInt64T = 0
|
||
reg_flags: int = 0
|
||
# 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句)
|
||
if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0:
|
||
at: t.CSizeT = line_start + 8
|
||
while at < line_start + line_len:
|
||
if dep_buf[at] == '@':
|
||
break
|
||
at += 1
|
||
if at < line_start + line_len:
|
||
lp: t.CSizeT = at + 1
|
||
while lp < line_start + line_len:
|
||
if dep_buf[lp] == '(':
|
||
break
|
||
lp += 1
|
||
if lp < line_start + line_len:
|
||
saved: t.CChar = 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: t.CSizeT = line_start + 1
|
||
while eq < line_start + line_len:
|
||
if dep_buf[eq] == '=':
|
||
break
|
||
eq += 1
|
||
if eq < line_start + line_len:
|
||
saved: t.CChar = 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: t.CSizeT = line_start + 1
|
||
while eq2 < line_start + line_len:
|
||
if dep_buf[eq2] == '=':
|
||
break
|
||
eq2 += 1
|
||
if eq2 < line_start + line_len:
|
||
saved2: t.CChar = 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: t.CChar = 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: t.CSizeT = 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: t.CSizeT, dep_sha1: str,
|
||
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
|
||
out_pos: t.CSizeT) -> t.CSizeT:
|
||
"""从依赖 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
|
||
fb: str = VLogger.fmtbuffer
|
||
tf_lt: fileio.File | t.CPtr = 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:
|
||
viperlib.snprintf(fb, 1024, "依赖 text 缺失: %s", dep_sha1)
|
||
VLogger.error(fb, "StubMerger")
|
||
return out_pos
|
||
text_br: t.CInt64T = 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'
|
||
else:
|
||
dep_buf[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||
|
||
dep_pos_lt: t.CSizeT = 0
|
||
in_body: int = 0
|
||
while dep_pos_lt < text_br:
|
||
ls: t.CSizeT = dep_pos_lt
|
||
while dep_pos_lt < text_br:
|
||
if dep_buf[dep_pos_lt] == '\n':
|
||
break
|
||
dep_pos_lt += 1
|
||
ll: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = 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: t.CChar = dep_buf[lp_lt + 1]
|
||
dep_buf[lp_lt + 1] = '\0'
|
||
fh_lt: t.CUInt64T = _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: t.CSizeT = ls + ll
|
||
if decl_end > ls + 1:
|
||
if dep_buf[decl_end - 1] == '{' and dep_buf[decl_end - 2] == ' ':
|
||
decl_end -= 2
|
||
|
||
# 输出: declare + content(skip "define " 7 chars, up to decl_end)
|
||
# 泛型特化方法 define 是 "define linkonce_odr ..."(FunctionPrint
|
||
# 对名含 '[' 的函数输出 linkonce_odr):若只跳过 "define " 会拼出
|
||
# "declare linkonce_odr ..." → llc 报 "invalid linkage for function
|
||
# declaration"(LLVM 只允许 define 用 linkonce_odr,declare 必须
|
||
# external)。对齐 TPC Phase2Translator 的 decl_line 剥 linkage 逻辑,
|
||
# 循环跳过所有 linkage 词。
|
||
c_start_lt: t.CSizeT = 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: t.CSizeT = decl_end - c_start_lt
|
||
if out_pos + 8 + copy_len_lt + 2 < out_size:
|
||
decl_pos_lt: t.CSizeT = 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: t.CSizeT = 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: t.CChar = dep_buf[eq_td]
|
||
dep_buf[eq_td] = '\0'
|
||
th_td: t.CUInt64T = _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: t.CSizeT = 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: t.CSizeT = ls + 1
|
||
while eq_gv < ls + ll:
|
||
if dep_buf[eq_gv] == '=':
|
||
break
|
||
eq_gv += 1
|
||
if eq_gv < ls + ll:
|
||
# 截断到 '=' 之前(不含 '='),键统一为 @name
|
||
saved_gv: t.CChar = dep_buf[eq_gv]
|
||
dep_buf[eq_gv] = '\0'
|
||
gh_gv: t.CUInt64T = _SeenHash(dep_buf + ls)
|
||
# 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义)
|
||
# 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份
|
||
# 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致
|
||
# ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。
|
||
# 仅当依赖方完全没有该全局(既无 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: t.CSizeT = 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
|
||
# ============================================================
|
||
# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译)
|
||
#
|
||
# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的
|
||
# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免
|
||
# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。
|
||
#
|
||
# 算法(工作列表):
|
||
# 1. 扫描 source_dir 下的 .vp/.py 文件,解析 AST 获取 _imported_modules
|
||
# 2. 构建 SHA1→module_name 映射(从全局内存存储器)
|
||
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
|
||
#
|
||
# Args:
|
||
# mb: 内存池
|
||
# source_dir: 源文件目录(Config.SourceDir)
|
||
# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取)
|
||
# reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节)
|
||
#
|
||
# Returns:
|
||
# 可达 SHA1 数量(>0 成功,<=0 失败)
|
||
# ============================================================
|
||
def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||
temp_dir: str,
|
||
reachable_set: str,
|
||
app_deps: str = None) -> int:
|
||
"""构建可达 SHA1 集合(依赖图按需翻译)
|
||
|
||
app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤
|
||
"""
|
||
if source_dir is None or temp_dir is None or reachable_set is None:
|
||
return -1
|
||
|
||
SRC_BUF_SIZE_R: t.CSizeT = 1048576
|
||
|
||
# 1. 构建 SHA1→module_name 映射
|
||
td_len_r: t.CSizeT = string.strlen(temp_dir)
|
||
sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||
if sha1_arr is None or mod_arr is None:
|
||
if sha1_arr is not None:
|
||
stdlib.free(sha1_arr)
|
||
if mod_arr is not None:
|
||
stdlib.free(mod_arr)
|
||
return -1
|
||
map_count: int = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr)
|
||
if map_count <= 0:
|
||
VLogger.error("无法构建 SHA1 映射", "Reachable")
|
||
stdlib.free(sha1_arr)
|
||
stdlib.free(mod_arr)
|
||
return -1
|
||
fb: str = VLogger.fmtbuffer
|
||
viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count)
|
||
VLogger.debug(fb, "Reachable")
|
||
|
||
# 工作列表(空格分隔的模块名)
|
||
worklist: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||
if worklist is None:
|
||
stdlib.free(sha1_arr)
|
||
stdlib.free(mod_arr)
|
||
return -1
|
||
worklist[0] = '\0'
|
||
wl_len: t.CSizeT = 0
|
||
|
||
reachable_count: int = 0
|
||
|
||
# 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描)
|
||
# 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层)
|
||
if app_deps is not None and app_deps[0] != '\0':
|
||
ad_len: t.CSizeT = string.strlen(app_deps)
|
||
if ad_len > 0 and ad_len < StubMerger.REACH_WORKLIST_BUF:
|
||
string.strcpy(worklist, app_deps)
|
||
wl_len = ad_len
|
||
else:
|
||
VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger")
|
||
else:
|
||
# app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退,
|
||
# 显式 warning 暴露回退,不再静默。
|
||
# 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的
|
||
# App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入
|
||
# worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A
|
||
# 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默
|
||
# translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。
|
||
# 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。
|
||
VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable")
|
||
scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir)
|
||
if scan_res_r is not None:
|
||
es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||
for e_i_r in range(scan_res_r.Count):
|
||
e_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(
|
||
t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr))
|
||
if e_r is None or e_r.Path is None:
|
||
continue
|
||
sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R)
|
||
if sbuf is None:
|
||
continue
|
||
# 解析 AST 获取 _imported_modules(与原顶层扫描一致)
|
||
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
|
||
if lx is not None:
|
||
ast._lexer_init(lx, sbuf, mb)
|
||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||
if tree is not None:
|
||
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
|
||
if tr is not None:
|
||
tr._declare_only = 2
|
||
# 源文件包名:用 RelPath(相对 source_dir,支持子目录),
|
||
# 保证 from .xxx import 相对导入解析正确
|
||
tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, e_r.RelPath)
|
||
GenApi.reset_visible_structs(mb, 0)
|
||
tr.translate(tree)
|
||
# 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[]
|
||
# /{} 字面量)时注入 _list/_dict 依赖(与原实现一致)
|
||
tr._inject_auto_container_visibility(mb, tree)
|
||
# 获取 _imported_modules
|
||
if tr._imported_modules is not None:
|
||
# 逐模块去重追加(同 app_deps 拼接,防 worklist 膨胀溢出)
|
||
im_len: t.CSizeT = string.strlen(tr._imported_modules)
|
||
im_pos: t.CSizeT = 0
|
||
while im_pos < im_len:
|
||
im_start: t.CSizeT = im_pos
|
||
while im_pos < im_len and tr._imported_modules[im_pos] != ' ':
|
||
im_pos += 1
|
||
im_mod_len: t.CSizeT = im_pos - im_start
|
||
if im_pos < im_len:
|
||
im_pos += 1
|
||
if im_mod_len == 0:
|
||
continue
|
||
im_buf: bytes = stdlib.malloc(im_mod_len + 1)
|
||
if im_buf is None:
|
||
continue
|
||
string.strncpy(im_buf, tr._imported_modules + im_start, im_mod_len)
|
||
im_buf[im_mod_len] = '\0'
|
||
if wl_len > 0 and GenApi.is_module_imported(worklist, im_buf) != 0:
|
||
stdlib.free(im_buf)
|
||
continue
|
||
if wl_len + im_mod_len + 2 < StubMerger.REACH_WORKLIST_BUF:
|
||
if wl_len > 0:
|
||
worklist[wl_len] = ' '
|
||
wl_len += 1
|
||
string.strcpy(worklist + wl_len, im_buf)
|
||
wl_len += im_mod_len
|
||
worklist[wl_len] = ' '
|
||
wl_len += 1
|
||
worklist[wl_len] = '\0'
|
||
stdlib.free(im_buf)
|
||
# 计算 App 源文件 SHA1,填充依赖到内存存储器
|
||
# worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖
|
||
if e_r.Sha1 is not None:
|
||
StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules)
|
||
# 释放 Translator 资源
|
||
if tr._global_names is not None:
|
||
stdlib.free(tr._global_names)
|
||
if tr._nonlocal_names is not None:
|
||
stdlib.free(tr._nonlocal_names)
|
||
stdlib.free(sbuf)
|
||
|
||
# 去掉末尾多余空格
|
||
if wl_len > 0 and worklist[wl_len - 1] == ' ':
|
||
worklist[wl_len - 1] = '\0'
|
||
wl_len -= 1
|
||
|
||
fb: str = VLogger.fmtbuffer
|
||
viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist)
|
||
VLogger.debug(fb, "Reachable")
|
||
|
||
# 2.5. 预加入基础模块(builtins)
|
||
#
|
||
# builtins(内置函数模块:ord/chr 等)始终纳入编译链,调用时自动关联。
|
||
# _list/_dict 不再无条件预加入:Phase A 翻译 App 源文件时
|
||
# _inject_auto_container_visibility 已按需检测 list/dict 使用并注入
|
||
# _imported_modules(含 []/{} 字面量),json 由 _dict 的真实 import 链带入。
|
||
if GenApi.is_module_imported(worklist, "builtins") == 0:
|
||
bsha1_b: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins")
|
||
if bsha1_b is not None:
|
||
if wl_len + 9 < StubMerger.REACH_WORKLIST_BUF:
|
||
if wl_len > 0:
|
||
worklist[wl_len] = ' '
|
||
wl_len += 1
|
||
string.strcpy(worklist + wl_len, "builtins")
|
||
wl_len += 8
|
||
worklist[wl_len] = ' '
|
||
wl_len += 1
|
||
worklist[wl_len] = '\0'
|
||
|
||
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
|
||
VLogger.debug(fb, "Reachable")
|
||
|
||
# 3. 工作列表算法:递归收集可达 SHA1
|
||
processed: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||
if processed is None:
|
||
stdlib.free(sha1_arr)
|
||
stdlib.free(mod_arr)
|
||
stdlib.free(worklist)
|
||
return -1
|
||
processed[0] = '\0'
|
||
proc_len: t.CSizeT = 0
|
||
|
||
wl_pos: t.CSizeT = 0
|
||
wl_iter: int = 0
|
||
while wl_pos < wl_len:
|
||
wl_iter += 1
|
||
# 提取一个模块名(到空格或末尾)
|
||
name_start: t.CSizeT = wl_pos
|
||
while wl_pos < wl_len and worklist[wl_pos] != ' ':
|
||
wl_pos += 1
|
||
name_len: t.CSizeT = wl_pos - name_start
|
||
if wl_pos < wl_len:
|
||
wl_pos += 1
|
||
if name_len == 0:
|
||
continue
|
||
|
||
# 复制模块名
|
||
mod_buf: bytes = stdlib.malloc(name_len + 1)
|
||
if mod_buf is None:
|
||
continue
|
||
string.strncpy(mod_buf, worklist + name_start, name_len)
|
||
mod_buf[name_len] = '\0'
|
||
|
||
# 检查是否已处理
|
||
already: int = 0
|
||
if GenApi.is_module_imported(processed, mod_buf) != 0:
|
||
already = 1
|
||
if already == 0:
|
||
# 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug)
|
||
if processed is not None:
|
||
if proc_len + name_len + 1 < StubMerger.REACH_WORKLIST_BUF:
|
||
string.strcpy(processed + proc_len, mod_buf)
|
||
proc_len = proc_len + name_len
|
||
processed[proc_len] = ' '
|
||
proc_len = proc_len + 1
|
||
processed[proc_len] = '\0'
|
||
else:
|
||
VLogger.error("依赖 worklist 溢出", "StubMerger")
|
||
|
||
# 查找 SHA1
|
||
found_sha1: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf)
|
||
if found_sha1 is not None:
|
||
# App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则)
|
||
# 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+
|
||
# 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的
|
||
# App obj 重复符号(SysLibTest ld.lld duplicate symbol: StubMerger._mbuddy 根因)。
|
||
# 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。
|
||
is_app_r: int = 0
|
||
if StubMerger._sha1_store_incdir is not None:
|
||
for ai_r in range(StubMerger._sha1_store_count):
|
||
asi_r: t.CSizeT = t.CSizeT(ai_r) * 17
|
||
if string.strcmp(StubMerger._sha1_store_arr + asi_r, found_sha1) == 0:
|
||
aii_r: t.CSizeT = t.CSizeT(ai_r) * StubMerger.MAX_INC_DIR_LEN
|
||
if StubMerger._sha1_store_incdir[aii_r] == '\0':
|
||
is_app_r = 1
|
||
break
|
||
if is_app_r == 0:
|
||
reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, found_sha1)
|
||
|
||
# 从内存查找依赖(不读 deps.txt 过程文件)
|
||
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存
|
||
deps_str: str = StubMergerStore.LookupIncludesDeps(found_sha1)
|
||
if deps_str is not None:
|
||
dl: t.CSizeT = string.strlen(deps_str)
|
||
if dl > 0:
|
||
# 逐模块追加 + 去重:worklist 初始已含全部 App 直接依赖,
|
||
# deps_str 中的模块若已在 worklist/processed 不重复追加,
|
||
# 否则 BFS 沿共享依赖(builtins/stdlib 等)反复累积到
|
||
# 64KB 溢出("依赖 worklist 溢出"根因)。
|
||
dpos: t.CSizeT = 0
|
||
while dpos < dl:
|
||
dstart: t.CSizeT = dpos
|
||
while dpos < dl and deps_str[dpos] != ' ':
|
||
dpos += 1
|
||
dlen: t.CSizeT = dpos - dstart
|
||
if dpos < dl:
|
||
dpos += 1
|
||
if dlen == 0:
|
||
continue
|
||
dmod: bytes = stdlib.malloc(dlen + 1)
|
||
if dmod is None:
|
||
continue
|
||
string.strncpy(dmod, deps_str + dstart, dlen)
|
||
dmod[dlen] = '\0'
|
||
in_wl: int = 0
|
||
if GenApi.is_module_imported(worklist, dmod) != 0:
|
||
in_wl = 1
|
||
if in_wl == 0 and processed is not None:
|
||
if GenApi.is_module_imported(processed, dmod) != 0:
|
||
in_wl = 1
|
||
if in_wl == 0:
|
||
if wl_len + dlen + 2 < StubMerger.REACH_WORKLIST_BUF:
|
||
if wl_len > 0 and worklist[wl_len - 1] != ' ':
|
||
worklist[wl_len] = ' '
|
||
wl_len = wl_len + 1
|
||
string.strcpy(worklist + wl_len, dmod)
|
||
wl_len = wl_len + dlen
|
||
worklist[wl_len] = ' '
|
||
wl_len = wl_len + 1
|
||
worklist[wl_len] = '\0'
|
||
else:
|
||
VLogger.error("依赖 worklist 溢出", "StubMerger")
|
||
stdlib.free(dmod)
|
||
# deps_str 为空字符串视为无依赖(合法情况,如叶子模块)
|
||
else:
|
||
# 依赖未在内存中找到
|
||
# 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件)
|
||
# App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过
|
||
sd_len_ff: t.CSizeT = string.strlen(source_dir)
|
||
mb_len_ff: t.CSizeT = string.strlen(mod_buf)
|
||
app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6)
|
||
is_app_src: int = 0
|
||
if app_mod_path is not None:
|
||
# 仅识别 .vp 源文件(不再做 vp -> py 后缀识别回退)
|
||
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf)
|
||
af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R)
|
||
if not af.closed:
|
||
is_app_src = 1
|
||
af.close()
|
||
stdlib.free(app_mod_path)
|
||
if is_app_src == 0:
|
||
# fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充
|
||
viperlib.snprintf(fb, 1024, "依赖未找到 fast-fail: 模块 %s(sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1)
|
||
VLogger.error(fb, "Reachable")
|
||
stdlib.free(sha1_arr)
|
||
stdlib.free(mod_arr)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(mod_buf)
|
||
return -1
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(sha1_arr)
|
||
stdlib.free(mod_arr)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
|
||
viperlib.snprintf(fb, 1024, "可达 SHA1: %d 个", reachable_count)
|
||
VLogger.debug(fb, "Reachable")
|
||
return reachable_count
|
||
# ============================================================
|
||
# 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: t.CSizeT) -> t.CSizeT:
|
||
"""组合本地 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: t.CSizeT = string.strlen(temp_dir)
|
||
out_buf[0] = '\0'
|
||
out_pos: t.CSizeT = 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: fileio.File | t.CPtr = 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: t.CInt64T = 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'
|
||
else:
|
||
stub_content[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||
slen: t.CSizeT = stub_br
|
||
# 修复本地 stub.ll 中字符串常量的非法 IR:
|
||
# llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。
|
||
# 简单方案:搜索 "= external unnamed_addr constant" 并将 external 替换为 8 个空格。
|
||
fix_pos: t.CSizeT = 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: t.CSizeT = t.CSizeT(t.CUInt64T(ext_ptr) - t.CUInt64T(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: fileio.File | t.CPtr = 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: t.CInt64T = df_deps.read_all(deps_buf, 4096)
|
||
if deps_br > 0:
|
||
if deps_br < 4096:
|
||
deps_buf[deps_br] = '\0'
|
||
else:
|
||
deps_buf[4095] = '\0'
|
||
deps_loaded = 1
|
||
elif deps_br == 0:
|
||
# 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、
|
||
# lib/StubGen/__init__.vp 纯注释)合法——deps_loaded 置 1 视为
|
||
# "已加载且无依赖",避免下方"deps.txt 缺失"误报(追加85 根因)。
|
||
# deps_buf 是 malloc 未初始化内存,必须显式置空串,
|
||
# 否则后续 strlen(deps_buf) 读到垃圾长度遍历越界。
|
||
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 + t.CSizeT(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: t.CSizeT = t.CSizeT(ii) * 17
|
||
if string.strcmp(sha1_arr + idx_ii, dep_sha1) != 0:
|
||
continue
|
||
is_include = 1
|
||
if deps_loaded != 0:
|
||
idx_mi: t.CSizeT = t.CSizeT(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 是否是已导入包的子模块
|
||
# (如 ast.lexer 是 ast 的子模块)
|
||
should_load = 1
|
||
else:
|
||
# 基名匹配:store 键是全路径模块名
|
||
# (Kernel.drivers.fs.fat32.fat32_types),而 deps.txt
|
||
# 记录 import 短名(fat32_types)→ 精确/子模块均不命中
|
||
# → 依赖 stub/text 不加载 → 跨模块类完整定义缺失
|
||
# (vpi 注入失败的 fat32_types.dirobj 只有 opaque)→
|
||
# 值类型 alloca 报 llc "Cannot allocate unsized type"
|
||
# (VKernel example_fs L500 根因)。取最后一个 '.' 之后
|
||
# 的基名与 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 映射,自动跳过)。
|
||
fb: str = VLogger.fmtbuffer
|
||
if deps_loaded != 0 and inc_count > 0:
|
||
dep_total: t.CSizeT = string.strlen(deps_buf)
|
||
dp: t.CSizeT = 0
|
||
while dp < dep_total:
|
||
# 跳过前导空格
|
||
while dp < dep_total:
|
||
if deps_buf[dp] != ' ':
|
||
break
|
||
dp += 1
|
||
if dp >= dep_total:
|
||
break
|
||
# 提取模块名(到下一个空格或末尾)
|
||
ms: t.CSizeT = dp
|
||
while dp < dep_total:
|
||
if deps_buf[dp] == ' ':
|
||
break
|
||
dp += 1
|
||
ml: t.CSizeT = 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: t.CSizeT = t.CSizeT(mi2) * 64
|
||
if string.strcmp(mod_arr + idx_m2, mod_nm) == 0:
|
||
idx_s2: t.CSizeT = t.CSizeT(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: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R)
|
||
if chk_f.closed:
|
||
viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path)
|
||
VLogger.error(fb, "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: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R)
|
||
if tf.closed:
|
||
# 本地 text.ll 缺失:合并 IR 无法生成定义块,fail-fast 终止
|
||
viperlib.snprintf(fb, 1024, "本地 text.ll 缺失: %s", text_path)
|
||
VLogger.error(fb, "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: t.CInt64T = 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'
|
||
else:
|
||
text_content[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||
# 行级去重追加(与 _LoadAndAppendStub 相同逻辑)
|
||
tx_pos: t.CSizeT = 0
|
||
tx_skip: int = 1
|
||
while tx_pos < text_br:
|
||
tx_ls: t.CSizeT = tx_pos
|
||
while tx_pos < text_br:
|
||
if text_content[tx_pos] == '\n':
|
||
break
|
||
tx_pos += 1
|
||
tx_ll: t.CSizeT = 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: t.CUInt64T = 0
|
||
tx_flags: int = 0
|
||
# 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句)
|
||
if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0:
|
||
tx_at: t.CSizeT = 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: t.CSizeT = 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: t.CChar = 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: t.CSizeT = 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: t.CSizeT = 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: t.CChar = 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: t.CSizeT = 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: t.CChar = 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: t.CSizeT = 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: t.CChar = 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: t.CSizeT = 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)
|
||
|
||
return out_pos
|
||
# ============================================================
|
||
# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块
|
||
#
|
||
# 遍历 imported_modules(空格分隔的模块名),对每个模块:
|
||
# 1. 用 LookupSha1ByModName 获取 SHA1
|
||
# 2. 用 _sliced_path 构造 .vpi 文件路径
|
||
# 3. 调用 VpiReader.ParseVpiIntoModule 解析 .vpi 并注入 declare
|
||
#
|
||
# 在 _translate_module_level 中、Import 处理完成后、translate_children
|
||
# 之前调用,确保跨模块函数调用时 declare 已存在。
|
||
#
|
||
# Args:
|
||
# pool: 内存池
|
||
# mod: 当前 LLVM 模块
|
||
# imported_modules: 已导入模块名字符串(空格分隔)
|
||
# from_imports: from-import 映射字符串("name:module name:module" 格式)
|
||
# temp_dir: temp 目录路径
|
||
# trans: 翻译器对象
|
||
#
|
||
# Returns:
|
||
# 成功加载的 .vpi 文件数
|
||
# ============================================================
|
||
def LoadDepVpiIntoModule(pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
temp_dir: str,
|
||
trans: HandlesTranslator.Translator | t.CPtr) -> int:
|
||
"""加载依赖模块的 .vpi 文件,将函数声明注入到当前模块"""
|
||
if pool is None or mod is None or temp_dir is None or trans is None:
|
||
return 0
|
||
if imported_modules is None:
|
||
return 0
|
||
|
||
td_len: t.CSizeT = string.strlen(temp_dir)
|
||
total_len: t.CSizeT = string.strlen(imported_modules)
|
||
success_count: int = 0
|
||
|
||
# 遍历 imported_modules 中的每个模块名(空格分隔)
|
||
ci: t.CSizeT = 0
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and imported_modules[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 提取模块名(到下一个空格或字符串末尾)
|
||
word_start: t.CSizeT = ci
|
||
while ci < total_len and imported_modules[ci] != ' ':
|
||
ci += 1
|
||
word_len: t.CSizeT = ci - word_start
|
||
if word_len == 0:
|
||
continue
|
||
|
||
# 复制模块名到临时缓冲区并 null 终止
|
||
mod_nm_buf: str = stdlib.malloc(word_len + 1)
|
||
if mod_nm_buf is None:
|
||
continue
|
||
string.strncpy(mod_nm_buf, imported_modules + word_start, word_len)
|
||
mod_nm_buf[word_len] = '\0'
|
||
|
||
# 用 LookupSha1ByModName 查找模块的 SHA1
|
||
sha1_val: str = StubMergerStore.LookupSha1ByModName(mod_nm_buf)
|
||
|
||
# 改名别名(import X as Y,如 HT→lib.core.Handles.HandlesTranslator):
|
||
# 精确/包名回退均失败时从 from_imports 查别名映射再查 SHA1,
|
||
# 否则该类模块的 vpi/stub 不注入 → 跨模块符号解析失败
|
||
if sha1_val is None and from_imports is not None:
|
||
sha1_val = StubMergerStore.LookupSha1ByAlias(from_imports, mod_nm_buf)
|
||
|
||
# 如果精确查找失败,尝试包名回退(如 "ast" → "ast.__init__")
|
||
# LookupSha1ByModName 内部已处理包名回退,此处无需重复
|
||
|
||
if sha1_val is not None:
|
||
# 构造 .vpi 文件路径
|
||
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1_val, "vpi")
|
||
if vpi_path is not None:
|
||
# 检查 .vpi 文件是否存在(可读)
|
||
vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
|
||
if not vf.closed:
|
||
vf.close()
|
||
# 调用 VpiReader 解析 .vpi 并注入 declare
|
||
# (错误上下文帧由调用方 HandlesTranslator 维护)
|
||
ret: int = VpiReader.ParseVpiIntoModule(pool, mod, vpi_path, trans)
|
||
if ret == 1:
|
||
success_count += 1
|
||
stdlib.free(vpi_path)
|
||
# sha1_val 指向内部存储,无需 free
|
||
|
||
stdlib.free(mod_nm_buf)
|
||
|
||
return success_count
|