1230 lines
61 KiB
Plaintext
1230 lines
61 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
|
||
|
||
# ============================================================
|
||
# 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,去重)"""
|
||
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)
|
||
if df_ls.closed:
|
||
stdlib.free(dep_path)
|
||
# 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast),
|
||
# 非映射模块(如 t/c 内部库、用户文件)按设计跳过
|
||
if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None:
|
||
fb_ls: str = VLogger.fmt_buf()
|
||
if fb_ls is not None:
|
||
viperlib.snprintf(fb_ls, 1024, "依赖 stub 缺失: %s", dep_sha1)
|
||
VLogger.error(fb_ls, "StubMerger")
|
||
return out_pos
|
||
dep_br_ls: t.CInt64T = df_ls.read_all(dep_buf, 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 < STUB_READ_BUF_SIZE:
|
||
dep_buf[dep_br_ls] = '\0'
|
||
else:
|
||
dep_buf[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)
|
||
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
|
||
# 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 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'
|
||
if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at) != 0:
|
||
dep_buf[lp + 1] = saved
|
||
continue
|
||
dep_buf[lp + 1] = saved
|
||
# 去重:external global
|
||
if line_len > 0 and dep_buf[line_start] == '@':
|
||
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 + 1]
|
||
dep_buf[eq + 1] = '\0'
|
||
if string.strstr(out_buf, dep_buf + line_start) is not None:
|
||
dep_buf[eq + 1] = saved
|
||
continue
|
||
dep_buf[eq + 1] = 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
|
||
if dep_is_full_def == 1:
|
||
# 依赖行是完整定义
|
||
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
|
||
saved_eq: t.CChar = dep_buf[eq2]
|
||
dep_buf[eq2] = '\0'
|
||
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
|
||
commented: int = StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start)
|
||
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
|
||
has_full: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + line_start)
|
||
dep_buf[eq2] = saved_eq
|
||
if has_full == 1:
|
||
dep_buf[eq2 + 1] = saved2
|
||
continue
|
||
# 3. 追加完整定义
|
||
else:
|
||
# 依赖行是 opaque 声明,用原有子串匹配去重
|
||
found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start)
|
||
if found_opaque is not None:
|
||
dep_buf[eq2 + 1] = saved2
|
||
continue
|
||
dep_buf[eq2 + 1] = saved2
|
||
# 追加
|
||
if out_pos + line_len + 2 < out_size:
|
||
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'
|
||
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"""
|
||
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: 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:
|
||
fb_lt: str = VLogger.fmt_buf()
|
||
if fb_lt is not None:
|
||
viperlib.snprintf(fb_lt, 1024, "依赖 text 缺失: %s", dep_sha1)
|
||
VLogger.error(fb_lt, "StubMerger")
|
||
return out_pos
|
||
text_br: t.CInt64T = tf_lt.read_all(dep_buf, 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 < STUB_READ_BUF_SIZE:
|
||
dep_buf[text_br] = '\0'
|
||
else:
|
||
dep_buf[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
|
||
# 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句
|
||
saved_lt: t.CChar = dep_buf[lp_lt + 1]
|
||
dep_buf[lp_lt + 1] = '\0'
|
||
if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at_lt) != 0:
|
||
dep_buf[lp_lt + 1] = saved_lt
|
||
in_body = 1
|
||
continue
|
||
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:
|
||
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'
|
||
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:
|
||
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
|
||
saved_eq_td: t.CChar = dep_buf[eq_td]
|
||
dep_buf[eq_td] = '\0'
|
||
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
|
||
StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
|
||
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
|
||
has_full_td: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + ls)
|
||
dep_buf[eq_td] = saved_eq_td
|
||
if has_full_td == 0:
|
||
# 追加完整类型定义
|
||
if out_pos + ll + 2 < out_size:
|
||
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'
|
||
continue
|
||
|
||
# 提取全局变量定义(@... = ...)
|
||
# 依赖 text.ll 中的全局变量带初值定义(如 @StubMerger._mbuddy = global ...)
|
||
# stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义
|
||
# 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义
|
||
if ll > 0 and dep_buf[ls] == '@':
|
||
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'
|
||
# 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义)
|
||
# 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份
|
||
# 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致
|
||
# ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。
|
||
# 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。
|
||
if StubMergerIR._HasGlobalInBuf(out_buf, dep_buf + ls) == 0:
|
||
# 注释掉 out_buf 中的同名行(external 声明或旧定义)
|
||
StubMergerIR._CommentOutGlobalInBuf(out_buf, dep_buf + ls)
|
||
dep_buf[eq_gv] = saved_gv
|
||
# 追加完整定义
|
||
if out_pos + ll + 2 < out_size:
|
||
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'
|
||
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(MAX_INCLUDES * 17)
|
||
mod_arr: bytes = stdlib.malloc(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.fmt_buf()
|
||
if fb is not None:
|
||
viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count)
|
||
VLogger.debug(fb, "Reachable")
|
||
|
||
# 工作列表(空格分隔的模块名)
|
||
worklist: bytes = stdlib.malloc(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 < 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 < 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.fmt_buf()
|
||
if fb is not None:
|
||
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 < 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'
|
||
fb: str = VLogger.fmt_buf()
|
||
if fb is not None:
|
||
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
|
||
VLogger.debug(fb, "Reachable")
|
||
|
||
# 3. 工作列表算法:递归收集可达 SHA1
|
||
processed: bytes = stdlib.malloc(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 < 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) * 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 < 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,向后兼容 .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()
|
||
else:
|
||
# .vp 不存在,回退 .py
|
||
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf)
|
||
af = 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 未正确填充
|
||
fb_ff: str = VLogger.fmt_buf()
|
||
if fb_ff is not None:
|
||
viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s(sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1)
|
||
VLogger.error(fb_ff, "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)
|
||
|
||
fb: str = VLogger.fmt_buf()
|
||
if fb is not None:
|
||
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(按需加载)"""
|
||
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
|
||
|
||
# 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(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, STUB_READ_BUF_SIZE)
|
||
sf.close()
|
||
stdlib.free(stub_path)
|
||
if stub_br <= 0:
|
||
stdlib.free(stub_content)
|
||
return 0
|
||
if stub_br < STUB_READ_BUF_SIZE:
|
||
stub_content[stub_br] = '\0'
|
||
else:
|
||
stub_content[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 内容
|
||
if out_pos + slen + 2 < out_size:
|
||
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(MAX_INCLUDES * 17)
|
||
mod_arr: bytes = stdlib.malloc(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(MAX_INCLUDES_SHA1 * 17)
|
||
if new_scan_arr is not None:
|
||
string.memset(new_scan_arr, 0, MAX_INCLUDES_SHA1 * 17)
|
||
new_scan_count: int = StubMergerCollect._collect_stub_sha1s(
|
||
temp_dir, td_len, Config.Sha1SliceLevel, new_scan_arr, 0, 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(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
|
||
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: 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:
|
||
fb: str = VLogger.fmt_buf()
|
||
if fb is not None:
|
||
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 终止
|
||
fb_tlp: str = VLogger.fmt_buf()
|
||
if fb_tlp is not None:
|
||
viperlib.snprintf(fb_tlp, 1024, "本地 text.ll 缺失: %s", text_path)
|
||
VLogger.error(fb_tlp, "StubMerger")
|
||
stdlib.free(text_path)
|
||
else:
|
||
text_content: bytes = stdlib.malloc(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, STUB_READ_BUF_SIZE)
|
||
tf.close()
|
||
stdlib.free(text_path)
|
||
if text_br > 0:
|
||
if text_br < STUB_READ_BUF_SIZE:
|
||
text_content[text_br] = '\0'
|
||
else:
|
||
text_content[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
|
||
# 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 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'
|
||
if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, text_content + tx_at) != 0:
|
||
text_content[tx_lp + 1] = tx_sv
|
||
continue
|
||
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'
|
||
StubMergerIR._CommentOutDeclareInBuf(out_buf, text_content + tx_at_d)
|
||
text_content[tx_lp_d + 1] = tx_sv_d
|
||
# 处理:@global = ...
|
||
# stub 中可能是 external 声明或完整定义,以 text 中的定义为准
|
||
# 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的
|
||
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 + 1]
|
||
text_content[tx_eq + 1] = '\0'
|
||
StubMergerIR._CommentOutGlobalInBuf(out_buf, text_content + tx_ls)
|
||
text_content[tx_eq + 1] = tx_sv2
|
||
# 去重:%type = ...
|
||
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 + 1]
|
||
text_content[tx_eq2 + 1] = '\0'
|
||
if string.strstr(out_buf, text_content + tx_ls) is not None:
|
||
text_content[tx_eq2 + 1] = tx_sv3
|
||
continue
|
||
text_content[tx_eq2 + 1] = tx_sv3
|
||
# 追加
|
||
if out_pos + tx_ll + 2 < out_size:
|
||
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'
|
||
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)
|
||
|
||
# 如果精确查找失败,尝试包名回退(如 "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
|