Files
ViperC/App/lib/Stub/StubMerger/StubMergerIR.vp
T
TermiNexus 7b3e814524 refactor(backend): 单端 MLIR —— 发射器全树改名 mlirlite 并删除 --backend 开关
- 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
2026-10-03 19:26:24 +08:00

317 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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