- App/Test 的 .vp 中 llvmlite 全部改名为 mlirlite(import、限定名、类型注解、注释) - 删除后端开关:Config.set_backend/is_mlir_backend/alias_module_name、BackendTarget/BackendIsMlir、CLI --backend、options.backend/options.target 读取,以及 IncludesScanner/StubMergerIR 的模块别名调用 - BuildPipeline 的 mlir-opt -> mlir-translate 降级管线去掉开关门,改为恒执行 - backend_dir_suffix 改名 mlir_dir_suffix 并恒返回 _mlir:TempDir/OutputDir/includes 缓存继续落在 *_mlir,避免复用旧 LLVM 后端的 .ll/obj - project.vpj 删除已失效的 options.target
317 lines
14 KiB
Plaintext
317 lines
14 KiB
Plaintext
import lib.core.Gen.GenApi as GenApi
|
||
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import mlirlite
|
||
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.StubMergerBuild as StubMergerBuild
|
||
import lib.Stub.StubMerger as StubMerger
|
||
|
||
|
||
# ============================================================
|
||
# StubMergerIR - 模块结构化 sidecar({sha1}.meta)写出/读取、子模块判定与模块名-SHA1 映射工具
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# WriteModuleMeta - 落盘 {sha1}.meta(模块结构化 sidecar:函数清单 + 结构体布局 + 虚表)
|
||
#
|
||
# 行格式(每行一条,纯文本、无嵌套括号):
|
||
# FUNC <mangled> DEF|DECL DEF=本模块定义了该符号(有函数体,等同 .text.ll 的 define 行)
|
||
# STRUCT <sha1>.<类名> FIELDS <n> PACKED <0|1> 命名结构体字段数/是否 packed
|
||
# STRUCT <sha1>.<类名> OPAQUE 前向声明(opaque,无 body)
|
||
# VTABLE <sha1>.<类名> SLOTS <n> 虚表槽数(全局 @<类名>.vtable 的元素数)
|
||
# 数据源均为翻译期模型(Module.FuncHead / NamedTypeHead / GlobalHead),与 dump_ir 同源,
|
||
# 取代「从 IR 文本反推 declare-vs-define / 结构体布局 / 虚表槽数」。
|
||
#
|
||
# 写不出(路径/打开失败)即 fail-fast:缺 meta 会使 Phase B+/C 的声明文件判定与
|
||
# REPL 的布局恢复失效。
|
||
#
|
||
# Args: mod 已翻译完成的 LLVM 模块;temp_dir 基础目录;td_len temp_dir 长度;sha1 模块 SHA1
|
||
# Returns: 0=成功(失败经 VLogger.error 终止进程)
|
||
# ============================================================
|
||
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str,
|
||
td_len: sizet, sha1: str) -> int:
|
||
"""把本模块的函数定义/结构体布局/虚表清单写入 {sha1}.meta"""
|
||
if mod is None or temp_dir is None or sha1 is None:
|
||
VLogger.error("meta 写出:参数为空", "Meta")
|
||
return 1
|
||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||
if mpath is None:
|
||
VLogger.error("meta 写出:路径构造失败", "Meta")
|
||
return 1
|
||
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
|
||
if mf.closed:
|
||
VLogger.error(f"meta 写出:无法打开 {mpath}", "Meta")
|
||
return 1
|
||
cur: mlirlite.Function* = mod.FuncHead
|
||
steps: sizet = 0
|
||
while cur is not None:
|
||
steps += 1
|
||
if steps > 100000:
|
||
VLogger.error("meta 写出:函数链表成环/超限", "Meta")
|
||
break
|
||
nm: str = mlirlite.function_get_name(cur)
|
||
if nm is not None:
|
||
nlen: sizet = string.strlen(nm)
|
||
line: bytes = stdlib.malloc(nlen + 16)
|
||
if line is None:
|
||
VLogger.error("meta 写出:行缓冲分配失败", "Meta")
|
||
break
|
||
mark: str = "DECL"
|
||
if mlirlite.function_is_declared(cur) == 0:
|
||
mark = "DEF"
|
||
viperlib.snprintf(line, nlen + 16, "FUNC %s %s\n", nm, mark)
|
||
mf.write(line, string.strlen(line))
|
||
stdlib.free(line)
|
||
cur = mlirlite.function_get_next(cur)
|
||
# 2) 结构体布局:STRUCT <name> FIELDS <n> PACKED <0|1>(opaque → STRUCT <name> OPAQUE)
|
||
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
|
||
nt_steps: sizet = 0
|
||
while nt is not None:
|
||
nt_steps += 1
|
||
if nt_steps > 100000:
|
||
VLogger.error("meta 写出:命名类型链表成环/超限", "Meta")
|
||
break
|
||
tn: str = mlirlite.get_struct_name(nt.Ty)
|
||
if tn is not None and tn[0] != '\0':
|
||
tl: sizet = string.strlen(tn)
|
||
lb: bytes = stdlib.malloc(tl + 48)
|
||
if lb is None:
|
||
VLogger.error("meta 写出:行缓冲分配失败", "Meta")
|
||
break
|
||
fc: int = mlirlite.get_struct_fcount(nt.Ty)
|
||
if fc < 0:
|
||
viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn)
|
||
else:
|
||
viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked)
|
||
mf.write(lb, string.strlen(lb))
|
||
stdlib.free(lb)
|
||
nt = nt.Next
|
||
# 3) 虚表槽数:VTABLE <class> SLOTS <n>(全局名 <class>.vtable,Ty 匿名结构体 fcount=槽数)
|
||
gv: mlirlite.GlobalVariable* = mod.GlobalHead
|
||
gv_steps: sizet = 0
|
||
while gv is not None:
|
||
gv_steps += 1
|
||
if gv_steps > 100000:
|
||
VLogger.error("meta 写出:全局变量链表成环/超限", "Meta")
|
||
break
|
||
gn: str = gv.Name
|
||
if gn is not None:
|
||
gnl: sizet = string.strlen(gn)
|
||
if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0:
|
||
base_len: sizet = gnl - 7
|
||
base: bytes = stdlib.malloc(base_len + 1)
|
||
if base is not None:
|
||
string.strncpy(base, gn, base_len)
|
||
base[base_len] = '\0'
|
||
ar: int = mlirlite.get_struct_fcount(gv.Ty)
|
||
if ar < 0:
|
||
ar = 0
|
||
lb2: bytes = stdlib.malloc(base_len + 40)
|
||
if lb2 is None:
|
||
VLogger.error("meta 写出:行缓冲分配失败", "Meta")
|
||
else:
|
||
viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar)
|
||
mf.write(lb2, string.strlen(lb2))
|
||
stdlib.free(lb2)
|
||
stdlib.free(base)
|
||
gv = gv.Next
|
||
mf.close()
|
||
stdlib.free(mpath)
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# _MetaHasDef - 读 {sha1}.meta,判断模块是否含函数定义("FUNC ... DEF" 行)
|
||
#
|
||
# Returns: 1=含 DEF(实现文件);0=可读但无 DEF(纯声明);-1=meta 不存在/读失败。
|
||
# ============================================================
|
||
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||
"""读 {sha1}.meta:含 "FUNC ... DEF" 行返回 1,无则 0,文件不可读 -1"""
|
||
if temp_dir is None or sha1 is None:
|
||
return -1
|
||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||
if mpath is None:
|
||
return -1
|
||
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
|
||
stdlib.free(mpath)
|
||
if mf.closed:
|
||
return -1
|
||
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||
mbr: int64 = 0
|
||
if mbuf is not None:
|
||
mbr = mf.read_all(mbuf, StubMerger.STUB_READ_BUF_SIZE)
|
||
mf.close()
|
||
if mbuf is None:
|
||
return -1
|
||
if mbr <= 0:
|
||
# 空 meta:无 DEF,纯声明
|
||
stdlib.free(mbuf)
|
||
return 0
|
||
if mbr < StubMerger.STUB_READ_BUF_SIZE:
|
||
mbuf[mbr] = '\0'
|
||
# 逐行扫描:行尾 " DEF" 即为实现文件
|
||
mpos: sizet = 0
|
||
while mpos < mbr:
|
||
mls: sizet = mpos
|
||
while mpos < mbr:
|
||
if mbuf[mpos] == '\n':
|
||
break
|
||
mpos += 1
|
||
mll: sizet = mpos - mls
|
||
if mpos < mbr:
|
||
mpos += 1
|
||
if mll > 0 and mbuf[mls + mll - 1] == '\r':
|
||
mll -= 1
|
||
if mll >= 10 and string.strncmp(mbuf + mls, "FUNC ", 5) == 0:
|
||
if mbuf[mls + mll - 4] == ' ' and string.strncmp(mbuf + mls + mll - 3, "DEF", 3) == 0:
|
||
stdlib.free(mbuf)
|
||
return 1
|
||
stdlib.free(mbuf)
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# IsDeclOnlyModule - 判断模块是否为纯声明文件(读 {sha1}.meta 的结构化定义清单)
|
||
#
|
||
# 返回 1 = 纯声明(无 FUNC ... DEF,如全 pass 的模块,无需 .obj);0 = 实现文件(含 FUNC ... DEF);
|
||
# -1 = meta 不存在/无法判断。供 Phase B+/C 区分「声明文件无需 .obj」与「实现文件缺失」,
|
||
# 取代原扫描 text.ll 的 "define " 行。
|
||
# ============================================================
|
||
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||
"""判断模块是否为纯声明文件(meta 无 FUNC ... DEF 行)"""
|
||
h: int = _MetaHasDef(temp_dir, td_len, sha1)
|
||
if h < 0:
|
||
return -1
|
||
if h == 1:
|
||
return 0
|
||
return 1
|
||
# ============================================================
|
||
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
|
||
#
|
||
# deps_buf 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer
|
||
# 子模块类型;模块名含 '.' 时检查其顶层包名是否在 deps_buf 中(deps_buf = "ast stdio string" 时 "ast.lexer" → 1,"foo.bar" → 0)。
|
||
# ============================================================
|
||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
|
||
if deps_buf is None or mod_name is None:
|
||
return 0
|
||
# 查找第一个 '.',提取顶层包名
|
||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||
if dot_pos is None:
|
||
return 0
|
||
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
||
if pkg_len == 0 or pkg_len >= 256:
|
||
return 0
|
||
# 构造顶层包名(截断到 '.' 之前)
|
||
# 直接在 mod_name 上临时截断检查,避免分配
|
||
saved_ch: char = mod_name[pkg_len]
|
||
mod_name[pkg_len] = '\0'
|
||
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
||
mod_name[pkg_len] = saved_ch
|
||
return result
|
||
# ============================================================
|
||
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
|
||
#
|
||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
|
||
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
|
||
# ============================================================
|
||
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet,
|
||
sha1_list: str,
|
||
mod_list: str) -> int:
|
||
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
|
||
|
||
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
|
||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||
"""
|
||
if sha1_list is None or mod_list is None:
|
||
return 0
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
||
return 0
|
||
count: int = 0
|
||
for i in range(StubMerger._sha1_store_count):
|
||
if count >= StubMerger.MAX_INCLUDES:
|
||
break
|
||
idx: sizet = sizet(i) * 17
|
||
idx2: sizet = sizet(i) * 64
|
||
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
||
count += 1
|
||
return count
|
||
# ============================================================
|
||
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配;供依赖图分析「模块名 → SHA1」)
|
||
#
|
||
# Args: sha1_arr SHA1 数组(每个 17 字节);mod_arr 模块名数组(每个 64 字节);count 映射条目数;mod_name 要查找的模块名
|
||
# Returns: SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
|
||
# ============================================================
|
||
def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
|
||
count: int, mod_name: str) -> str:
|
||
"""从模块名查找 SHA1(精确匹配 + 包名回退)
|
||
|
||
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||
"""
|
||
if sha1_arr is None or mod_arr is None or mod_name is None:
|
||
return None
|
||
if count <= 0:
|
||
return None
|
||
# 第一遍:精确匹配
|
||
for i in range(count):
|
||
idx: sizet = sizet(i) * 64
|
||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
|
||
# (热点:本函数全表逐条 strcmp 查名)。
|
||
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
||
sidx: sizet = sizet(i) * 17
|
||
return sha1_arr + sidx
|
||
# 第二遍:包名回退("ast" → "ast.__init__")
|
||
nm_len: sizet = string.strlen(mod_name)
|
||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||
if init_buf is not None:
|
||
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
||
for i in range(count):
|
||
idx2: sizet = sizet(i) * 64
|
||
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||
sidx2: sizet = sizet(i) * 17
|
||
stdlib.free(init_buf)
|
||
return sha1_arr + sidx2
|
||
stdlib.free(init_buf)
|
||
return None
|
||
# ============================================================
|
||
# _AddSha1ToSet - 将 SHA1 加入集合(去重;已在集合中则 count 不变)
|
||
#
|
||
# Args: set_buf SHA1 集合缓冲区(每个 17 字节);count 当前集合中的 SHA1 数量;sha1 要加入的 SHA1
|
||
# Returns: 新的 count
|
||
# ============================================================
|
||
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
||
"""将 SHA1 加入集合,返回新 count"""
|
||
if set_buf is None or sha1 is None:
|
||
return count
|
||
for i in range(count):
|
||
idx: sizet = sizet(i) * 17
|
||
if string.strcmp(set_buf + idx, sha1) == 0:
|
||
return count
|
||
if count < StubMerger.MAX_INCLUDES_SHA1:
|
||
idx: sizet = sizet(count) * 17
|
||
string.strcpy(set_buf + idx, sha1)
|
||
return count + 1
|
||
return count
|