c 类(源码违规):(str | t.CPtr)(x) 按既定语义 = i8**(char**,指针的指针), 而这些位置要的是 i8*。TPC 的 return/赋值检查现在按声明精确校验(本次即 TypeGenResolve.vp:1221 `return (str | t.CPtr)(v)` 声明返回 i8* 却 return i8** 被抳下), 该 idiom 在旧的无条件 bitcast 下一直不可见。 改法:86 处 (str | t.CPtr)(X) → rawptr(X)(rawptr 是 t.CPtr 的内置名,同为 i8*; 整数操作数走 inttoptr,指针操作数走 bitcast)。不用 str(X):整数操作数会编译成 itoa (Gen/FuncGenMixin.vp:1159 注释已记录该陷阱),精确类型仍由使用处的 `: str` 标注承担。 保留 4 处本就该是 i8** 的用例(未改):Converter.vp:820(_g_pyi_cdef_names 声明为 str | t.CPtr 的指针数组)、CallGenCBuiltin.vp:1194/1215(声明即 str | t.CPtr)、 1411(先 cast 再 [0] 取首元素)。 另:Gen/Type/TypeGenResolve.vp:1221 手工改为 return t.CPtr(v)(该文件含你的未提交改动,未一并提交)。
1917 lines
94 KiB
Plaintext
1917 lines
94 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.Gen.Translator 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))
|
||
#
|
||
# 用定长槽位开放寻址哈希表记录 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 为空)
|
||
#
|
||
# 依赖图 BFS 中识别 App 中间节点:App 文件不入 reachable_set(Phase B
|
||
# 已编译其 .obj),但其 includes 依赖必须继续展开。App 条目由 App 扫描
|
||
# 阶段用 AppendToSha1MapStore 登记(incdir 留空);includes 条目用
|
||
# AddIncludeToSha1MapStore 登记(incdir 非空)。
|
||
# ============================================================
|
||
def _IsAppModule(mod_name: str) -> int:
|
||
"""mod_name 是否为 App 源文件(store 命中且 incdir 为空)"""
|
||
if mod_name is None:
|
||
return 0
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
||
return 0
|
||
for i in range(StubMerger._sha1_store_count):
|
||
midx: sizet = sizet(i) * 64
|
||
if string.strcmp(StubMerger._sha1_store_mod + midx, mod_name) == 0:
|
||
iidx: sizet = sizet(i) * StubMerger.MAX_INC_DIR_LEN
|
||
if StubMerger._sha1_store_incdir[iidx] == '\0':
|
||
return 1
|
||
return 0
|
||
return 0
|
||
# ============================================================
|
||
# _AppendDepsToWorklist - 追加空格分隔模块名到 worklist(逐模块去重)
|
||
#
|
||
# worklist 是 BFS 队列(空格分隔);deps_str 中的模块若已在 worklist/processed 中则不重复追加,
|
||
# 否则 BFS 沿共享依赖(builtins/stdlib 等)反复累积会致 REACH_WORKLIST_BUF 溢出。
|
||
#
|
||
# Args: worklist 队列缓冲(原地修改);wl_len 当前长度(值);deps_str 要追加的模块名(空格分隔);processed 已处理集合(空格分隔,查重)
|
||
# Returns: 追加后的新长度(>=0);溢出/参数错误返回 -1(调用方 fail-fast)
|
||
# ============================================================
|
||
def _AppendDepsToWorklist(worklist: bytes, wl_len: sizet,
|
||
deps_str: str, processed: str) -> int:
|
||
if worklist is None or deps_str is None:
|
||
return -1
|
||
cur_len: sizet = wl_len
|
||
dl: sizet = string.strlen(deps_str)
|
||
dpos: sizet = 0
|
||
while dpos < dl:
|
||
dstart: sizet = dpos
|
||
while dpos < dl and deps_str[dpos] != ' ':
|
||
dpos += 1
|
||
dlen: sizet = 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 cur_len > 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 cur_len + dlen + 2 < StubMerger.REACH_WORKLIST_BUF:
|
||
if cur_len > 0 and worklist[cur_len - 1] != ' ':
|
||
worklist[cur_len] = ' '
|
||
cur_len = cur_len + 1
|
||
string.strcpy(worklist + cur_len, dmod)
|
||
cur_len = cur_len + dlen
|
||
worklist[cur_len] = ' '
|
||
cur_len = cur_len + 1
|
||
worklist[cur_len] = '\0'
|
||
else:
|
||
stdlib.free(dmod)
|
||
VLogger.error("依赖 worklist 溢出", "StubMerger")
|
||
return -1
|
||
stdlib.free(dmod)
|
||
return int(cur_len)
|
||
|
||
# ============================================================
|
||
# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需扫描;未引用的 includes 从不读取)
|
||
#
|
||
# App 优先模型:从 app_deps 出发 BFS,对每个模块名按文件系统解析到 includes 文件(ResolveModuleToPath),只对可达 includes
|
||
# 读文件 + 算 SHA1 + 解析 import,补充依赖图并登记 Sha1Store/deps 存储器/写 deps.txt;Phase 1b / Phase B+ 只处理可达集合中的文件。
|
||
#
|
||
# 算法(工作列表 BFS):1) App 扫描(app_deps 为空时)递归扫 source_dir,declare_only=2 解析 import(相对导入 resolve 成绝对模块名)
|
||
# + 容器注入(_list/_dict)→ worklist 初始队列,App 文件登记 store(incdir 空)+ 填 deps;2) builtins 预加入队列;3) BFS 直到闭包。
|
||
#
|
||
# Args: mb 内存池;source_dir App 源文件目录(Config.SourceDir);temp_dir 临时目录(deps.txt 所在位置);reachable_set 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节);app_deps App 源文件直接依赖模块名(空格分隔),非空则跳过 App 扫描;includes_dirs includes 根目录(空格分隔)
|
||
# Returns: 可达 SHA1 数量(>0 成功;0 无 includes 依赖;<0 失败)
|
||
# ============================================================
|
||
def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str,
|
||
temp_dir: str,
|
||
reachable_set: str,
|
||
app_deps: str = None,
|
||
includes_dirs: str = None) -> int:
|
||
"""构建可达 SHA1 集合(依赖图按需扫描,App 优先)"""
|
||
if source_dir is None or temp_dir is None or reachable_set is None:
|
||
return -1
|
||
if includes_dirs is None:
|
||
VLogger.error("includes_dirs 缺失,无法按需解析依赖图", "Reachable")
|
||
return -1
|
||
|
||
SRC_BUF_SIZE_R: sizet = 1048576
|
||
|
||
# 工作列表(空格分隔的模块名)
|
||
worklist: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||
if worklist is None:
|
||
return -1
|
||
worklist[0] = '\0'
|
||
wl_len: sizet = 0
|
||
|
||
# 1. App 扫描(app_deps 非空时跳过,直接用调用方提供的初始队列)
|
||
if app_deps is None or app_deps[0] == '\0':
|
||
VLogger.info("App 扫描:解析 source_dir 的 import 依赖", "Reachable")
|
||
scan_res_r: rawptr[IncludesScanner.ScanResult] = IncludesScanner.scan_includes(mb, source_dir)
|
||
if scan_res_r is not None:
|
||
es_r: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||
for e_i_r in range(scan_res_r.Count):
|
||
e_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](uint64(scan_res_r.Entries) + uint64(e_i_r) * es_r)
|
||
if e_r is None or e_r.Path is None:
|
||
continue
|
||
# 文件级 mark:parse 完立即回收 AST(App 全量 parse 驻留会 OOM)
|
||
app_mark: sizet = mb.mark()
|
||
sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R)
|
||
if sbuf is None:
|
||
mb.reset_to(app_mark)
|
||
continue
|
||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||
tree: ast.AST* = None
|
||
if lx is not None:
|
||
ast._lexer_init(lx, sbuf, mb)
|
||
tokens: ast.Token* = ast.tokenize(lx)
|
||
tree = ast.parse_tokens(mb, tokens)
|
||
if tree is not None:
|
||
tr: rawptr[HandlesTranslator.Translator] = 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.set_current_file_type(e_r.Path)
|
||
GenApi.set_current_file_imports(e_r.Path)
|
||
GenApi.set_current_module_sha1(e_r.Sha1)
|
||
GenApi.clear_cdefine_constants()
|
||
GenApi.clear_ctypedefs()
|
||
GenApi.reset_visible_structs(mb, 0)
|
||
ret_a: int = tr.translate(tree)
|
||
if ret_a == 0:
|
||
# 容器使用检测:App 文件用 list/dict(裸名注解/构造)
|
||
# 时注入 _list/_dict 依赖(对齐原实现)
|
||
tr._inject_auto_container_visibility(mb, tree)
|
||
# App 文件登记 store(incdir 空)+ 填 deps:
|
||
# BFS 展开 App 中间节点的 includes 依赖时经
|
||
# LookupIncludesDeps(app_sha1) 读取
|
||
if e_r.Sha1 is not None:
|
||
StubMergerStore.AppendToSha1MapStore(e_r.Sha1, e_r.RelPath)
|
||
StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules)
|
||
# 收集 deps 到初始队列(逐模块去重)
|
||
if tr._imported_modules is not None:
|
||
im_len_a: sizet = string.strlen(tr._imported_modules)
|
||
im_pos_a: sizet = 0
|
||
while im_pos_a < im_len_a:
|
||
im_start_a: sizet = im_pos_a
|
||
while im_pos_a < im_len_a and tr._imported_modules[im_pos_a] != ' ':
|
||
im_pos_a += 1
|
||
im_mod_len: sizet = im_pos_a - im_start_a
|
||
if im_pos_a < im_len_a:
|
||
im_pos_a += 1
|
||
if im_mod_len == 0:
|
||
continue
|
||
im_buf_a: bytes = stdlib.malloc(im_mod_len + 1)
|
||
if im_buf_a is None:
|
||
continue
|
||
string.strncpy(im_buf_a, tr._imported_modules + im_start_a, im_mod_len)
|
||
im_buf_a[im_mod_len] = '\0'
|
||
in_wl_a: int = 0
|
||
if wl_len > 0 and GenApi.is_module_imported(worklist, im_buf_a) != 0:
|
||
in_wl_a = 1
|
||
if in_wl_a == 0:
|
||
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_a)
|
||
wl_len += im_mod_len
|
||
worklist[wl_len] = ' '
|
||
wl_len += 1
|
||
worklist[wl_len] = '\0'
|
||
stdlib.free(im_buf_a)
|
||
# 释放 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)
|
||
mb.reset_to(app_mark)
|
||
else:
|
||
# app_deps 已提供(Phase2 场景):直接作为初始队列
|
||
ad_len: sizet = 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")
|
||
stdlib.free(worklist)
|
||
return -1
|
||
|
||
# 去掉末尾多余空格
|
||
if wl_len > 0 and worklist[wl_len - 1] == ' ':
|
||
worklist[wl_len - 1] = '\0'
|
||
wl_len -= 1
|
||
|
||
# 2. 预加入基础模块(builtins)
|
||
# builtins(内置函数模块)始终纳入编译链。_list/_dict 仅在显式
|
||
# list/dict 注解/构造时经 _inject_auto_container_visibility 注入
|
||
# (list 字面量赋给 t.CArray 走数组初始化,不触发 _list)。
|
||
# processed 尚未分配,此处传 None 仅按 worklist 查重。
|
||
wl_len = _AppendDepsToWorklist(worklist, wl_len, "builtins", None)
|
||
if wl_len < 0:
|
||
stdlib.free(worklist)
|
||
return -1
|
||
|
||
# 2b. --traceback: force the traceback runtime module into the reachable set
|
||
# (TPC: used_includes.add('traceback')). Its Install() symbol and the
|
||
# @llvm.global_ctors ctor are only produced by compiling this module, so a
|
||
# missing file is fail-fast rather than a silent "feature off" build.
|
||
if BuildPipeline._traceback_mode != 0:
|
||
tb_abs: t.CPtr = stdlib.malloc(8)
|
||
tb_rel: t.CPtr = stdlib.malloc(8)
|
||
tb_inc: t.CPtr = stdlib.malloc(8)
|
||
tb_found: int = -1
|
||
if tb_abs is not None and tb_rel is not None and tb_inc is not None:
|
||
tb_abs_slot: sizet* = (sizet*)(tb_abs)
|
||
tb_rel_slot: sizet* = (sizet*)(tb_rel)
|
||
tb_inc_slot: sizet* = (sizet*)(tb_inc)
|
||
tb_abs_slot[0] = 0
|
||
tb_rel_slot[0] = 0
|
||
tb_inc_slot[0] = 0
|
||
tb_found = IncludesScanner.ResolveModuleToPath(
|
||
includes_dirs, "traceback", tb_abs, tb_rel, tb_inc)
|
||
if tb_abs is not None:
|
||
stdlib.free(tb_abs)
|
||
if tb_rel is not None:
|
||
stdlib.free(tb_rel)
|
||
if tb_inc is not None:
|
||
stdlib.free(tb_inc)
|
||
if tb_found != 0:
|
||
stdlib.free(worklist)
|
||
VLogger.error(f"--traceback: 在 includes 目录中找不到 traceback.vp,无法编译并链接 Install()(请确认 project.vpj 的 includes 包含仓库 includes 目录;已扫描: {includes_dirs})", "Traceback")
|
||
return -1
|
||
tb_wl: int = _AppendDepsToWorklist(worklist, wl_len, "traceback", None)
|
||
if tb_wl < 0:
|
||
stdlib.free(worklist)
|
||
return -1
|
||
wl_len = sizet(tb_wl)
|
||
|
||
# 3. 工作列表 BFS:模块名 → 文件系统解析 → 读+hash+parse import
|
||
processed: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||
if processed is None:
|
||
stdlib.free(worklist)
|
||
return -1
|
||
processed[0] = '\0'
|
||
proc_len: sizet = 0
|
||
reachable_count: int = 0
|
||
wl_pos: t.CPtr = stdlib.malloc(8)
|
||
if wl_pos is None:
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
return -1
|
||
wl_pos_slot: sizet* = (sizet*)(wl_pos)
|
||
wl_pos_slot[0] = 0
|
||
|
||
while wl_pos_slot[0] < wl_len:
|
||
pos: sizet = wl_pos_slot[0]
|
||
# 提取一个模块名(到空格或末尾)
|
||
name_start: sizet = pos
|
||
while pos < wl_len and worklist[pos] != ' ':
|
||
pos += 1
|
||
name_len: sizet = pos - name_start
|
||
if pos < wl_len:
|
||
pos += 1
|
||
wl_pos_slot[0] = pos
|
||
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'
|
||
|
||
# 已处理去重
|
||
if GenApi.is_module_imported(processed, mod_buf) != 0:
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
# 加入 processed
|
||
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("依赖 processed 溢出", "StubMerger")
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
|
||
# App 中间节点:只展开其 includes 依赖,不入 reachable_set
|
||
# (Phase B 已编译 App 的 .obj,加入会导致 Phase B+ 重复编译 → 重复符号)
|
||
if _IsAppModule(mod_buf) != 0:
|
||
app_sha1: str = StubMergerStore.LookupSha1ByModName(mod_buf)
|
||
if app_sha1 is not None:
|
||
app_deps_str: str = StubMergerStore.LookupIncludesDeps(app_sha1)
|
||
if app_deps_str is not None:
|
||
ret_ap: int = _AppendDepsToWorklist(worklist, wl_len, app_deps_str, processed)
|
||
if ret_ap < 0:
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(wl_pos)
|
||
return -1
|
||
wl_len = sizet(ret_ap)
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
|
||
# includes:按文件系统按需解析(未引用的 includes 根本不触碰)
|
||
out_abs: t.CPtr = stdlib.malloc(8)
|
||
out_rel: t.CPtr = stdlib.malloc(8)
|
||
out_inc: t.CPtr = stdlib.malloc(8)
|
||
if out_abs is None or out_rel is None or out_inc is None:
|
||
if out_abs is not None:
|
||
stdlib.free(out_abs)
|
||
if out_rel is not None:
|
||
stdlib.free(out_rel)
|
||
if out_inc is not None:
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
# 输出槽防御性初始化(成功路径必全写;未找到/未命中时仅 free 槽本身)
|
||
# 槽联合必须用 (sizet*):t.CPtr|t.CPtr 会被折叠成 t.CPtr,
|
||
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
|
||
# 8 字节:写侧 sizet(ptr),读侧必须用 rawptr(slot[0]) 做 u64→
|
||
# 指针转换;str(slot[0]) 是内置"数字转字符串"(snprintf 到栈缓冲并返回
|
||
# 该栈地址),拿到的是十进制文本+栈指针 → free 即 bad-free。
|
||
abs_slot: sizet* = (sizet*)(out_abs)
|
||
rel_slot: sizet* = (sizet*)(out_rel)
|
||
inc_slot: sizet* = (sizet*)(out_inc)
|
||
abs_slot[0] = 0
|
||
rel_slot[0] = 0
|
||
inc_slot[0] = 0
|
||
rcode: int = IncludesScanner.ResolveModuleToPath(
|
||
includes_dirs, mod_buf, out_abs, out_rel, out_inc)
|
||
if rcode != 0:
|
||
# 内建模块(t/c/builtins 等内部库)或未找到:静默跳过
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
abs_path: str = rawptr(abs_slot[0])
|
||
rel_path: str = rawptr(rel_slot[0])
|
||
inc_dir: str = rawptr(inc_slot[0])
|
||
|
||
# 读文件 + 计算 SHA1(CRLF→LF,与 IncludesScanner 一致)
|
||
sha1_r2: str = IncludesScanner.compute_file_sha1(mb, abs_path)
|
||
if sha1_r2 is None:
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
|
||
# 已在可达集合(前序 BFS 已 parse 过):只展开其 deps
|
||
if StubMergerCollect._is_in_sha1_set(sha1_r2, reachable_set, reachable_count) != 0:
|
||
deps_ex: str = StubMergerStore.LookupIncludesDeps(sha1_r2)
|
||
if deps_ex is not None:
|
||
ret_ex: int = _AppendDepsToWorklist(worklist, wl_len, deps_ex, processed)
|
||
if ret_ex < 0:
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(wl_pos)
|
||
return -1
|
||
wl_len = sizet(ret_ex)
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
continue
|
||
|
||
# 加入可达集合 + 登记 Sha1Store(仅可达 includes)
|
||
reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, sha1_r2)
|
||
StubMergerStore.AddIncludeToSha1MapStore(sha1_r2, rel_path, inc_dir, mod_buf)
|
||
|
||
# 文件级 mark:parse import 后立即回收 AST(可达 includes 全量
|
||
# parse 的 AST 若驻留 → 主池 OOM)
|
||
scan_mark_r: sizet = mb.mark()
|
||
sbuf_r: bytes = Utils.read_file_to_buf(abs_path, SRC_BUF_SIZE_R)
|
||
if sbuf_r is None:
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
mb.reset_to(scan_mark_r)
|
||
continue
|
||
lx_r: ast.Lexer* = ast.new_lexer(mb)
|
||
tree_r: ast.AST* = None
|
||
if lx_r is not None:
|
||
ast._lexer_init(lx_r, sbuf_r, mb)
|
||
tokens_r: ast.Token* = ast.tokenize(lx_r)
|
||
tree_r = ast.parse_tokens(mb, tokens_r)
|
||
if tree_r is None:
|
||
# parse_tokens keeps the error detail (line/col/message) in the global
|
||
# channel only, so print the unified parse traceback before the one-line
|
||
# log; without it a reachable-includes syntax error is undiagnosable.
|
||
GenApi.dump_parse_traceback(abs_path)
|
||
VLogger.error(f"可达 includes 解析失败: {abs_path}", "Reachable")
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(sbuf_r)
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(wl_pos)
|
||
mb.reset_to(scan_mark_r)
|
||
return -1
|
||
tr_r2: rawptr[HandlesTranslator.Translator] = HandlesTranslator.Translator()
|
||
if tr_r2 is None:
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(sbuf_r)
|
||
stdlib.free(mod_buf)
|
||
mb.reset_to(scan_mark_r)
|
||
continue
|
||
tr_r2.ModuleSha1 = sha1_r2
|
||
tr_r2._declare_only = 2
|
||
# 包名:相对 includes 根目录的 RelPath(支持 from .xxx import 相对导入)
|
||
tr_r2.CurrentPackage = GenApi.compute_package_from_relpath(mb, rel_path)
|
||
GenApi.set_current_file_type(abs_path)
|
||
GenApi.set_current_file_imports(abs_path)
|
||
GenApi.set_current_module_sha1(sha1_r2)
|
||
GenApi.clear_cdefine_constants()
|
||
GenApi.clear_ctypedefs()
|
||
GenApi.reset_visible_structs(mb, 0)
|
||
ret_r2: int = tr_r2.translate(tree_r)
|
||
if ret_r2 != 0:
|
||
VLogger.error(f"import 依赖扫描失败(可达 includes): {abs_path}", "Reachable")
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(sbuf_r)
|
||
if tr_r2._global_names is not None:
|
||
stdlib.free(tr_r2._global_names)
|
||
if tr_r2._nonlocal_names is not None:
|
||
stdlib.free(tr_r2._nonlocal_names)
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(wl_pos)
|
||
mb.reset_to(scan_mark_r)
|
||
return -1
|
||
# 容器按需注入(includes 用 list/dict 泛型时追加 _list/_dict 到依赖,
|
||
# 对齐 App 层 _inject_auto_container_visibility;内联破环防 _list/_dict 环)
|
||
tr_r2._inject_auto_container_visibility(mb, tree_r)
|
||
|
||
# 填内存依赖 + 写 deps.txt(供依赖图展开与 Phase B 按需加载)
|
||
deps_r2: str = tr_r2._imported_modules
|
||
StubMergerStore.PopulateIncludesDeps(sha1_r2, deps_r2)
|
||
if deps_r2 is not None:
|
||
td_len_w: sizet = string.strlen(temp_dir)
|
||
deps_path_w: str = StubMergerCollect._sliced_path(temp_dir, td_len_w, sha1_r2, "deps.txt")
|
||
if deps_path_w is not None:
|
||
df_w: rawptr[fileio.File] = fileio.File(deps_path_w, fileio.MODE.W)
|
||
if not df_w.closed:
|
||
dl_w: sizet = string.strlen(deps_r2)
|
||
if dl_w > 0:
|
||
df_w.write(deps_r2, dl_w)
|
||
df_w.close()
|
||
stdlib.free(deps_path_w)
|
||
# 追加依赖到 worklist(去重防溢出)
|
||
ret_w: int = _AppendDepsToWorklist(worklist, wl_len, deps_r2, processed)
|
||
if ret_w < 0:
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(sbuf_r)
|
||
stdlib.free(mod_buf)
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
stdlib.free(wl_pos)
|
||
mb.reset_to(scan_mark_r)
|
||
return -1
|
||
wl_len = sizet(ret_w)
|
||
else:
|
||
VLogger.debug(f"无依赖: {rel_path}", "Reachable")
|
||
|
||
# 释放 Translator 的 C malloc 资源 + 回收本文件 AST
|
||
if tr_r2._global_names is not None:
|
||
stdlib.free(tr_r2._global_names)
|
||
if tr_r2._nonlocal_names is not None:
|
||
stdlib.free(tr_r2._nonlocal_names)
|
||
stdlib.free(sbuf_r)
|
||
mb.reset_to(scan_mark_r)
|
||
|
||
stdlib.free(abs_path)
|
||
stdlib.free(rel_path)
|
||
stdlib.free(inc_dir)
|
||
stdlib.free(out_abs)
|
||
stdlib.free(out_rel)
|
||
stdlib.free(out_inc)
|
||
stdlib.free(mod_buf)
|
||
|
||
stdlib.free(worklist)
|
||
stdlib.free(processed)
|
||
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
|
||
|
||
# ============================================================
|
||
# 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 到当前模块
|
||
#
|
||
# 遍历 imported_modules(空格分隔的模块名):LookupSha1ByModName 取 SHA1 → _sliced_path 构造 .vpi 路径 → VpiReader.ParseVpiIntoModule 解析并注入 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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
temp_dir: str,
|
||
trans: rawptr[HandlesTranslator.Translator]) -> 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
|
||
|
||
# 每个模块的 stub 注入前重置 typedef 递归注册的防环标记:
|
||
# CTypedef 表每文件隔离(translate 前 clear),visited 若跨模块残留会
|
||
# 导致 stdint 等 typedef 只注册一次、后续模块注入再次解析时表里为空
|
||
# → 参数类型退化 i8_ptr_ty → 整数实参误报 CastError(FuncTest 根因)。
|
||
VpiReader.ResetVpiTypedefVisited()
|
||
# 按需注入防重入表同周期重置:注入目标 mod 每模块不同,跨模块沿用会让
|
||
# 后续模块命中缓存、拿不到本模块需要的 declare(包重导出按需注入用)。
|
||
VpiReader.ResetVpiInjectVisited()
|
||
|
||
td_len: sizet = string.strlen(temp_dir)
|
||
total_len: sizet = string.strlen(imported_modules)
|
||
success_count: int = 0
|
||
|
||
# 遍历 imported_modules 中的每个模块名(空格分隔)
|
||
ci: sizet = 0
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and imported_modules[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 提取模块名(到下一个空格或字符串末尾)
|
||
word_start: sizet = ci
|
||
while ci < total_len and imported_modules[ci] != ' ':
|
||
ci += 1
|
||
word_len: sizet = 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: rawptr[fileio.File] = 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
|