更新,可自举

This commit is contained in:
TermiNexus committed 2026-08-27 09:48:20 +08:00
1 parent 07db560510
commit ab0f37e1a8
35 files changed
+3759 -1518

No files matched your search

+115
View File
@@ -157,6 +157,9 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
# P2-P5: 注入各 Handles 模块级单例(委托函数经本地 _g_gen 访问,
# 避免函数体 import 包模块与类/模块名歧义)
GenApi._g_gen = gen
HandlesTranslator._g_gen = gen
HandlesExpr._g_gen = gen
HandlesExprCall._g_gen = gen
# P6: 注入语句/赋值/变量/导入/翻译驱动模块的单例
# 设置当前文件名(供报错使用)
GenApi.set_current_file_type(file_path)
@@ -836,6 +839,63 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
return 0
def asm_to_obj(asm_path: str, sha1_val: str, output_dir: str,
triple: str) -> int:
"""用 llvm-mc 汇编 .s 源文件为 .obj(切片路径)
裸机项目(VKernel 等)含 .s 汇编文件(如 Kernel/intr/isr.s 定义
isr0/syscall_entry/_per_cpu 等符号)。.s 不经过 TPV 翻译/llc,
直接用配套的 llvm-mc 汇编为 .obj,obj 路径与其他模块一致
(output_dir/{sha1前缀}/{sha1}.obj),参与 Phase C 链接。
Args:
asm_path: .s 源文件完整路径
sha1_val: 文件内容 SHA1 前16字符(切片路径用)
output_dir: 输出目录
triple: 目标 triple(如 x86_64-none-elf)
Returns:
0 成功,非 0 失败
"""
if asm_path is None or sha1_val is None or output_dir is None:
return 1
obj_path: str = build_sliced_path(output_dir, sha1_val, "obj")
if obj_path is None:
return 1
trip: str = triple
if trip is None or trip[0] == '\0':
trip = "x86_64-none-elf"
cmd_len: t.CSizeT = len("llvm-mc -triple=") + len(trip) \
+ len(" -filetype=obj -o ") + len(obj_path) + len(asm_path) + 64
cmd: bytes = _mbuddy.alloc(cmd_len)
if cmd is None:
return 1
viperlib.snprintf(cmd, cmd_len, "llvm-mc -triple=%s -filetype=obj -o %s %s",
trip, obj_path, asm_path)
# --phase2command 模式:命令写入 bat(与 llc/链接一致)
if _phase2_cmd_path is not None:
_phase2_write_cmd(cmd)
return 0
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("llvm-mc subprocess.run 返回 None", "asm")
return 1
if result.returncode != 0:
if result.stdout is not None:
stdio.printf("%s\n", result.stdout)
if result.stderr is not None:
stdio.printf("%s\n", result.stderr)
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "llvm-mc 汇编失败,返回码: %d, 命令: %s",
result.returncode, cmd)
VLogger.error(fb, "asm")
return 1
return 0
def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT,
temp_dir: str, output_dir: str, module_name: str,
cc_cmd: str, cc_flags: str) -> int:
@@ -899,6 +959,61 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
if obj_paths is None or obj_paths_len == 0 or linker_cmd is None:
return 1
# 去重 .obj 路径:同一模块的 obj 可能被多个收集环节加入(Phase B+
# 新编译 obj_paths 与 Phase C reachable 收集指向 includes.binary 中
# 同一文件)。App 项目 linker flags 含 --allow-multiple-definition 可
# 容忍;VKernel 无此 flag → ld.lld "duplicate symbol"。链接命令中同一
# 路径出现两次即重复符号,保留首次出现的路径,后续重复跳过。
if obj_paths_len > 0:
dedup: bytes = _mbuddy.alloc(obj_paths_len + 1)
if dedup is not None:
dedup[0] = '\0'
dpos: t.CSizeT = 0
dsrc_i: t.CSizeT = 0
while dsrc_i < obj_paths_len:
# 跳过前导空格
while dsrc_i < obj_paths_len and obj_paths[dsrc_i] == ' ':
dsrc_i += 1
if dsrc_i >= obj_paths_len:
break
dstart: t.CSizeT = dsrc_i
while dsrc_i < obj_paths_len and obj_paths[dsrc_i] != ' ':
dsrc_i += 1
dtlen: t.CSizeT = dsrc_i - dstart
# 检查 dedup 中是否已有相同 token
ddup: int = 0
dj: t.CSizeT = 0
while dj < dpos:
while dj < dpos and dedup[dj] == ' ':
dj += 1
if dj >= dpos:
break
ds2: t.CSizeT = dj
while dj < dpos and dedup[dj] != ' ':
dj += 1
dt2len: t.CSizeT = dj - ds2
if dt2len == dtlen:
deq: int = 1
dk: t.CSizeT = 0
while dk < dtlen:
if dedup[ds2 + dk] != obj_paths[dstart + dk]:
deq = 0
break
dk += 1
if deq != 0:
ddup = 1
break
if ddup == 0:
if dpos > 0:
dedup[dpos] = ' '
dpos += 1
string.memcpy(dedup + dpos, obj_paths + dstart, dtlen)
dpos += dtlen
dedup[dpos] = '\0'
if dpos > 0 and dpos != obj_paths_len:
obj_paths = dedup
obj_paths_len = dpos
# 收集 includes.binary 的 .obj 文件路径
EXTRA_BUF_SIZE: t.CSizeT = 32768
extra_objs: bytes = _mbuddy.alloc(EXTRA_BUF_SIZE)
+105 -13
View File
@@ -74,8 +74,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
if log is not None:
log.banner("Phase1: 扫描 includes(按需翻译)")
# 扫描 includes 目录
# 扫描 includes 目录(含每文件 SHA1 计算计时)
t_scan0: t.CUInt64T = w32.win32base.GetTickCount64()
result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
t_scan1: t.CUInt64T = w32.win32base.GetTickCount64()
fb_scan: str = VLogger.fmt_buf()
if fb_scan is not None:
viperlib.snprintf(fb_scan, 1024,
"扫描 includes 总耗时: %llu ms (%d 文件, 含 SHA1 计算)",
t_scan1 - t_scan0, result.Count if result is not None else 0)
VLogger.info(fb_scan, "Phase1")
if result is None:
VLogger.error("扫描失败", "Phase1")
return 1
@@ -126,6 +134,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_registered: int = 0
p1a_skipped: int = 0
p1a_failed: int = 0
# 1a-pre 阶段总耗时起点(GetTickCount64,ms;对齐 Phase2 计时插桩)
t_pre0: t.CUInt64T = w32.win32base.GetTickCount64()
for i in range(result.Count):
# 文件级 mark:import 依赖收集完(deps 写盘 + PopulateIncludesDeps
@@ -259,6 +269,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 回收本文件 AST/lexer/token(deps 已写盘、依赖存储器已深拷贝)
mb.reset_to(scan_mark_a)
# 1a-pre 阶段总耗时(全部 includes 文件读+parse+import 依赖扫描)
t_pre1: t.CUInt64T = w32.win32base.GetTickCount64()
fb_pre: str = VLogger.fmt_buf()
if fb_pre is not None:
viperlib.snprintf(fb_pre, 1024, "1a-pre 总耗时: %llu ms (%d 文件扫描 import 依赖)",
t_pre1 - t_pre0, result.Count)
VLogger.info(fb_pre, "Phase1")
if p1a_failed > 0:
fb_p1apre: str = VLogger.fmt_buf()
if fb_p1apre is not None:
@@ -397,6 +415,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 遍历文件:优先使用拓扑顺序,回退到字母序
iter_count: int = topo_count if topo_count > 0 else result.Count
# 1a 阶段总耗时起点(struct/enum/union 注册)
t_a0: t.CUInt64T = w32.win32base.GetTickCount64()
for i in range(iter_count):
# 每文件级 mark:翻译完回收该文件 AST(注册数据在 perm_pool)
p1a_mark: t.CSizeT = mb.mark()
@@ -430,6 +450,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_skp += 1
continue
# 单文件计时起点(读文件 + parse 段,定位 1a 卡点)
t_fa0: t.CUInt64T = w32.win32base.GetTickCount64()
# 读取文件内容
file_path_r: str = entry_r.Path
f_r: fileio.File | t.CPtr = fileio.File(file_path_r, fileio.MODE.R)
@@ -468,6 +491,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_fl += 1
continue
# 计时点 1:读文件 + parse 完成
t_fa1: t.CUInt64T = w32.win32base.GetTickCount64()
# 创建 Translator,设置 _declare_only=1(注册 struct/enum/union)
tr_r: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if tr_r is None:
@@ -484,11 +510,22 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
GenApi.clear_ctypedefs()
GenApi.reset_visible_structs(mb, 0)
ret_r: int = tr_r.translate(tree_r)
# 计时点 2:translate(declare=1) 返回
t_fa2: t.CUInt64T = w32.win32base.GetTickCount64()
if ret_r != 0:
p1a_fl += 1
else:
p1a_reg += 1
# 逐文件进度日志(parse/translate 两段,定位 1a 卡点)
fb_1a: str = VLogger.fmt_buf()
if fb_1a is not None:
viperlib.snprintf(fb_1a, 1024,
"[%d/%d] 1a: %s (parse=%llu ms, translate=%llu ms, total=%llu ms)",
i + 1, iter_count, file_path_r,
t_fa1 - t_fa0, t_fa2 - t_fa1, t_fa2 - t_fa0)
VLogger.info(fb_1a, "Phase1")
# 释放 Translator 的 C malloc 资源
if tr_r._global_names is not None:
stdlib.free(tr_r._global_names)
@@ -500,6 +537,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# reset 不影响;IncludesScanner result 等在 p1a_mark 前分配同样保留)
mb.reset_to(p1a_mark)
# 1a 阶段总耗时(struct/enum/union 注册,含每文件读+parse+翻译声明)
t_a1: t.CUInt64T = w32.win32base.GetTickCount64()
fb_a: str = VLogger.fmt_buf()
if fb_a is not None:
viperlib.snprintf(fb_a, 1024, "1a 总耗时: %llu ms (注册 struct/enum, reg=%d skp=%d)",
t_a1 - t_a0, p1a_reg, p1a_skp)
VLogger.info(fb_a, "Phase1")
if p1a_fl > 0:
fb_p1ar: str = VLogger.fmt_buf()
if fb_p1ar is not None:
@@ -544,6 +589,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# ============================================================
p1bpre_failed: int = 0
dir_len_pv: t.CSizeT = string.strlen(temp_dir)
# 1b-pre 阶段总耗时起点(vpi 预生成)
t_pv0: t.CUInt64T = w32.win32base.GetTickCount64()
for i_pv in range(result.Count):
entry_addr_pv: t.CUInt64T = t.CUInt64T(result.Entries) + i_pv * entry_size
entry_pv: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_pv, t.CPtr))
@@ -620,6 +667,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 回收本文件 AST(vpi 已写盘,AST 不再需要;mark1 后无其他持久数据)
mb.reset_to(mark1)
# 1b-pre 阶段总耗时(vpi 预生成)
t_pv1: t.CUInt64T = w32.win32base.GetTickCount64()
fb_pv: str = VLogger.fmt_buf()
if fb_pv is not None:
viperlib.snprintf(fb_pv, 1024, "1b-pre 总耗时: %llu ms (vpi 预生成)",
t_pv1 - t_pv0)
VLogger.info(fb_pv, "Phase1")
if p1bpre_failed > 0:
fb_p1bpre: str = VLogger.fmt_buf()
if fb_p1bpre is not None:
@@ -632,6 +687,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
translated: int = 0
skipped: int = 0
failed: int = 0
# 1b 阶段总耗时起点(全量翻译)
t_b0: t.CUInt64T = w32.win32base.GetTickCount64()
loop_n_b: int = result.Count
if topo_count > 0:
@@ -705,6 +762,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(stub_path)
# 单文件计时起点:读文件(parse/translate/dump 三段,对齐 Phase2 插桩)
t_f0: t.CUInt64T = w32.win32base.GetTickCount64()
# 读取文件内容
file_path: str = entry.Path
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
@@ -762,6 +822,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
failed += 1
continue
# 计时点 1:读文件 + lexer/tokenize/parse 完成
t_f1: t.CUInt64T = w32.win32base.GetTickCount64()
# 文本扫描注册 CTypedef(stdint.vp 崩溃根因修复):AST 字符串字段
# (attr/id)token 零拷贝指向 src_buf,跨阶段内容不稳定(悬空),
# ScanCTypedefDeclarations 读 attr 会 0xC0000005。改用源码文本逐行
@@ -769,18 +832,27 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
GenApi.RegisterCTypedefFromSource(mb, src_buf)
# 生成 .vpi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter)
pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE)
if pyi_pos > 0:
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
if pyi_path is not None:
pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.W)
if not pf.closed:
pf.write(pyi_buf, pyi_pos)
pf.close()
stdlib.free(pyi_path)
stdlib.free(pyi_buf)
# 1b-pre 已为所有待翻译文件生成 vpi,此处仅兜底:vpi 缺失才生成
# (消除 1b-pre 与 1b 对同一文件的重复 _GeneratePyiFromAst 工作)
vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
if vpi_path_1b is not None:
vf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.R)
vpi_exists_1b: int = 0
if not vf_1b.closed:
vf_1b.close()
vpi_exists_1b = 1
if vpi_exists_1b == 0:
pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(
mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE)
if pyi_pos > 0:
pf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.W)
if not pf_1b.closed:
pf_1b.write(pyi_buf, pyi_pos)
pf_1b.close()
stdlib.free(pyi_buf)
stdlib.free(vpi_path_1b)
# 翻译 AST → LLVM IR
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
@@ -810,6 +882,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.exit(1)
ret: int = tr.translate(tree)
# 计时点 2:translate 返回
t_f2: t.CUInt64T = w32.win32base.GetTickCount64()
# 诊断:translate 正常返回(若挂起在 translate 内部,此行不会打印,
# 用于区分"translate 内部挂起"与"后续 dump 挂起")
if ret == 0:
@@ -891,6 +965,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(text_path_p1)
stdlib.free(text_buf)
# 计时点 3:dump(stub+text)完成;输出单文件三段耗时
t_f3: t.CUInt64T = w32.win32base.GetTickCount64()
fb_t: str = VLogger.fmt_buf()
if fb_t is not None:
viperlib.snprintf(fb_t, 1024,
"[%d/%d] 翻译: %s (parse=%llu ms, translate=%llu ms, dump=%llu ms, total=%llu ms)",
i + 1, loop_n_b, file_path,
t_f1 - t_f0, t_f2 - t_f1, t_f3 - t_f2, t_f3 - t_f0)
VLogger.info(fb_t, "Phase1")
# save dependencies(_imported_modules) for Phase B
deps_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt")
if deps_path_p1 is not None:
@@ -945,6 +1029,14 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
VLogger.error(fb, "Phase1")
stdlib.exit(1)
# 1b 阶段总耗时(全量翻译 + dump)
t_b1: t.CUInt64T = w32.win32base.GetTickCount64()
fb_b: str = VLogger.fmt_buf()
if fb_b is not None:
viperlib.snprintf(fb_b, 1024, "1b 总耗时: %llu ms (全量翻译, trans=%d skip=%d fail=%d)",
t_b1 - t_b0, translated, skipped, failed)
VLogger.info(fb_b, "Phase1")
# 释放 Phase 1b 使用的拓扑排序资源
if topo_order is not None:
mb.free(topo_order)
+34 -24
View File
@@ -4,6 +4,7 @@ import string
import stdlib
import memhub
import viperlib
import sys
import w32.fileio as fileio
import w32.win32file
import w32.win32base
@@ -379,8 +380,17 @@ def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
# ============================================================
def _ScanTreeForContainers(node: ast.AST | t.CPtr,
need_list_box: t.CPtr,
need_dict_box: t.CPtr):
need_dict_box: t.CPtr,
depth: int = 0):
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
if depth > 128:
fb_stc: str = VLogger.fmt_buf()
if fb_stc is not None:
viperlib.snprintf(fb_stc, 256, "容器扫描递归过深(AST 环或嵌套超过 128 层)")
VLogger.error(fb_stc, "Phase1")
sys.exit(1)
if node is None:
return
k_s: int = node.kind()
@@ -393,21 +403,21 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
need_dict_box[0] = 1
elif k_s == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box)
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BinOp:
bop_s: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
_ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box)
_ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box)
_ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Call:
cl_s2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box)
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
cl_args_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.args
if cl_args_s is not None:
na_s: t.CSizeT = len(cl_args_s)
i_a2: t.CSizeT
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box)
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.keywords
if cl_kws_s is not None:
nk_s: t.CSizeT = len(cl_kws_s)
@@ -415,23 +425,23 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
for i_k2 in range(nk_s):
kw_s: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(cl_kws_s.get(i_k2))
if kw_s is not None:
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Assign:
asn_s: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
tgts_s: list[ast.AST | t.CPtr] | t.CPtr = asn_s.targets
if tgts_s is not None:
nt_s: t.CSizeT = len(tgts_s)
i_t2: t.CSizeT
for i_t2 in range(nt_s):
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box)
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.AnnAssign:
aa_s2: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box)
_ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box)
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.FunctionDef:
fn_s: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box)
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
fn_args_s: ast.Arguments | t.CPtr = fn_s.args
if fn_args_s is not None:
fa_s: list[ast.AST | t.CPtr] | t.CPtr = fn_args_s.args
@@ -441,7 +451,7 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
for i_f2 in range(nf_s):
arg_s: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(fa_s.get(i_f2))
if arg_s is not None:
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box)
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node)
bases_s: list[ast.AST | t.CPtr] | t.CPtr = cd_s.bases
@@ -449,10 +459,10 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
nb_s: t.CSizeT = len(bases_s)
i_b2: t.CSizeT
for i_b2 in range(nb_s):
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box)
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Attribute:
at_s: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Tuple:
tu_s: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
elts_s: list[ast.AST | t.CPtr] | t.CPtr = tu_s.elts
@@ -460,25 +470,25 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
ne_s: t.CSizeT = len(elts_s)
i_e2: t.CSizeT
for i_e2 in range(ne_s):
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box)
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.UnaryOp:
uop_s: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box)
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Expr:
ex_s: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Return:
rt_s: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node)
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box)
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Compare:
cp_s: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
cps_s: list[ast.AST | t.CPtr] | t.CPtr = cp_s.comparators
if cps_s is not None:
nc_s: t.CSizeT = len(cps_s)
i_c2: t.CSizeT
for i_c2 in range(nc_s):
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box)
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
bvs_s: list[ast.AST | t.CPtr] | t.CPtr = bo_s.values
@@ -486,7 +496,7 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
nv_s: t.CSizeT = len(bvs_s)
i_v2: t.CSizeT
for i_v2 in range(nv_s):
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box)
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.List:
# [] 字面量
need_list_box[0] = 1
@@ -499,4 +509,4 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
n_s: t.CSizeT = len(ch_s)
ci_s: t.CSizeT
for ci_s in range(n_s):
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box)
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
+102 -6
View File
@@ -35,6 +35,7 @@ LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.
LinkerOutput: str = None
TargetTriple: str = None
TargetDataLayout: str = None
ProjectDir: str = None # project.vpj 所在目录(resolve_paths 设置)
Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级)
StrictMode: int = 0
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
@@ -146,7 +147,7 @@ def Load_project_config(path: str) -> int:
# 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs)
includes_val: JsonValue | t.CPtr = root["includes"]
if includes_val is not None and includes_val.is_array():
arr_len: t.CSizeT = includes_val.__len__()
arr_len: t.CSizeT = len(includes_val)
inc_buf: bytes = stdlib.malloc(2048)
if inc_buf is not None:
inc_buf[0] = '\0'
@@ -186,12 +187,10 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
Returns:
空格分隔的字符串,或 None 如果 flags_val 为 None 或非数组或空数组
"""
if flags_val is None:
return None
if not flags_val.is_array():
return None
if flags_val is None: return None
if not flags_val.is_array(): return None
arr_len: t.CSizeT = flags_val.__len__()
arr_len: t.CSizeT = len(flags_val)
if arr_len == 0:
return None
@@ -385,11 +384,14 @@ def resolve_paths(project_dir: str) -> int:
0 表示成功,非 0 表示失败
"""
global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs
global ProjectDir
if project_dir is None:
return 1
if _mbuddy is None:
return 1
ProjectDir = project_dir
SourceDir = _join_path(SourceDir, project_dir)
BuildDir = _join_path(BuildDir, project_dir)
@@ -441,9 +443,103 @@ def resolve_paths(project_dir: str) -> int:
IncludesDirs = id_out
if IncludesDir is not None:
IncludesDir = _join_path(IncludesDir, project_dir)
# linker.flags 中 `-T <脚本>` 的链接脚本路径同样基于 project_dir 解析,
# 否则 subprocess 按进程 cwd(TransPyC/)找 linker.ld → ld.lld 报
# "cannot find linker script linker.ld"
_resolve_linker_t_paths(project_dir)
return 0
def _join_path_abs(rel: str, project_dir: str) -> str:
"""将相对路径基于 project_dir 拼接为绝对路径(用于 -T 链接脚本等)
已为绝对路径(/ \\ 开头)或盘符形式(x:)时原样返回。
"""
if rel is None:
return None
rel_len: t.CSizeT = string.strlen(rel)
if rel_len == 0:
return rel
if rel[0] == '/' or rel[0] == '\\':
return rel
if rel_len >= 2 and rel[1] == ':':
return rel
dir_len: t.CSizeT = string.strlen(project_dir)
if dir_len == 0:
return rel
total_len: t.CSizeT = dir_len + 1 + rel_len + 1
buf: str = _mbuddy.alloc(total_len)
if buf is None:
return rel
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
return buf
def _resolve_linker_t_paths(project_dir: str) -> None:
"""将 LinkerFlags 中 `-T <相对路径>` 的链接脚本路径解析为绝对路径
ld.lld 的 -T 参数若是相对路径,将按 subprocess 进程 cwd 解析而找不到
脚本。逐 token 扫描 LinkerFlags,遇 -T 其后 token 若非绝对路径则基于
project_dir 拼接(与 resolve_paths 对其他相对路径的处理保持一致)。
"""
global LinkerFlags
if LinkerFlags is None or project_dir is None:
return
fl_total: t.CSizeT = string.strlen(LinkerFlags)
if fl_total == 0:
return
out: str = _mbuddy.alloc(fl_total * 2 + 512)
if out is None:
return
out[0] = '\0'
out_pos: t.CSizeT = 0
i: t.CSizeT = 0
prev_is_t: int = 0 # 前一 token 是否为 -T
while i < fl_total:
# 跳过前导空格
while i < fl_total and LinkerFlags[i] == ' ':
i += 1
if i >= fl_total:
break
start: t.CSizeT = i
while i < fl_total and LinkerFlags[i] != ' ':
i += 1
tok_len: t.CSizeT = i - start
is_t_flag: int = 0
if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T':
is_t_flag = 1
# 输出分隔符(前一 token 之后)
if out_pos > 0:
out[out_pos] = ' '
out_pos += 1
if is_t_flag:
string.strcpy(out + out_pos, "-T")
out_pos += 2
elif prev_is_t != 0:
# -T 后的脚本路径:非绝对路径则基于 project_dir 拼接
tok_buf: str = _mbuddy.alloc(tok_len + 1)
if tok_buf is not None:
string.memcpy(tok_buf, LinkerFlags + start, tok_len)
tok_buf[tok_len] = '\0'
abs_path: str = _join_path_abs(tok_buf, project_dir)
if abs_path is not None:
p_len: t.CSizeT = string.strlen(abs_path)
string.strcpy(out + out_pos, abs_path)
out_pos += p_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
else:
string.memcpy(out + out_pos, LinkerFlags + start, tok_len)
out_pos += tok_len
prev_is_t = is_t_flag
out[out_pos] = '\0'
LinkerFlags = out
def get_includes_binary_dir() -> str:
"""获取 includes.binary 目录路径(includes 目录 + 缓存后缀)
+10 -8
View File
@@ -89,16 +89,18 @@ def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str:
# ============================================================
# is_vp_or_py_at - 检查路径段是否以 .vp/.py 后缀结尾
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
# ============================================================
def is_vp_or_py_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int:
"""检查 p[start, start+plen) 是否以 .vp/.py 后缀结尾(1=是,0=否)"""
def is_vp_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int:
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
"""
if p is None or plen <= 3:
return 0
base: t.CSizeT = start + plen - 3
if p[base] == '.':
if p[base + 1] == 'v' and p[base + 2] == 'p': return 1
if p[base + 1] == 'p' and p[base + 2] == 'y': return 1
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
return 1
return 0
@@ -167,8 +169,8 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
if psep_ch == '/' or psep_ch == '\\':
path_start = prefix_len
plen = plen - prefix_len
# 去掉 .vp 或 .py 后缀(.vp 为 Viper 源,.py 向后兼容)
if is_vp_or_py_at(path, path_start, plen) != 0:
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
if is_vp_at(path, path_start, plen) != 0:
plen -= 3
if plen == 0:
return None
+133 -45
View File
@@ -1,4 +1,5 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.VLogger as VLogger
import t, c
from stdint import *
import ast
@@ -6,6 +7,7 @@ import string
import memhub
import viperlib
import stdlib
import sys
# ============================================================
# StubGen.Converter - vpi 存根文件生成器
@@ -184,7 +186,7 @@ def _eval_cdefine_value_cache(node: ast.AST | t.CPtr) -> t.CInt64T:
return _eval_binop_int(node)
if k == ast.ASTKind.Call:
cl_c: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl_c.args is not None and cl_c.args.__len__() > 0:
if cl_c.args is not None and len(cl_c.args) > 0:
return _eval_cdefine_value_cache(cl_c.args.get(0))
return 0
if k == ast.ASTKind.Name:
@@ -244,8 +246,18 @@ def _PyiIndent(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# 支持: Name, Attribute, Subscript, BinOp(|), Constant(前向引用), Tuple
# ============================================================
def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
annot: ast.AST | t.CPtr) -> t.CSizeT:
annot: ast.AST | t.CPtr,
depth: int = 0) -> t.CSizeT:
"""递归生成类型注解字符串,返回新的 pos"""
# 递归深度守卫:AST 损坏成自引用环(Attribute.value/Subscript.slice/
# BinOp.left 等回指祖先)时无限递归直至栈耗尽(Phase1 vpi 生成最先
# 执行,早于翻译阶段)。fail-fast 终止,不静默回退。
if depth > 64:
fb_pt: str = VLogger.fmt_buf()
if fb_pt is not None:
viperlib.snprintf(fb_pt, 256, "vpi 类型注解递归过深(AST 环或嵌套超过 64 层)")
VLogger.error(fb_pt, "StubGen")
sys.exit(1)
if annot is None:
return _PyiEmit(buf, buf_size, pos, "t.CInt")
@@ -261,7 +273,7 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# Attribute: 属性引用 (如 t.CInt, memhub.MemBuddy)
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
pos = _PyiTypeStr(buf, buf_size, pos, at.value)
pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, ".")
if at.attr is not None:
pos = _PyiEmit(buf, buf_size, pos, at.attr)
@@ -270,18 +282,18 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# Subscript: 下标类型 (如 list[int], t.CArray[elem, count])
if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
pos = _PyiTypeStr(buf, buf_size, pos, sub.value)
pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "[")
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice)
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "]")
return pos
# BinOp: 联合类型 (如 int | str, File | t.CPtr)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.left)
pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, " | ")
pos = _PyiTypeStr(buf, buf_size, pos, bop.right)
pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1)
return pos
# Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count)
@@ -313,30 +325,30 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(annot)
if tup.elts is not None:
tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts
tn: t.CSizeT = tup_elts.__len__()
tn: t.CSizeT = len(tup_elts)
ti: t.CSizeT = 0
while ti < tn:
if ti > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
el: ast.AST | t.CPtr = tup_elts.get(ti)
pos = _PyiTypeStr(buf, buf_size, pos, el)
pos = _PyiTypeStr(buf, buf_size, pos, el, depth + 1)
ti += 1
return pos
# Call: 函数调用表达式 (用于装饰器如 @t.NoVTable())
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(annot)
pos = _PyiTypeStr(buf, buf_size, pos, cl.func)
pos = _PyiTypeStr(buf, buf_size, pos, cl.func, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "(")
if cl.args is not None:
cl_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
can: t.CSizeT = cl_args.__len__()
can: t.CSizeT = len(cl_args)
cai: t.CSizeT = 0
while cai < can:
if cai > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
ca: ast.AST | t.CPtr = cl_args.get(cai)
pos = _PyiTypeStr(buf, buf_size, pos, ca)
pos = _PyiTypeStr(buf, buf_size, pos, ca, depth + 1)
cai += 1
pos = _PyiEmit(buf, buf_size, pos, ")")
return pos
@@ -345,6 +357,79 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
return _PyiEmit(buf, buf_size, pos, "t.CInt")
# ============================================================
# _PyiDefaultStr - 生成默认值表达式文本
#
# 用于 vpi 签名 '= <default>' 输出(如 depth: int = 0)。
# vpi stub 必须保留默认值:VpiReader 解析后注册默认参数表,
# 跨模块缺参调用(如 resolve_annotation_type 的 depth=0)才能
# 用默认值填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。
# 支持: Constant(int/str/bool/None), Name(CDefine 常量), UnaryOp。
# ============================================================
def _PyiDefaultStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
node: ast.AST | t.CPtr,
depth: int = 0) -> t.CSizeT:
"""生成默认值表达式文本到 buf,返回新的 pos"""
# 递归深度守卫:AST 损坏成自引用环时 fail-fast 终止(同 _PyiTypeStr)
if depth > 64:
fb_pd: str = VLogger.fmt_buf()
if fb_pd is not None:
viperlib.snprintf(fb_pd, 256, "vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)")
VLogger.error(fb_pd, "StubGen")
sys.exit(1)
if node is None:
return pos
k: int = node.kind()
# Constant: 数字/字符串/bool/None 直接输出字面量
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
num_buf_dv: str = stdlib.malloc(24)
if num_buf_dv is not None:
viperlib.snprintf(num_buf_dv, 24, "%d", cn.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_dv)
stdlib.free(num_buf_dv)
return pos
if cn.const_kind == ast.CONST_STR:
pos = _PyiEmit(buf, buf_size, pos, '"')
if cn.str_val is not None:
pos = _PyiEmit(buf, buf_size, pos, cn.str_val)
return _PyiEmit(buf, buf_size, pos, '"')
if cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return _PyiEmit(buf, buf_size, pos, "True")
return _PyiEmit(buf, buf_size, pos, "False")
if cn.const_kind == ast.CONST_NONE:
return _PyiEmit(buf, buf_size, pos, "None")
return pos
# Name: CDefine 常量名(如 share: ULONG = SHARE_READ)
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
return _PyiEmit(buf, buf_size, pos, nm.id)
return pos
# UnaryOp: 一元运算(如 -1, ~0)
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop.op == ast.OpKind.USub:
pos = _PyiEmit(buf, buf_size, pos, "-")
elif uop.op == ast.OpKind.UAdd:
pos = _PyiEmit(buf, buf_size, pos, "+")
elif uop.op == ast.OpKind.Invert:
pos = _PyiEmit(buf, buf_size, pos, "~")
else:
return pos
if uop.operand is not None:
return _PyiDefaultStr(buf, buf_size, pos, uop.operand, depth + 1)
return pos
# 其它(BinOp 等复杂表达式):不输出,保持无默认值(fail-fast 由调用侧兜底)
return pos
# ============================================================
# _PyiFunction - 生成函数签名
#
@@ -357,7 +442,7 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# 装饰器
if fn.decorator_list is not None:
fn_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = fn.decorator_list
dn: t.CSizeT = fn_decorator_list.__len__()
dn: t.CSizeT = len(fn_decorator_list)
di: t.CSizeT = 0
while di < dn:
dec: ast.AST | t.CPtr = fn_decorator_list.get(di)
@@ -383,7 +468,17 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# 位置参数
if args.args is not None:
args_args: list[ast.AST | t.CPtr] | t.CPtr = args.args
an: t.CSizeT = args_args.__len__()
an: t.CSizeT = len(args_args)
# 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。
# vpi stub 必须输出 '= <default>',否则 VpiReader 解析后默认参数
# 表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。
# 注意:arg_defaults 必须内联声明(list[ast.AST|CPtr] | t.CPtr 联合
# 接收者的 .get 会被 TPC/TPV 误分派——预声明 + 重赋值会解析成
# list[int].get 返回 i32 截断指针崩溃;内联声明走 list[ast.ASTptr].get)。
arg_defaults: list[ast.AST | t.CPtr] | t.CPtr = args.defaults
dn: t.CSizeT = 0
if arg_defaults is not None:
dn = len(arg_defaults)
ai: t.CSizeT = 0
while ai < an:
if first == 0:
@@ -395,6 +490,12 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
if ag.annotation is not None:
pos = _PyiEmit(buf, buf_size, pos, ": ")
pos = _PyiTypeStr(buf, buf_size, pos, ag.annotation)
if arg_defaults is not None and an - dn <= ai:
di: t.CSizeT = ai - (an - dn)
dnode: ast.AST | t.CPtr = arg_defaults.get(di)
if dnode is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiDefaultStr(buf, buf_size, pos, dnode)
ai += 1
# *args
@@ -442,7 +543,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# 装饰器
if cls.decorator_list is not None:
cls_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = cls.decorator_list
dcn: t.CSizeT = cls_decorator_list.__len__()
dcn: t.CSizeT = len(cls_decorator_list)
dci: t.CSizeT = 0
while dci < dcn:
ddec: ast.AST | t.CPtr = cls_decorator_list.get(dci)
@@ -461,7 +562,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# PEP 695 类型参数 (class Foo[T]:)
if cls.type_params is not None:
cls_type_params: list[ast.AST | t.CPtr] | t.CPtr = cls.type_params
tpn: t.CSizeT = cls_type_params.__len__()
tpn: t.CSizeT = len(cls_type_params)
if tpn > 0:
pos = _PyiEmit(buf, buf_size, pos, "[")
tpi: t.CSizeT = 0
@@ -477,7 +578,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# 基类
if cls.bases is not None:
cls_bases: list[ast.AST | t.CPtr] | t.CPtr = cls.bases
bn: t.CSizeT = cls_bases.__len__()
bn: t.CSizeT = len(cls_bases)
if bn > 0:
pos = _PyiEmit(buf, buf_size, pos, "(")
bi: t.CSizeT = 0
@@ -496,7 +597,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
if cls.children is not None:
cls_children: list[ast.AST | t.CPtr] | t.CPtr = cls.children
cn: t.CSizeT = cls_children.__len__()
cn: t.CSizeT = len(cls_children)
ci: t.CSizeT = 0
while ci < cn:
stmt: ast.AST | t.CPtr = cls_children.get(ci)
@@ -510,7 +611,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0:
if mfn.children is not None:
mfn_children: list[ast.AST | t.CPtr] | t.CPtr = mfn.children
icn: t.CSizeT = mfn_children.__len__()
icn: t.CSizeT = len(mfn_children)
ici: t.CSizeT = 0
while ici < icn:
ibody: ast.AST | t.CPtr = mfn_children.get(ici)
@@ -595,17 +696,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
# = 1<<0 = 1、MASK = 0-1 = -1 → 跨模块读回错误常量 → 索引越界崩溃根因)。
# 必须 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时创建局部
# 变量(_lookup_cdefine_for_pyi 读到的仍是模块级 None → 缓存永不命中,
# 追加58 首版失效根因)。
global _g_pyi_cdef_names
global _g_pyi_cdef_vals
global _g_pyi_cdef_count
global _g_pyi_cdef_names, _g_pyi_cdef_vals, _g_pyi_cdef_count
if tree.children is not None:
pre_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children
pcn: t.CSizeT = pre_children.__len__()
pcn: t.CSizeT = len(pre_children)
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
_g_pyi_cdef_count = 0
pci0: t.CSizeT = 0
while pci0 < pcn:
for pci0 in range(pcn):
stmt_pc0: ast.AST | t.CPtr = pre_children.get(pci0)
if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign:
aa_pc0: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc0)
@@ -616,14 +713,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
ann_pc0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(aa_pc0.annotation)
if ann_pc0.attr is not None and string.strcmp(ann_pc0.attr, "CDefine") == 0:
_g_pyi_cdef_count += 1
pci0 += 1
if _g_pyi_cdef_count > 0:
_g_pyi_cdef_names = (str | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8))
_g_pyi_cdef_vals = (t.CInt64T | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8))
# 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项)
pci: t.CSizeT = 0
cdi: t.CInt = 0
while pci < pcn:
for pci in range(pcn):
stmt_pc: ast.AST | t.CPtr = pre_children.get(pci)
if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign:
aa_pc: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc)
@@ -638,7 +734,6 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
slot_pc[0] = tgt_pc.id
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
cdi += 1
pci += 1
# 文件头
pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n")
@@ -649,29 +744,25 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
return pos
tree_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children
n: t.CSizeT = tree_children.__len__()
n: t.CSizeT = len(tree_children)
# 第一遍扫描:检测 import t/c
has_import_t: int = 0
has_import_c: int = 0
i: t.CSizeT = 0
while i < n:
for i in range(n):
stmt: ast.AST | t.CPtr = tree_children.get(i)
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(stmt)
if imp.names is not None:
imp_names: list[ast.AST | t.CPtr] | t.CPtr = imp.names
an: t.CSizeT = imp_names.__len__()
ai: t.CSizeT = 0
while ai < an:
an: t.CSizeT = len(imp_names)
for ai in range(an):
al: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp_names.get(ai))
if al is not None and al.name is not None:
if string.strcmp(al.name, "t") == 0:
has_import_t = 1
elif string.strcmp(al.name, "c") == 0:
has_import_c = 1
ai += 1
i += 1
# 补充缺失的 import t/c
if has_import_t == 0 and has_import_c == 0:
@@ -684,8 +775,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
pos = _PyiEmit(buf, buf_size, pos, "\n")
# 第二遍扫描:生成声明
i = 0
while i < n:
for i in range(n):
stmt = tree_children.get(i)
if stmt is not None:
k: int = stmt.kind()
@@ -696,7 +786,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
pos = _PyiEmit(buf, buf_size, pos, "import ")
if imp2.names is not None:
imp2_names: list[ast.AST | t.CPtr] | t.CPtr = imp2.names
an2: t.CSizeT = imp2_names.__len__()
an2: t.CSizeT = len(imp2_names)
ai2: t.CSizeT = 0
while ai2 < an2:
if ai2 > 0:
@@ -723,7 +813,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
pos = _PyiEmit(buf, buf_size, pos, " import ")
if ifr.names is not None:
ifr_names: list[ast.AST | t.CPtr] | t.CPtr = ifr.names
an3: t.CSizeT = ifr_names.__len__()
an3: t.CSizeT = len(ifr_names)
ai3: t.CSizeT = 0
while ai3 < an3:
if ai3 > 0:
@@ -822,7 +912,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
call_cv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aa.value)
if call_cv.args is not None:
call_args_cv: list[ast.AST | t.CPtr] | t.CPtr = call_cv.args
if call_args_cv.__len__() > 0:
if len(call_args_cv) > 0:
first_arg: ast.AST | t.CPtr = call_args_cv.get(0)
if first_arg is not None:
arg_kind: int = first_arg.kind()
@@ -874,7 +964,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
asg: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(stmt)
if asg.targets is not None and asg.value is not None:
asg_targets: list[ast.AST | t.CPtr] | t.CPtr = asg.targets
atn: t.CSizeT = asg_targets.__len__()
atn: t.CSizeT = len(asg_targets)
if atn == 1:
atg: ast.AST | t.CPtr = asg_targets.get(0)
if atg is not None and atg.kind() == ast.ASTKind.Name:
@@ -909,8 +999,6 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
pos = _PyiEmit(buf, buf_size, pos, "None")
pos = _PyiEmit(buf, buf_size, pos, "\n")
i += 1
# NUL 终止
if pos < buf_size:
buf[pos] = 0
+433 -66
View File
@@ -25,6 +25,244 @@ import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger as StubMerger
# ============================================================
# 已见符号表(BuildCombinedIR 去重 O(n²)→O(1))
#
# Phase B 对每个源文件调用 BuildCombinedIR 组合 IR:原实现对每行
# declare/global/type 都在不断增长的 out_buf 全量扫描去重
# (_IsFuncDeclaredOrDefined 逐字节 strncmp、strstr 等),依赖最多时
# (CallGenCall)单文件 merge 达 12s。改用定长槽位开放寻址哈希表
# 记录 out_buf 中已活跃的行:去重查找 O(1)、注释行 O(1) 定位。
#
# slot 布局(步长 _SEEN_SLOT):
# {u64 hash} {u64 pos} {u8 flags} {name[_SEEN_NAME_MAX]} {u8 nul}
# 空槽判定:hash==0(memset 全零初始化;FNV-1a 保证 >=2)。
# flags:类型表 bit0=1 表示活跃行为完整定义(= type { / = type <),
# bit0=0 表示仅 opaque 声明;函数/全局表恒为 0。
# pos:活跃行在 out_buf 中的起始位置(注释操作直接 out_buf[pos]=';')。
# 注释行不从表中删除:注释后必然立即追加新行(更新 pos),表保持
# "有活跃行"不变量。超长名(>= _SEEN_NAME_MAX)不登记,防御性回退。
# 表满(count >= cap)fail-fast 报错:静默丢弃登记会致去重失效,
# 重复行追加 → llc redefinition(88410e59e3e5f98b .str 常量根因,
# 该模块合并 IR 实测 4486 个全局行 > 旧容量 4096,故全局表设 16384)。
# ============================================================
_SEEN_NAME_MAX: t.CDefine = 256
_SEEN_SLOT: t.CDefine = 274 # 8(hash)+8(pos)+1(flags)+256(name)+1(nul)
_SEEN_FUNC_CAP: t.CDefine = 16384
_SEEN_GLOBAL_CAP: t.CDefine = 16384
_SEEN_TYPE_CAP: t.CDefine = 4096
_SEEN_FNV_OFFSET: t.CUInt64T = 0xcbf29ce484222325
_SEEN_FNV_PRIME: t.CUInt64T = 0x100000001b3
_seen_funcs: bytes # 已见函数签名表(@"sha1.name"( 等)
_seen_funcs_count: int
_seen_globals: bytes # 已见全局变量名表(@name)
_seen_globals_count: int
_seen_types: bytes # 已见类型名表(%name)
_seen_types_count: int
_seen_key_scratch: bytes # 注册键暂存(跨 truncation 窗口复制)
def _SeenHash(key: str) -> t.CUInt64T:
"""FNV-1a 64 位哈希(对齐 includes/hashtable.vp)"""
h: t.CUInt64T = _SEEN_FNV_OFFSET
p: str = key
while p[0] != 0:
h = h ^ t.CUInt64T(p[0])
h = h * _SEEN_FNV_PRIME
p += 1
if h < 2:
h = 2
return h
def _SeenFind(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT:
"""已见表槽位查找:命中返回槽位下标;未命中返回 cap(哨兵)"""
if tbl is None or key is None:
return cap
mask: t.CSizeT = cap - 1
idx: t.CSizeT = t.CSizeT(key_hash) & mask
i: t.CSizeT = 0
while i < cap:
off: t.CSizeT = idx * _SEEN_SLOT
hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off)
sh: t.CUInt64T = hp[0]
if sh == 0:
return cap
if sh == key_hash:
nm: str = tbl + off + 17
if string.strcmp(nm, key) == 0:
return idx
idx = (idx + 1) & mask
i += 1
return cap
def _SeenPresent(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> int:
"""已见表查找:返回 0=不存在;>0=存在(返回值 = flags+1)"""
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
if si >= cap:
return 0
return t.CInt(tbl[si * _SEEN_SLOT + 16]) + 1
def _SeenInsert(tbl: bytes, cap: t.CSizeT, count: int, key: str, key_hash: t.CUInt64T,
flags: int, pos: t.CSizeT) -> int:
"""已见表插入/更新:key 不存在则新登记(返回 count+1);已存在则 OR flags、更新 pos"""
if tbl is None or key is None:
return count
kl: t.CSizeT = string.strlen(key)
if kl >= _SEEN_NAME_MAX:
return count
if count >= cap:
fb_full: str = VLogger.fmt_buf()
if fb_full is not None:
viperlib.snprintf(fb_full, 1024, "已见符号表满(容量 %d),新增符号登记被丢弃——去重失效,必须增大容量", cap)
VLogger.error(fb_full, "StubMerger")
return count
mask: t.CSizeT = cap - 1
idx: t.CSizeT = t.CSizeT(key_hash) & mask
i: t.CSizeT = 0
while i < cap:
off: t.CSizeT = idx * _SEEN_SLOT
hp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off)
sh: t.CUInt64T = hp[0]
if sh == 0:
hp[0] = key_hash
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8)
pp[0] = t.CUInt64T(pos)
tbl[off + 16] = t.CChar(flags)
string.strcpy(tbl + off + 17, key)
return count + 1
if sh == key_hash:
nm: str = tbl + off + 17
if string.strcmp(nm, key) == 0:
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + off + 8)
pp[0] = t.CUInt64T(pos)
tbl[off + 16] = t.CChar(t.CInt(tbl[off + 16]) | flags)
return count
idx = (idx + 1) & mask
i += 1
return count
def _SeenCommentOut(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T,
out_buf: bytes) -> int:
"""注释 out_buf 中该符号的活跃行(行首改 ';'),返回 1=已注释 0=无"""
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
if si >= cap:
return 0
if t.CInt(tbl[si * _SEEN_SLOT + 16]) != 0:
return 0 # 类型表 flags=1:活跃行为完整定义,无可注释的 opaque
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8)
p: t.CUInt64T = pp[0]
if out_buf is not None:
out_buf[t.CSizeT(p)] = ';'
return 1
def _SeenGetPos(tbl: bytes, cap: t.CSizeT, key: str, key_hash: t.CUInt64T) -> t.CSizeT:
"""读取已见表条目记录的活跃行位置;不存在返回 cap(哨兵)"""
si: t.CSizeT = _SeenFind(tbl, cap, key, key_hash)
if si >= cap:
return cap
pp: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(tbl) + si * _SEEN_SLOT + 8)
return t.CSizeT(pp[0])
def _RegisterStubLines(buf: bytes, buf_len: t.CSizeT, base_pos: t.CSizeT):
"""把本地 stub(整体复制进 out_buf)中的 declare/global/type 全部登记到已见表
本地 stub.ll 在 BuildCombinedIR 第 1 步整体 strcpy 进 out_buf,不做逐行
去重处理,若不同步登记,后续依赖的同名行查表未命中会重复追加 →
llc "redefinition of type/global"(7871cc1a... ll:758 根因)。
行位置 = base_pos + 行内偏移(复制是 1:1 保序),供注释操作定位。
同名重复行(如 61d41cf89...stub.ll 的 _g_gen 出现两次)在复制前将
行首改为 ';'(注释掉),表只指向首个活跃行(61d41cf89... ll:9853 根因);
若先 opaque 后完整定义,则注释旧行、表升级指向完整定义行。
"""
global _seen_funcs_count, _seen_globals_count, _seen_types_count
if buf is None:
return
pos: t.CSizeT = 0
while pos < buf_len:
ls: t.CSizeT = pos
while pos < buf_len:
if buf[pos] == '\n':
break
pos += 1
ll: t.CSizeT = pos - ls
if pos < buf_len:
pos += 1
line_pos: t.CSizeT = base_pos + ls
# declare → 函数签名 @"name"(
if ll >= 8 and string.strncmp(buf + ls, "declare ", 8) == 0:
at: t.CSizeT = ls + 8
while at < ls + ll:
if buf[at] == '@':
break
at += 1
if at < ls + ll:
lp: t.CSizeT = at + 1
while lp < ls + ll:
if buf[lp] == '(':
break
lp += 1
if lp < ls + ll:
sv_f: t.CChar = buf[lp + 1]
buf[lp + 1] = '\0'
kh_f: t.CUInt64T = _SeenHash(buf + at)
if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, buf + at, kh_f) == 0:
_seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, buf + at, kh_f, 0, line_pos)
else:
buf[ls] = ';'
buf[lp + 1] = sv_f
continue
# 全局 @name =(键 @name,截断于 '=' 前)
if ll > 0 and buf[ls] == '@':
# 字符串常量 @.str.{sha1}.{N}:本地 stub 的 external 声明是死代码
# (本地 text.ll 有 internal constant 定义),注释掉避免与定义重复
if ll >= 5 and buf[ls + 1] == '.' and buf[ls + 2] == 's' and buf[ls + 3] == 't' and buf[ls + 4] == 'r':
buf[ls] = ';'
continue
eq: t.CSizeT = ls + 1
while eq < ls + ll:
if buf[eq] == '=':
break
eq += 1
if eq < ls + ll:
sv_g: t.CChar = buf[eq]
buf[eq] = '\0'
kh_g: t.CUInt64T = _SeenHash(buf + ls)
if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, buf + ls, kh_g) == 0:
_seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, buf + ls, kh_g, 0, line_pos)
else:
buf[ls] = ';'
buf[eq] = sv_g
continue
# 类型 %name = type ...(完整定义 flags=1,opaque flags=0)
if ll > 0 and buf[ls] == '%':
eq: t.CSizeT = ls + 1
while eq < ls + ll:
if buf[eq] == '=':
break
eq += 1
if eq < ls + ll:
sv_t: t.CChar = buf[eq]
buf[eq] = '\0'
kh_t: t.CUInt64T = _SeenHash(buf + ls)
is_full: int = 0
if eq + 8 <= ls + ll:
if buf[eq + 2] == 't' and buf[eq + 3] == 'y' and buf[eq + 4] == 'p' and buf[eq + 5] == 'e' and buf[eq + 6] == ' ':
if buf[eq + 7] == '{' or buf[eq + 7] == '<':
is_full = 1
prev_t: int = _SeenPresent(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t)
if prev_t == 0:
_seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, is_full, line_pos)
elif is_full == 1 and prev_t == 1:
# 先 opaque 后完整定义:注释旧行,表升级指向新完整定义行
old_pos: t.CSizeT = _SeenGetPos(_seen_types, _SEEN_TYPE_CAP, buf + ls, kh_t)
if old_pos >= base_pos and old_pos < base_pos + buf_len:
buf[old_pos - base_pos] = ';'
_seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, buf + ls, kh_t, 1, line_pos)
else:
buf[ls] = ';'
buf[eq] = sv_t
continue
# ============================================================
# StubMergerBuild - StubMerger 拆分(阶段 E):StubMergerBuild
#
@@ -43,6 +281,7 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
out_pos: t.CSizeT) -> t.CSizeT:
"""加载依赖 stub 并追加(跳过 header,去重)"""
global _seen_funcs_count, _seen_globals_count, _seen_types_count
if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None:
return out_pos
@@ -91,7 +330,7 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
dep_buf[ls] = ';'
cfs_pos = ext_off2 + 30
# 跳过 header,追加内容(去重 declare/global)
# 跳过 header,追加内容(去重 declare/global/type,已见符号表 O(1) 去重)
dep_pos: t.CSizeT = 0
skipping: int = 1
while dep_pos < dep_br_ls:
@@ -116,7 +355,12 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0:
continue
skipping = 0
# 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句)
# 注册暂存(append 成功后才登记;reg_key 指向 scratch,insert 当轮完成)
reg_tbl: int = 0
reg_key: str = None
reg_hash: t.CUInt64T = 0
reg_flags: int = 0
# 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句)
if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0:
at: t.CSizeT = line_start + 8
while at < line_start + line_len:
@@ -132,24 +376,39 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
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:
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
# 去重:external global(键 @name,截断于 '=' 前)
if line_len > 0 and dep_buf[line_start] == '@':
# 字符串常量 @.str.{sha1}.{N}:依赖 stub 的 external 声明是死代码
# (定义在模块自身 text.ll,合并 IR 中依赖方无需声明),跳过
if line_len >= 5 and dep_buf[line_start + 1] == '.' and dep_buf[line_start + 2] == 's' \
and dep_buf[line_start + 3] == 't' and dep_buf[line_start + 4] == 'r':
continue
eq: t.CSizeT = line_start + 1
while eq < line_start + line_len:
if dep_buf[eq] == '=':
break
eq += 1
if eq < line_start + line_len:
saved: t.CChar = dep_buf[eq + 1]
dep_buf[eq + 1] = '\0'
if string.strstr(out_buf, dep_buf + line_start) is not None:
dep_buf[eq + 1] = saved
saved: t.CChar = dep_buf[eq]
dep_buf[eq] = '\0'
reg_hash = _SeenHash(dep_buf + line_start)
if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + line_start, reg_hash) != 0:
dep_buf[eq] = saved
continue
dep_buf[eq + 1] = saved
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 行,
@@ -169,34 +428,48 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ':
if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<':
dep_is_full_def = 1
saved_eq: t.CChar = dep_buf[eq2]
dep_buf[eq2] = '\0'
reg_hash = _SeenHash(dep_buf + line_start)
if dep_is_full_def == 1:
# 依赖行是完整定义
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
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:
_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 声明,用原有子串匹配去重
found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start)
if found_opaque is not None:
# opaque 声明:已存在同名(任意形态)则跳过
if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, dep_buf + line_start, reg_hash) != 0:
dep_buf[eq2] = saved_eq
dep_buf[eq2 + 1] = saved2
continue
string.strcpy(_seen_key_scratch, dep_buf + line_start)
reg_tbl = 3
reg_key = _seen_key_scratch
reg_flags = 0
dep_buf[eq2] = saved_eq
dep_buf[eq2 + 1] = saved2
# 追加
# 追加(append 成功后才登记已见表)
if out_pos + line_len + 2 < out_size:
reg_pos: t.CSizeT = out_pos
string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len)
out_pos += line_len
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
if reg_tbl == 1:
_seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, reg_key, reg_hash, reg_flags, reg_pos)
elif reg_tbl == 2:
_seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, reg_key, reg_hash, reg_flags, reg_pos)
elif reg_tbl == 3:
_seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, reg_key, reg_hash, reg_flags, reg_pos)
return out_pos
# ============================================================
# _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf
@@ -208,6 +481,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
out_pos: t.CSizeT) -> t.CSizeT:
"""从依赖 text.ll 提取 declare(define 转 declare),追加到 out_buf"""
global _seen_funcs_count, _seen_globals_count, _seen_types_count
if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None:
return out_pos
@@ -285,13 +559,15 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
if lp_lt >= ls + ll:
in_body = 1
continue
# 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句
# 去重检查:用已见函数表(键 @"name"(,避免误匹配 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:
fh_lt: t.CUInt64T = _SeenHash(dep_buf + at_lt)
if _SeenPresent(_seen_funcs, _SEEN_FUNC_CAP, dep_buf + at_lt, fh_lt) != 0:
dep_buf[lp_lt + 1] = saved_lt
in_body = 1
continue
string.strcpy(_seen_key_scratch, dep_buf + at_lt)
dep_buf[lp_lt + 1] = saved_lt
# 找行尾的 ' {' 并截断
@@ -337,6 +613,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
skipped_lt = 1
copy_len_lt: t.CSizeT = decl_end - c_start_lt
if out_pos + 8 + copy_len_lt + 2 < out_size:
decl_pos_lt: t.CSizeT = out_pos
string.strncpy(out_buf + out_pos, "declare ", 8)
out_pos += 8
string.strncpy(out_buf + out_pos, dep_buf + c_start_lt, copy_len_lt)
@@ -344,6 +621,7 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
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
@@ -366,22 +644,27 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<':
dep_is_full_td = 1
if dep_is_full_td == 1:
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
# 临时截断到 '=' 之前(不含 '='),键统一为 %name
saved_eq_td: t.CChar = dep_buf[eq_td]
dep_buf[eq_td] = '\0'
th_td: t.CUInt64T = _SeenHash(dep_buf + ls)
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full_td: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + ls)
_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 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'
# 追加完整类型定义
if out_pos + ll + 2 < out_size:
td_pos: t.CSizeT = out_pos
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
_seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, _seen_key_scratch, th_td, 1, td_pos)
continue
# 提取全局变量定义(@... = ...)
@@ -389,31 +672,38 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
# stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义
# 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义
if ll > 0 and dep_buf[ls] == '@':
# 字符串常量 @.str.{sha1}.{N}:依赖 text.ll 中的 internal 定义是死代码
# (定义模块自身 .obj 已有,合并 IR 中依赖方无需重复),跳过
if ll >= 5 and dep_buf[ls + 1] == '.' and dep_buf[ls + 2] == 's' and dep_buf[ls + 3] == 't' \
and dep_buf[ls + 4] == 'r':
continue
eq_gv: t.CSizeT = ls + 1
while eq_gv < ls + ll:
if dep_buf[eq_gv] == '=':
break
eq_gv += 1
if eq_gv < ls + ll:
# 截断到 '=' 之前(不含 '='),得到全局变量名 @name
# 截断到 '=' 之前(不含 '='),键统一为 @name
saved_gv: t.CChar = dep_buf[eq_gv]
dep_buf[eq_gv] = '\0'
gh_gv: t.CUInt64T = _SeenHash(dep_buf + ls)
# 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义)
# 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份
# 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致
# ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。
# 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。
if StubMergerIR._HasGlobalInBuf(out_buf, dep_buf + ls) == 0:
# 注释掉 out_buf 中的同名行(external 声明或旧定义)
StubMergerIR._CommentOutGlobalInBuf(out_buf, dep_buf + ls)
if _SeenPresent(_seen_globals, _SEEN_GLOBAL_CAP, dep_buf + ls, gh_gv) == 0:
# 追加完整定义(无同名行时注释旧行是 no-op)
string.strcpy(_seen_key_scratch, dep_buf + ls)
dep_buf[eq_gv] = saved_gv
# 追加完整定义
if out_pos + ll + 2 < out_size:
gv_pos: t.CSizeT = out_pos
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
_seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, _seen_key_scratch, gh_gv, 0, gv_pos)
else:
dep_buf[eq_gv] = saved_gv
continue
@@ -731,19 +1021,12 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
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
# 仅识别 .vp 源文件(不再做 vp -> py 后缀识别回退)
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf)
af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R)
if not af.closed:
is_app_src = 1
af.close()
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 未正确填充
@@ -778,6 +1061,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
def BuildCombinedIR(temp_dir: str, local_sha1: str,
out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
"""组合本地 stub + 依赖 stubs + 本地 text → out_buf(按需加载)"""
global _seen_funcs, _seen_funcs_count, _seen_globals, _seen_globals_count
global _seen_types, _seen_types_count, _seen_key_scratch
if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0:
return 0
@@ -785,6 +1070,34 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
out_buf[0] = '\0'
out_pos: t.CSizeT = 0
# 已见符号表重置(跨构建复用缓冲,仅清零计数 + 全槽位清零)
if _seen_funcs is None:
_seen_funcs = stdlib.malloc(_SEEN_FUNC_CAP * _SEEN_SLOT)
if _seen_funcs is None:
VLogger.error("已见函数表分配失败", "StubMerger")
return 0
if _seen_globals is None:
_seen_globals = stdlib.malloc(_SEEN_GLOBAL_CAP * _SEEN_SLOT)
if _seen_globals is None:
VLogger.error("已见全局表分配失败", "StubMerger")
return 0
if _seen_types is None:
_seen_types = stdlib.malloc(_SEEN_TYPE_CAP * _SEEN_SLOT)
if _seen_types is None:
VLogger.error("已见类型表分配失败", "StubMerger")
return 0
if _seen_key_scratch is None:
_seen_key_scratch = stdlib.malloc(_SEEN_NAME_MAX + 1)
if _seen_key_scratch is None:
VLogger.error("已见表键暂存分配失败", "StubMerger")
return 0
string.memset(_seen_funcs, 0, _SEEN_FUNC_CAP * _SEEN_SLOT)
string.memset(_seen_globals, 0, _SEEN_GLOBAL_CAP * _SEEN_SLOT)
string.memset(_seen_types, 0, _SEEN_TYPE_CAP * _SEEN_SLOT)
_seen_funcs_count = 0
_seen_globals_count = 0
_seen_types_count = 0
# 1. 读取并追加本地 stub.ll(含 header)
stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "stub.ll")
if stub_path is None:
@@ -830,8 +1143,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
stub_content[ext_off + 6] = ' '
stub_content[ext_off + 7] = ' '
fix_pos = ext_off + 8
# 直接复制修复后的 stub 内容
# 直接复制修复后的 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':
@@ -944,6 +1259,19 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
# 检查 dep_mod_name 是否是已导入包的子模块
# (如 ast.lexer 是 ast 的子模块)
should_load = 1
else:
# 基名匹配:store 键是全路径模块名
# (Kernel.drivers.fs.fat32.fat32_types),而 deps.txt
# 记录 import 短名(fat32_types)→ 精确/子模块均不命中
# → 依赖 stub/text 不加载 → 跨模块类完整定义缺失
# (vpi 注入失败的 fat32_types.dirobj 只有 opaque)→
# 值类型 alloca 报 llc "Cannot allocate unsized type"
# (VKernel example_fs L500 根因)。取最后一个 '.' 之后
# 的基名与 deps 词边界匹配(多加载无害,去重表兜底)。
last_dot_bm: str = string.strrchr(dep_mod_name, '.')
if last_dot_bm is not None and last_dot_bm[1] != '\0':
if GenApi.is_module_imported(deps_buf, last_dot_bm + 1) != 0:
should_load = 1
break
if is_include == 0:
# 非 includes stub(用户文件),总是加载
@@ -1057,7 +1385,12 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0:
continue
tx_skip = 0
# 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句)
# 注册暂存(append 成功后才登记;tx_key 指向 scratch,insert 当轮完成)
tx_reg: int = 0
tx_key: str = None
tx_hash: t.CUInt64T = 0
tx_flags: int = 0
# 去重:declare(用已见函数表,键 @"name"(,避免误匹配 call 语句)
if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0:
tx_at: t.CSizeT = tx_ls + 8
while tx_at < tx_ls + tx_ll:
@@ -1073,11 +1406,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
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:
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 冲突)
# 去重: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:
@@ -1093,11 +1431,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
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)
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 中的定义为准
# 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的
# 注释 out_buf 中同名全局行,然后追加 text 中的定义(键 @name,截断于 '=' 前)
if tx_ll > 0 and text_content[tx_ls] == '@':
tx_eq: t.CSizeT = tx_ls + 1
while tx_eq < tx_ls + tx_ll:
@@ -1105,11 +1448,16 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
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 = ...
tx_sv2: t.CChar = text_content[tx_eq]
text_content[tx_eq] = '\0'
tx_hash = _SeenHash(text_content + tx_ls)
_SeenCommentOut(_seen_globals, _SEEN_GLOBAL_CAP, text_content + tx_ls, tx_hash, out_buf)
string.strcpy(_seen_key_scratch, text_content + tx_ls)
tx_reg = 2
tx_key = _seen_key_scratch
tx_flags = 0
text_content[tx_eq] = tx_sv2
# 去重:%type = ...(键 %name,截断于 '=' 前)
if tx_ll > 0 and text_content[tx_ls] == '%':
tx_eq2: t.CSizeT = tx_ls + 1
while tx_eq2 < tx_ls + tx_ll:
@@ -1117,19 +1465,38 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
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
tx_sv3: t.CChar = text_content[tx_eq2]
text_content[tx_eq2] = '\0'
tx_is_full: int = 0
if tx_eq2 + 8 <= tx_ls + tx_ll:
if text_content[tx_eq2 + 2] == 't' and text_content[tx_eq2 + 3] == 'y' \
and text_content[tx_eq2 + 4] == 'p' and text_content[tx_eq2 + 5] == 'e' \
and text_content[tx_eq2 + 6] == ' ':
if text_content[tx_eq2 + 7] == '{' or text_content[tx_eq2 + 7] == '<':
tx_is_full = 1
tx_hash = _SeenHash(text_content + tx_ls)
if _SeenPresent(_seen_types, _SEEN_TYPE_CAP, text_content + tx_ls, tx_hash) != 0:
text_content[tx_eq2] = tx_sv3
continue
text_content[tx_eq2 + 1] = tx_sv3
# 追加
string.strcpy(_seen_key_scratch, text_content + tx_ls)
tx_reg = 3
tx_key = _seen_key_scratch
tx_flags = tx_is_full
text_content[tx_eq2] = tx_sv3
# 追加(append 成功后才登记已见表)
if out_pos + tx_ll + 2 < out_size:
tx_reg_pos: t.CSizeT = out_pos
string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll)
out_pos += tx_ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
if tx_reg == 1:
_seen_funcs_count = _SeenInsert(_seen_funcs, _SEEN_FUNC_CAP, _seen_funcs_count, tx_key, tx_hash, tx_flags, tx_reg_pos)
elif tx_reg == 2:
_seen_globals_count = _SeenInsert(_seen_globals, _SEEN_GLOBAL_CAP, _seen_globals_count, tx_key, tx_hash, tx_flags, tx_reg_pos)
elif tx_reg == 3:
_seen_types_count = _SeenInsert(_seen_types, _SEEN_TYPE_CAP, _seen_types_count, tx_key, tx_hash, tx_flags, tx_reg_pos)
stdlib.free(text_content)
if deps_buf is not None:
+3 -8
View File
@@ -19,19 +19,15 @@ 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 - StubMerger 拆分(阶段 E):StubMergerIR
#
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
# StubMerger._xxx 访问(main.vp 先例)。
# ============================================================
# ============================================================
@@ -52,7 +48,7 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll")
if tpath is None:
return -1
tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R)
tf_d: fileio.File = fileio.File(tpath, fileio.MODE.R)
stdlib.free(tpath)
if tf_d.closed:
return -1
@@ -110,7 +106,7 @@ def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + sig_len <= out_len:
for pos in range(0, out_len - sig_len + 1):
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: t.CSizeT = pos
while ls > 0 and out_buf[ls - 1] != '\n':
@@ -119,7 +115,6 @@ def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
return 1
if string.strncmp(out_buf + ls, "define ", 7) == 0:
return 1
pos += 1
return 0
# ============================================================
# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉
+25 -3
View File
@@ -72,6 +72,12 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
if rel_idx is not None:
stdlib.free(rel_idx)
return 0
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
zi: int = 0
for zi in range(store_count + 1):
indegree[zi] = 0
queue[zi] = 0
rel_idx[zi] = 0
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
rel_count: int = 0
i_t: int
@@ -84,6 +90,11 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0:
rel_idx[rel_count] = i_t
rel_count += 1
# 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行)
fb_rel: str = VLogger.fmt_buf()
if fb_rel is not None:
viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count)
VLogger.debug(fb_rel, "StubMergerTopo")
# 计算入度:m 在可达集合内的直接依赖数
i_r: int
for i_r in range(rel_count):
@@ -110,7 +121,10 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
stdlib.free(dname_buf)
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
indegree[si] = indegree[si] + 1
# 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug
indg: int = indegree[si]
indg = indg + 1
indegree[si] = indg
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
q_tail: int = 0
for i_r in range(rel_count):
@@ -119,7 +133,11 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
queue[q_tail] = si2
q_tail += 1
out_count: int = 0
for q_head in range(q_tail):
# Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队
# (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次,
# 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项
q_head: int = 0
while q_head < q_tail:
m: int = queue[q_head]
sorted_idx[out_count] = m
out_count += 1
@@ -148,10 +166,14 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0:
found_dep = 1
if found_dep == 1:
indegree[si3] = indegree[si3] - 1
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
indg3: int = indegree[si3]
indg3 = indg3 - 1
indegree[si3] = indg3
if indegree[si3] == 0:
queue[q_tail] = si3
q_tail += 1
q_head += 1
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
for i_r in range(rel_count):
si4: int = rel_idx[i_r]
+6 -7
View File
@@ -203,7 +203,7 @@ class AssignGenMixin:
cl_cd: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node)
if cl_cd is not None and cl_cd.args is not None:
cl_args_cd: list[ast.AST | t.CPtr] | t.CPtr = cl_cd.args
if cl_args_cd.__len__() > 0:
if len(cl_args_cd) > 0:
first_arg_cd: ast.AST | t.CPtr = cl_args_cd.get(0)
if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant:
val_node = first_arg_cd
@@ -946,8 +946,7 @@ class AssignGenMixin:
支持两种书写形式:
1. 模块限定 t.CArray[...](Attribute value)
2. 裸名 CArray[...](from t import * 后使用,如 Termination4K3 core.vp
的 `__python_suffix__: CArray[str] = ["py", ...]`)
2. 裸名 CArray[...](from t import * 后使用) # 存疑
"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return 0
@@ -1076,7 +1075,7 @@ class AssignGenMixin:
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is None:
# 尝试特化泛型类注解(如 list[str])
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
@@ -1094,7 +1093,7 @@ class AssignGenMixin:
trans, sub_nm.id, type_args_ps)
if spec_name_ps is not None:
resolved = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
var_ty = resolved
# 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。
@@ -1189,7 +1188,7 @@ class AssignGenMixin:
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
var_ty = resolved
else:
@@ -1207,7 +1206,7 @@ class AssignGenMixin:
# t.Callable 函数指针:注册返回类型签名(供函数指针间接调用精确返回类型)
callable_ret: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenErr.try_extract_callable_ret(
pool, annot, trans._imported_modules, trans._from_imports, trans)
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if callable_ret is not None:
self.Gen.CallGenLeafKit.register_func_ptr_sig(nm.id, callable_ret)
+32 -8
View File
@@ -330,17 +330,41 @@ class CallGenBase:
def is_cexport_func(self, sha1: str, func_name: str) -> int:
"""检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)"""
"""检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)
注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp
先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc
误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined
symbol: 1fdad1c76fb116bc.malloc)。
"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is None:
if self.Gen.CallGenBase.CexportSha1Arr is not None:
for i in range(self.Gen.CallGenBase.CexportCount):
idx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
idx2: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0:
return 1
return self._is_cexport_func_from_vpi(sha1, func_name)
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
"""vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头)
对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。
"""
if sha1 is None or func_name is None:
return 0
for i in range(self.Gen.CallGenBase.CexportCount):
idx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
idx2: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0:
return 1
if self.Gen is None or self.Gen.CallGenBase.TempDir is None \
or self.Gen.CallGenBase.TempDirLen <= 0:
return 0
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if cexp_annot is None:
return 0
if string.strstr(cexp_annot, "CExport") is not None:
return 1
return 0
+401 -47
View File
@@ -133,7 +133,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
and trans is not None and trans.CurAssignAnnot is not None:
dref_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
trans.Pool, trans.CurAssignAnnot,
trans._imported_modules, trans._from_imports, trans)
trans._imported_modules, trans._from_imports, trans, 0)
if dref_ty is not None:
dref_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dref_ty)
dref_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(dref_ty)
@@ -161,12 +161,21 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store)"""
"""c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store)
单参形式 c.DerefAs(ptr) → 读取 ptr 指向的值(等价 c.Deref(ptr))。
"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 2:
if cargs is None or len(cargs) < 1:
return None
# 单参形式:deref 读(paging.vp save_pt_range L263
# saved = c.DerefAs(save_count)、restore_pt_range L277-278
# c.DerefAs(entry_ptr) 根因——此前 len<2 直接 return None →
# 跌落跨模块函数调用生成 call @DerefAs → ld.lld undefined symbol:
# DerefAs。复用 translate_c_deref 的类型推断读取逻辑。
if len(cargs) < 2:
return self.translate_c_deref(pool, builder, mod, cl, trans)
ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), None, 0, trans)
@@ -344,7 +353,69 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
pass
case _:
pass
# nonlocal 变量:提升函数内从 env 读对象指针(load_nonlocal_var),
# 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱崩溃)。
# 与普通局部 c.Addr(obj) 返回对象指针(load 一次)语义一致。
if trans is not None:
if HT.is_nonlocal_name(trans, nm.id) != 0:
nl_addr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.load_nonlocal_var(
trans, nm.id)
if nl_addr is not None:
return nl_addr
return_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if return_var is None and mod is not None:
# 模块全局变量:c.Addr(模块全局) → 返回全局变量指针。
# 此前局部符号表查找失败即返回 None → 跌落跨模块函数调用路径
# 生成 call @Addr(裸名,无 obj 定义)→ ld.lld undefined symbol:
# Addr(VKernel main.vp L87 c.Addr(ncpu_cr) 及 4d/8d/a1/c3/d3
# 大量模块全局取地址根因)。必须经 make_global_ref 转成带引号的
# Ptr(Ty) 引用——直接返回 GlobalVariable 节点时其 Ty 是 pointee、
# Name 无 @ 引号,参与 ptrtoint 会渲染成 "ptrtoint void to i64"
# (llc: void type only allowed for function results)。
gv_ref_nm: llvmlite.GlobalVariable | t.CPtr = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id)
if gv_ref_nm is not None and gv_ref_nm.Name is not None and gv_ref_nm.Ty is not None:
return_var = self.Gen.ExprGenAttr.make_global_ref(pool, gv_ref_nm.Name, gv_ref_nm.Ty)
# 模块函数地址:c.Addr(_on_int_complete) → 函数符号本身(@sha1.func)。
# 此前 Name 分支只查局部/模块全局变量,函数取地址失败 return None
# → 跌落跨模块函数调用生成 call @Addr → ld.lld undefined symbol:
# Addr(uhci.vp L130、coroutine.vp L133 c.Addr(_coro_entry)、
# sched.vp L209 c.Addr(_thread_entry) 根因)。
if return_var is None and mod is not None:
f_addr_ref: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(mod, nm.id)
if f_addr_ref is not None:
f_addr_name: str = llvmlite.function_get_name(f_addr_ref)
if f_addr_name is not None:
p_head_ca: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(f_addr_ref)
p_cnt_ca: t.CSizeT = 0
p_cur_ca: llvmlite.Param | t.CPtr = p_head_ca
while p_cur_ca is not None:
p_cnt_ca += 1
p_cur_ca = llvmlite.param_get_next(p_cur_ca)
fn_ty_ca: llvmlite.LLVMType | t.CPtr = llvmlite.Func(
pool, f_addr_ref.RetTy, p_head_ca, p_cnt_ca)
# 函数引用名需按需加引号(数字开头/含 '.' 的 sha1 前缀名
# 必须 @\"...\",否则 llc "expected 'to' after cast value",
# uhci.vp c.Addr(_on_int_complete) 根因)。
fnlen_ca: t.CSizeT = string.strlen(f_addr_name)
needs_q_ca: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(f_addr_name)
faddr_buf_ca: str = pool.alloc(fnlen_ca + 4)
if faddr_buf_ca is not None and fn_ty_ca is not None:
if needs_q_ca != 0:
viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@\"%s\"", f_addr_name)
else:
viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@%s", f_addr_name)
return_var = llvmlite.SSAValue(
pool, llvmlite.Ptr(pool, fn_ty_ca), faddr_buf_ca)
# 闭包变量:c.Addr(闭包名) → 返回闭包对象指针(load 一次)。
# 闭包 alloca 是 i8**(存 {fn_ptr, env_ptr} 结构地址);不 load 的话
# 会把 &闭包指针变量(i8**)当对象地址传,回调侧按闭包结构读
# fn_ptr 会读到指针变量的存储地址而非函数地址(与 Struct 对象
# 变量 c.Addr(obj) load 一次的语义一致)。
if return_var is not None and return_var.Ty is not None:
if self.Gen.CallGenLeafKit.is_closure_name(nm.id) != 0:
i8_ty_cb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_cb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_cb)
return llvmlite.build_load(builder, i8_ptr_ty_cb, return_var)
if return_var is not None and return_var.Ty is not None:
# 对象指针变量(如 lit_tree = zhuff.zhuff_tree(),alloca 是
# Ptr(Ptr(Struct)),存对象指针):c.Addr(对象变量) 应返回对象
@@ -377,52 +448,151 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
at_addr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg0)
if at_addr is None or at_addr.attr is None or at_addr.value is None:
return None
# 解析 self(Name)→ 查找 alloca
if at_addr.value.kind() != ast.ASTKind.Name:
return None
self_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value)
if self_nm.id is None:
return None
self_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, self_nm.id)
if self_alloca is None or self_alloca.Ty is None:
return None
# 从 self 类型提取结构体类型(Ptr(Struct) 或 Ptr(Ptr(Struct)))
# 跨模块函数地址:c.Addr(serial.puts) → 函数符号本身(@sha1.puts)。
# 此前 Attribute 分支只支持 self/cls 字段 GEP;serial.puts 的 value 是
# 已导入模块名(非局部变量)→ 下方 self_alloca 查找失败 return None →
# 回退 ExprGenAttr 当全局变量处理(创建 @sha1.puts external global i8*
# + load)→ 与 vpi 注入的 puts 函数 declare 同名冲突 → llc
# "redefinition of function '@a6890f1aeecb0aaf.puts'"
# (elf.vp L485 c.Addr(serial.puts) 根因)。
if at_addr.value.kind() == ast.ASTKind.Name and trans is not None \
and trans._imported_modules is not None:
mod_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value)
if mod_nm2.id is not None:
if self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, mod_nm2.id) != 0:
f_ref2: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(
mod, at_addr.attr)
if f_ref2 is not None:
fname2: str = llvmlite.function_get_name(f_ref2)
if fname2 is not None:
# 构造带 @ 名的 SSAValue(Ptr(FuncTy)):直接返回
# Function 节点会被 IRBuilder 格式化成 RetTy(void)
# → "bitcast void to i8*"(elf.vp L2874 根因)。
# Function 无 Ty 字段(成员仅 Next/Name/RetTy/
# Params/Blocks/IsDeclared/IsVarArg/Attrs),
# 用 RetTy+Params 重建函数类型。Params 是
# GSList(含 Head 字段),必须经
# function_get_param_head 取链表头——直接
# 把 Params 当 Param 遍历会读 GSList.Head/
# Next 当 Name/Ty → 垃圾参数类型
# "i32 (i1425633744, i8*)"(elf.vp L2874 根因)。
p_head2: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(f_ref2)
p_cnt2: t.CSizeT = 0
p_cur2: llvmlite.Param | t.CPtr = p_head2
while p_cur2 is not None:
p_cnt2 += 1
p_cur2 = llvmlite.param_get_next(p_cur2)
fn_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Func(
pool, f_ref2.RetTy, p_head2, p_cnt2)
# 函数引用名按需加引号(数字开头/含 '.' 的
# sha1 前缀名必须 @\"...\")。
fnlen2: t.CSizeT = string.strlen(fname2)
needs_q2: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(fname2)
faddr_buf2: str = pool.alloc(fnlen2 + 4)
if faddr_buf2 is not None and fn_ty2 is not None:
if needs_q2 != 0:
viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@\"%s\"", fname2)
else:
viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@%s", fname2)
return llvmlite.SSAValue(
pool, llvmlite.Ptr(pool, fn_ty2), faddr_buf2)
else:
# 模块限定全局:c.Addr(gdt.gp) → 全局符号本身(@gp)。
# 跨模块全局(含 CExport 裸名 @gp)已注入为 external
# global。经 make_global_ref 转成带引号的 Ptr(Ty) 引用
# (直接返回 GlobalVariable 节点会渲染 "ptrtoint void")。
# 此前未处理 → return None → 跌落跨模块函数调用路径
# 生成 call @Addr(裸名,无 obj 定义)→ ld.lld
# undefined symbol: Addr(apic.vp L193 c.Addr(gdt.gp)
# / L199 c.Addr(idt.idt) 根因)。
g_ref2: llvmlite.GlobalVariable | t.CPtr = self.Gen.ExprGenAttr.find_global_in_module(
mod, at_addr.attr)
if g_ref2 is not None and g_ref2.Name is not None and g_ref2.Ty is not None:
return self.Gen.ExprGenAttr.make_global_ref(pool, g_ref2.Name, g_ref2.Ty)
# 解析 base(Name 变量 / Subscript 数组元素)→ 对象指针
obj_ptr: llvmlite.Value | t.CPtr = None
struct_ty: llvmlite.LLVMType | t.CPtr = None
self_is_ptr_ptr: int = 0
match self_alloca.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
struct_ty = inner2
self_is_ptr_ptr = 1
case _:
struct_ty = inner1
case _:
if at_addr.value.kind() == ast.ASTKind.Subscript:
# c.Addr(s.threads[0].rsp):base 是数组元素(Subscript)→ 先取
# 元素指针(GEP 数组),再 GEP 到字段。此前仅支持 Name base,
# Subscript 落到下方 return None → 跌落跨模块函数调用生成
# call @Addr(sched.vp L90/L182/L188/L196、coroutine.vp
# L99/L106/L112/L120 根因)。
sub_base: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(at_addr.value)
if sub_base.value is None or sub_base.slice is None:
return None
bval: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_base.value, None, 0, trans)
bidx: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_base.slice, None, 0, trans)
if bval is None or bidx is None or bval.Ty is None:
return None
bpointee: llvmlite.LLVMType | t.CPtr = None
if self.Gen.ExprGenBase.is_ptr_type(bval.Ty) != 0:
bpointee = bval.Ty.Pointee
if bpointee is not None:
match bpointee:
case llvmlite.LLVMType.Array(bet, _):
obj_ptr = llvmlite.build_gep_array(
builder, bpointee, bet, bval, bidx)
struct_ty = bet
case _:
return None
else:
return None
elif at_addr.value.kind() == ast.ASTKind.Name:
self_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value)
if self_nm.id is None:
return None
self_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, self_nm.id)
if self_alloca is None or self_alloca.Ty is None:
return None
# 从 self 类型提取结构体类型(Ptr(Struct) 或 Ptr(Ptr(Struct)))
self_is_ptr_ptr: int = 0
match self_alloca.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
struct_ty = inner2
self_is_ptr_ptr = 1
case _:
struct_ty = inner1
case _:
return None
# self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针
obj_ptr = self_alloca
if self_is_ptr_ptr == 1:
self_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty)
obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca)
else:
return None
if obj_ptr is None or struct_ty is None:
return None
# 查找字段
fe_addr: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
struct_ty, at_addr.attr)
if fe_addr is None:
return None
# self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针
obj_ptr: llvmlite.Value | t.CPtr = self_alloca
if self_is_ptr_ptr == 1:
self_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty)
obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca)
if obj_ptr is None:
return None
# GEP 到字段地址
return llvmlite.build_gep_struct(
builder, struct_ty, fe_addr.Ty, obj_ptr, fe_addr.Index)
# Subscript 形式: c.Addr(arr[i]) → 数组元素地址(GEP 数组,不 load)
# 用于 self.root_directory = c.Addr(directories[directory_count]) 等。
# 此前只支持 Name/Attribute,Subscript 落到函数末尾返回 None →
# 赋值右侧为 None(devfs.vp:159 根因)。
# Subscript 形式: c.Addr(arr[i]) / c.Addr(a.b[i][j]) → 元素地址(GEP,不 load)
# 统一走 get_subscript_ptr(递归处理嵌套下标、数组/指针/str|t.CPtr 形态)。
# 此前仅处理单层数组 GEP:嵌套下标(c.Addr(self.lines[line_idx][0]),
# vpui.vp L985)的 value 是内层 Subscript,translate_value 把内层加载成
# 数组值([80 x i8])→ 非 Ptr → return None → 跌落跨模块函数调用生成
# call @Addr(ld.lld undefined symbol: Addr 根因)。
if arg0.kind() == ast.ASTKind.Subscript:
if trans is None:
return None
elem_ptr_ca: llvmlite.Value | t.CPtr = self.Gen.ExprGenName.get_subscript_ptr(
builder, pool, mod, arg0, trans)
if elem_ptr_ca is not None:
return elem_ptr_ca
# 兜底:单层数组基址 GEP(get_subscript_ptr 未命中的形态)
sub_addr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(arg0)
if sub_addr is None or sub_addr.value is None or sub_addr.slice is None:
return None
@@ -434,7 +604,6 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
builder, pool, mod, sub_addr.slice, None, 0, trans)
if idx_val is None:
return None
# 数组基址 pointee 是 Array(elem_ty, N) → GEP 得元素指针
arr_pointee_ca: llvmlite.LLVMType | t.CPtr = None
if self.Gen.ExprGenBase.is_ptr_type(arr_val.Ty) != 0:
arr_pointee_ca = arr_val.Ty.Pointee
@@ -444,7 +613,12 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
return llvmlite.build_gep_array(
builder, arr_pointee_ca, arr_et_ca, arr_val, idx_val)
case _:
pass
# 标量/指针元素(如字符串 a[4])→ 简单 GEP 元素地址。
# 此前仅 Array 走 GEP,标量 pointee 落 return None →
# 跌落跨模块函数调用生成 call @Addr(main.vp L272
# c.Addr(a[4]) 根因)。
return llvmlite.build_gep(
builder, arr_pointee_ca, arr_val, idx_val)
return None
# Call 形式: c.Addr(expr) → 求值 expr 存临时槽(entry alloca),返回槽地址
@@ -462,8 +636,13 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
llvmlite.build_store(builder, call_val, tmp_slot)
return tmp_slot
# 其他 arg0 形式(非 Name/Attribute/Subscript/Call):真实掉出函数末尾,
# 补显式 return None(fail-fast 不允许流程掉出末尾)
# 其他 arg0 形式(非 Name/Attribute/Subscript/Call):fail-fast。
# c.Addr 是编译期指令,绝不跌落成运行时 call @Addr(ld.lld undefined
# symbol: Addr 根因——未识别形态 return None 会被 translate_call 当作
# 普通函数调用处理)。
self.Gen.TypeGenErr.fatal_error(
cl, f"c.Addr 不支持的操作数形式(kind={arg0.kind()}),"
f"c.Addr 仅支持 变量/全局/函数/数组元素/字段", "TypeError")
return None
@@ -529,6 +708,78 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
dst[dlen + i] = '\0'
def _asm_mangle_global_symbols(self, pool: memhub.MemBuddy | t.CPtr,
asm_text: str, asm_size: t.CSizeT,
mod: llvmlite.LLVMModule | t.CPtr) -> str:
"""asm 模板中裸符号引用模块全局的检查(fail-fast)
内联汇编 `lea rax, [rip + gdt]` 的 gdt 是汇编器符号,若对应模块全局
带 SHA1 混淆头(@"054f5e56cbefcaa4.gdt")则无法在 LLVM MC 中引用:
数字开头的符号名会被解析成数字 → llc "Unexpected offset"(VKernel
gdt.vp L76/L93、sched.vp `[rip + _switch_old_rsp]` 根因)。因此引用
的全局必须标记 t.CExport 使用裸名(@gdt),此处按词边界匹配(前后
非标识符字符,避免误伤寄存器名/局部标签/指令助记符)命中即报错。
"""
if asm_text is None or mod is None:
return asm_text
src_len: t.CSizeT = string.strlen(asm_text)
if src_len == 0:
return asm_text
out: str = pool.alloc(asm_size * 2)
if out is None:
return asm_text
out[0] = '\0'
out_pos: t.CSizeT = 0
i: t.CSizeT = 0
while i < src_len:
matched: int = 0
cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
while cur is not None:
gname: str = cur.Name
if gname is not None and string.strchr(gname, 46) is not None:
dot_g: str = string.strchr(gname, 46)
bare_g: str = dot_g + 1
bare_len: t.CSizeT = string.strlen(bare_g)
if bare_len > 0 and i + bare_len <= src_len:
if string.strncmp(asm_text + i, bare_g, bare_len) == 0:
# 词边界:前后非标识符字符(字母/数字/_)
prev_ok: int = 1
next_ok: int = 1
if i > 0:
prev_ch: t.CChar = asm_text[i - 1]
if (prev_ch >= 'a' and prev_ch <= 'z') \
or (prev_ch >= 'A' and prev_ch <= 'Z') \
or (prev_ch >= '0' and prev_ch <= '9') \
or prev_ch == '_':
prev_ok = 0
if i + bare_len < src_len:
next_ch: t.CChar = asm_text[i + bare_len]
if (next_ch >= 'a' and next_ch <= 'z') \
or (next_ch >= 'A' and next_ch <= 'Z') \
or (next_ch >= '0' and next_ch <= '9') \
or next_ch == '_':
next_ok = 0
if prev_ok != 0 and next_ok != 0:
# fail-fast:asm 模板引用的模块全局带 SHA1 混淆头
# 不可在 LLVM MC 内联汇编中使用(数字开头的符号名
# 会被解析成数字而报 "Unexpected offset"),必须
# 标记 t.CExport 使用裸名(如 gdt → @gdt)。
self.Gen.TypeGenErr.fatal_error(
None,
f"asm 模板引用的全局符号 '{bare_g}' 带 SHA1 混淆头"
f"({gname}),内联汇编必须引用裸名符号,请将该"
f"全局定义标记为 t.CExport", "TypeError")
return asm_text
cur = cur.Next
if matched == 0:
if out_pos + 1 < asm_size * 2:
out[out_pos] = asm_text[i]
out_pos += 1
i += 1
out[out_pos] = '\0'
return out
def _asm_escape_for_ir(self, pool: memhub.MemBuddy | t.CPtr,
src: str) -> str:
"""转义汇编文本用于 LLVM IR 字符串字面量"""
@@ -730,6 +981,38 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
return at.attr
def _asm_name_is_mangled(self, name: str) -> int:
"""判断 LLVM 名称是否带 SHA1 混淆头(16 hex + '.'),返回 1=是
兼容 make_global_ref 生成的 '@"sha1.name"' 引用名和 GlobalVariable
裸名 "sha1.name"。'@name' 裸引用与局部 SSA(%N)都不算混淆。
"""
if name is None:
return 0
n: str = name
nlen: t.CSizeT = string.strlen(n)
# 去掉 '@"..."' 包装(引用名)
if nlen >= 3 and n[0] == '@' and n[1] == '"' and n[nlen - 1] == '"':
n = n + 2
nlen -= 3
elif nlen >= 1 and n[0] == '@':
n = n + 1
nlen -= 1
if nlen <= 17:
return 0
for i in range(16, nlen):
if n[i] == '.':
ok: int = 1
for j in range(16):
cc: t.CChar = n[i - 16 + j]
if not (('0' <= cc <= '9') or ('a' <= cc <= 'f') or ('A' <= cc <= 'F')):
ok = 0
break
if ok != 0:
return 1
return 0
def _asm_add_operand(self, operands: GenTypes.AsmOperand | t.CPtr,
count: int,
val: llvmlite.Value | t.CPtr,
@@ -744,6 +1027,16 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
"""
if count >= ASM_MAX_OPERANDS:
return count
# fail-fast:AsmInp/AsmOut 操作数不允许带 SHA1 混淆头的符号
# (模块级全局/函数)。LLVM MC 内联汇编无法解析数字开头的符号名,
# 且裸机链接要求裸名 —— 必须将对应定义标记为 t.CExport。
if val is not None and val.Name is not None:
if self._asm_name_is_mangled(val.Name) != 0:
self.Gen.TypeGenErr.fatal_error(
None,
f"c.Asm 操作数(AsmInp/AsmOut)不允许带 SHA1 混淆头的符号 "
f"'{val.Name}',请将对应函数/全局定义标记为 t.CExport(裸名)",
"TypeError")
if val is not None:
for ei in range(count):
eop: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, ei)
@@ -751,6 +1044,16 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
return count
op: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, count)
op.Value = val
# 缺省约束:AsmInp(reg) 只传 1 参无 descr → 约束空 → llc
# "number of input constraints does not match number of parameters"
# (mm.vp L730 c.Asm(f"mov al, {AsmInp(reg)}\nout 0x70, al") 根因)。
# 输入默认 "r"(通用寄存器)、输出默认 "=r"(寄存器输出),
# 与 _resolve_register 的 REG_ANY/OUTPUT_REG 一致。
if constraint is None or string.strlen(constraint) == 0:
if is_output != 0:
constraint = "=r"
else:
constraint = "r"
op.Constraint = constraint
op.IsOutput = is_output
return count + 1
@@ -845,13 +1148,60 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
cons_str = self._resolve_asm_descr(pool, fargs.get(1))
if is_out != 0:
# 输出:获取变量 alloca
if val_node is None or val_node.kind() != ast.ASTKind.Name:
# 输出:获取变量 alloca(Name 局部/全局 或
# Attribute 字段地址)
if val_node is None:
continue
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node)
if nm.id is None:
continue
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
alloca: llvmlite.Value | t.CPtr = None
if val_node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node)
if nm.id is None:
continue
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None:
# 模块级全局变量(process.vp _saved_cr3 来自
# isr.s .comm):lookup_var 只查局部 → 全局
# 落空 → 操作数缺失 → asm 模板 "$0" 空 →
# "mov , cr3" llc unknown token in expression。
# 回退查模块全局(精确+后缀),全局 Value
# (@name,类型 i64*)直接当 store 目标。
alloca = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id)
elif val_node.kind() == ast.ASTKind.Attribute:
# AsmOut(p.pml4_root)(p 为 Process 指针):
# GEP 到字段地址(对齐 translate_c_addr Attribute
# 分支)。仅 Name 检查时 Attribute 被跳过 →
# 空操作数 "mov , cr3"(process.vp L127/L208 根因)。
at_a: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(val_node)
if at_a.attr is not None and at_a.value is not None \
and at_a.value.kind() == ast.ASTKind.Name:
base_nm_a: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_a.value)
if base_nm_a.id is not None:
base_alloca_a: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, base_nm_a.id)
if base_alloca_a is not None and base_alloca_a.Ty is not None:
struct_ty_a: llvmlite.LLVMType | t.CPtr = None
self_is_ptr_ptr_a: int = 0
match base_alloca_a.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
struct_ty_a = inner2
self_is_ptr_ptr_a = 1
case _:
struct_ty_a = inner1
case _:
pass
if struct_ty_a is not None:
fe_a: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
struct_ty_a, at_a.attr)
if fe_a is not None:
obj_ptr_a: llvmlite.Value | t.CPtr = base_alloca_a
if self_is_ptr_ptr_a == 1:
obj_ptr_a = llvmlite.build_load(
builder, llvmlite.Ptr(pool, struct_ty_a), base_alloca_a)
if obj_ptr_a is not None:
alloca = llvmlite.build_gep_struct(
builder, struct_ty_a, fe_a.Ty, obj_ptr_a, fe_a.Index)
if alloca is None:
continue
operand_count = self._asm_add_operand(
@@ -1192,8 +1542,12 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
# ============================================================
# 7. 转义汇编文本
# ============================================================
escaped_asm: str = self._asm_escape_for_ir(pool, asm_text)
# 7.0 先做裸符号→混淆名替换(lea rax, [rip + gdt] → [rip + 054f5e56cbefcaa4.gdt])
mangled_asm: str = self._asm_mangle_global_symbols(
pool, asm_text, ASM_TEXT_BUF_SIZE, mod)
if mangled_asm is None:
mangled_asm = asm_text
escaped_asm: str = self._asm_escape_for_ir(pool, mangled_asm)
if escaped_asm is None:
return None
+24 -1
View File
@@ -710,6 +710,12 @@ class CallGenCall(CallGenBase.CallGenBase):
mc_entry = self.Gen.StructGen.find_struct_by_type(mc_struct_ty)
if mc_entry is None:
mc_entry = self.Gen.StructGen.find_struct(class_name)
# [MMDBG] 诊断:mc_entry 解析(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if mc_entry is not None:
stdio.printf("[MMDBG] mc_entry.Name=%s\n", mc_entry.Name)
else:
stdio.printf("[MMDBG] mc_entry=None\n")
# 构造方法查找名: ClassName.method_name
lookup_name: str = pool.alloc(128)
@@ -719,6 +725,12 @@ class CallGenCall(CallGenBase.CallGenBase):
# 查找函数(支持 SHA1 后缀匹配)
found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name)
# [MMDBG] 诊断:found_func 查找(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if found_func is not None:
stdio.printf("[MMDBG] found_func=%s\n", llvmlite.function_get_name(found_func))
else:
stdio.printf("[MMDBG] found_func=None lookup=%s\n", lookup_name)
# 泛型模板类方法:特化函数名带类型参数({sha1}.list[int].get),
# "ClassName.method" 短名匹配不到。特化由触发方(通常即调用方)模块完成,
@@ -1577,9 +1589,20 @@ class CallGenCall(CallGenBase.CallGenBase):
is_variadic_3441: int = self.Gen.CallGenVa._check_and_promote_variadic_call(
pool, builder, mod, call_name, head, total_count, cargs, 1, trans)
# try-except:方法含 raise(EH 输出参数)时追加输出实参 + 调用后检测
return self.Gen.CallGenLeafKit._emit_eh_guarded_call(
# [MMDBG] 诊断:调用发射前状态(用后移除)
if string.strcmp(method_name, "Sub") == 0:
stdio.printf("[MMDBG] pre-call call_name=%s ret_ty_ptr=%llu head=%llu count=%d\n",
call_name, t.CUInt64T(call_ret_ty), t.CUInt64T(head), total_count)
res_final: llvmlite.Value | t.CPtr = self.Gen.CallGenLeafKit._emit_eh_guarded_call(
builder, pool, mod, trans, found_func, call_name, head, total_count,
call_ret_ty, is_variadic_3441)
# [MMDBG] 诊断:调用发射结果(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if res_final is not None:
stdio.printf("[MMDBG] _emit_eh_guarded_call ok\n")
else:
stdio.printf("[MMDBG] _emit_eh_guarded_call None\n")
return res_final
# ============================================================
# _translate_super_call — super().method(args) → 静态调用父类方法
#
+1 -1
View File
@@ -715,7 +715,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
if kw is None or kw.arg is None or kw.value is None:
continue
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field_by_class(
class_name, kw.arg, kw_sha1)
class_name, kw.arg, kw_sha1, kw, trans._from_imports)
if fe is None:
# 关键字字段不存在:fail-fast(AttributeError),绝不静默跳过
self.Gen.TypeGenErr.fatal_error(
+140 -2
View File
@@ -306,6 +306,81 @@ class CallGenDispatch(CallGenBase.CallGenBase):
if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name:
tc_mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tc_at.value)
if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0:
# t.CType 特殊形式:t.CType(value, TypeName, t.CPtr) →
# 把 value 转为 TypeName|t.CPtr(TypeName* 指针)。TypeName
# 是类型名(Name/Attribute),不是值表达式——此前被当普通
# 参数翻译 → NameError: 未定义名称 'memory_block'
# (VKernel mm.vp merge_blocks 根因)。CType 不是 map_t_type
# 的具名类型,须在此显式处理,否则跌落到跨模块 FATAL。
if string.strcmp(func_name, "CType") == 0 and can >= 2:
cty_node: ast.AST | t.CPtr = cargs.get(1)
ctype_target: llvmlite.LLVMType | t.CPtr = None
if cty_node is not None:
cti_imp: str = None
cti_fi: str = None
if trans is not None:
cti_imp = trans._imported_modules
cti_fi = trans._from_imports
ctype_target = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, cty_node, cti_imp, cti_fi, trans, 0)
if ctype_target is not None:
if can == 3:
cty_arg3: ast.AST | t.CPtr = cargs.get(2)
cty_is_cptr: int = 0
if cty_arg3 is not None:
# 形式 1: t.CPtr(Attribute)
if cty_arg3.kind() == ast.ASTKind.Attribute:
cty_at3: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cty_arg3)
if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0:
if cty_at3.value is not None and cty_at3.value.kind() == ast.ASTKind.Name:
cty_nm3: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cty_at3.value)
if cty_nm3.id is not None and string.strcmp(cty_nm3.id, "t") == 0:
cty_is_cptr = 1
# 形式 2: 裸名 CPtr
elif cty_arg3.kind() == ast.ASTKind.Name:
cty_bn3: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cty_arg3)
if cty_bn3.id is not None and string.strcmp(cty_bn3.id, "CPtr") == 0:
cty_is_cptr = 1
if cty_is_cptr != 0:
# CVoid* → i8*(LLVM 中 void* 表示为 i8*)
match ctype_target:
case llvmlite.LLVMType.Void():
ctype_target = llvmlite.Int8(pool)
case _:
pass
ctype_target = llvmlite.Ptr(pool, ctype_target)
cty_arg0: ast.AST | t.CPtr = cargs.get(0)
if cty_arg0 is not None:
cty_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cty_arg0, funcs_ptr, func_count, trans)
if cty_val is not None:
return self.Gen.CallGenCtor._translate_t_type_cast(
pool, builder, cty_val, ctype_target,
func_name, cty_arg0, trans)
# t.CType 语法已确认:即使类型解析失败也不跌落到跨模块 FATAL
return None
# CConst const 标记:CConst(val, t.CPtr) → Ptr(i8)(const void*)。
# CConst 不是 map_t_type 具名类型——t.CConst|t.CChar|t.CPtr 注解 =
# const char*(i8*),显式调用 t.CConst(addr, t.CPtr) 把整数地址转
# const 指针。此前 map_t_type 失败 → 跌落跨模块函数调用 → 生成
# call @CConst(裸名,无 obj 定义)→ ld.lld undefined symbol:
# CConst(elf.vp L650 t.CConst(t.CUInt64T(strtab_ptr)+sym.st_name,
# t.CPtr) 根因)。单参 CConst(x) 仅 const 限定,LLVM 类型不变,
# 直接返回原值。
if string.strcmp(func_name, "CConst") == 0:
if can >= 2:
cconst_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
cconst_arg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cconst_arg is not None:
return self.Gen.CallGenCtor._translate_t_type_cast(
pool, builder, cconst_arg, cconst_ptr_ty,
func_name, cargs.get(0), trans)
elif can == 1:
return self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
return None
target_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
if target_ty is not None:
tc_effective_can: t.CSizeT = can
@@ -374,7 +449,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
mod_imp_td = trans._imported_modules
mod_fi_td = trans._from_imports
mod_td_ty = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, mod_td_ast, mod_imp_td, mod_fi_td, trans)
pool, mod_td_ast, mod_imp_td, mod_fi_td, trans, 0)
if mod_td_ty is not None and can == 1:
mt_arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
@@ -412,7 +487,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
imp_gp = trans._imported_modules
fi_gp = trans._from_imports
bare_ty = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, gp_ast, imp_gp, fi_gp, trans)
pool, gp_ast, imp_gp, fi_gp, trans, 0)
# 泛型类型参数双参数形式: T(val, t.CPtr) → Ptr(T)
# (与 t.CInt(val, t.CPtr) → Ptr(i32) 对齐;vector.vp get() 的
# typed_ptr = T(offset, t.CPtr) 依赖此路径,否则双参数不进
@@ -944,9 +1019,72 @@ class CallGenDispatch(CallGenBase.CallGenBase):
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
method_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if method_at is not None and method_at.value is not None:
# 链式方法调用: a.b().c() —— value 是内层 Call(如 gfx.vp 的
# eye.Sub(target).Norm())。此前未处理 → 外层 .Norm() 的 value
# 非 Name 跳过方法分支 → 通用路径把 eye.Sub 当属性解析 →
# "属性写入失败: .Sub"。先翻译内层调用得接收者,再方法调用。
if method_at.value.kind() == ast.ASTKind.Call and trans is not None:
recv_val_c: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, method_at.value, None, 0, trans)
if recv_val_c is not None and recv_val_c.Ty is not None:
recv_ty_c: llvmlite.LLVMType | t.CPtr = recv_val_c.Ty
if self.Gen.ExprGenBase.is_ptr_type(recv_ty_c) != 0:
recv_ty_c = recv_ty_c.Pointee
# 堆对象(@t.Object)双指针 → 再解一层取 struct
if self.Gen.ExprGenBase.is_ptr_type(recv_ty_c) != 0:
recv_ty_c = recv_ty_c.Pointee
recv_cls_c: str = self.Gen.StructGen.get_class_name_by_type(
pool, recv_ty_c)
if recv_cls_c is not None:
if self.Gen.CallGenProp._is_list_builtin_method(
recv_cls_c, method_at.attr) == 0:
obj_ptr_c: llvmlite.Value | t.CPtr = recv_val_c
if self.Gen.ExprGenBase.is_ptr_type(recv_val_c.Ty) != 0 \
and recv_val_c.Ty.Pointee is not None \
and self.Gen.ExprGenBase.is_ptr_type(recv_val_c.Ty.Pointee) != 0:
obj_ptr_c = llvmlite.build_load(
builder, recv_val_c.Ty, recv_val_c)
if obj_ptr_c is not None:
if hit_box is not None:
hit_box[0] = 1
return self.Gen.CallGenCall._translate_method_call(
pool, builder, mod, recv_cls_c, method_at.attr,
obj_ptr_c, cl, trans)
if method_at.value.kind() == ast.ASTKind.Name and trans is not None:
method_obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(method_at.value)
if method_obj_nm.id is not None:
# nonlocal 对象:提升函数内从 env 读对象指针(load_nonlocal_var),
# 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱 →
# llc "use of undefined value")。entry.get() 等方法调用的
# 基座对象经此路径解析(closure 隐式捕获场景)。
# 用 get_struct_type_from_value 递归解 Ptr,兼容
# Ptr(Struct)(load_nonlocal_var 已 load)与
# Ptr(Ptr(Struct))(联合简化未解引用)两种形态。
if HT.is_nonlocal_name(trans, method_obj_nm.id) != 0:
nl_obj: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.load_nonlocal_var(
trans, method_obj_nm.id)
if nl_obj is not None and nl_obj.Ty is not None:
nl_deref: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase._deref_if_ptr_ptr(
builder, nl_obj)
if nl_deref is None:
nl_deref = nl_obj
nl_struct: llvmlite.LLVMType | t.CPtr = \
self.Gen.StructGen.get_struct_type_from_value(nl_deref)
if nl_struct is not None:
nl_cls: str = self.Gen.StructGen.get_class_name_by_type(
pool, nl_struct)
if nl_cls is not None:
if self.Gen.CallGenProp._is_list_builtin_method(
nl_cls, func_name) == 0:
if hit_box is not None:
hit_box[0] = 1
return self.Gen.CallGenCall._translate_method_call(
pool, builder, mod, nl_cls, func_name,
nl_deref, cl, trans)
# 非 local 对象但分派未成功:绝不回退 lookup_var
# (其 Alloca 是父函数存储,跨函数引用 → SSA 错乱),
# 返回 None 让上层 fast-fail 暴露类型解析问题。
return None
# 查找对象变量
obj_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, method_obj_nm.id)
+7
View File
@@ -1183,6 +1183,13 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
entry.Defaults = defaults
entry.DefaultCount = default_count
entry.ParamCount = param_count
# 范围防御:default_count 异常(vpi tree 损坏时 len 垃圾大数)时放弃
# 提取快照(调用方哨兵已拦截,此为第二道),避免 alloc(垃圾) + 数组
# 越界写 perm_pool 覆盖当前模块 AST/表(SlotsBugTest 崩溃根因候选)。
if default_count > 128:
entry.Defaults = None
entry.DefaultCount = 0
return 0
# 提取常量快照到 stdlib
if default_count > 0:
kinds: t.CInt | t.CPtr = CompilerFactory.get_perm_pool().alloc(default_count * 4)
+85 -8
View File
@@ -79,7 +79,7 @@ class CallGenVa(CallGenBase.CallGenBase):
return llvmlite.Int32(pool)
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, type_node, trans._imported_modules, trans._from_imports, trans)
pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
return resolved
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
@@ -230,6 +230,50 @@ class CallGenVa(CallGenBase.CallGenBase):
count += 1
param = llvmlite.param_get_next(param)
return count
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
"""已知变参 C 库函数固定参数个数(-1=未知)
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
"""
if name is None:
return -1
if string.strcmp(name, "snprintf") == 0:
return 3
if string.strcmp(name, "sprintf") == 0:
return 2
if string.strcmp(name, "vsprintf") == 0:
return 2
return -1
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
name: str, idx: int) -> llvmlite.LLVMType | t.CPtr:
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
"""
if pool is None or name is None or idx < 0:
return None
i8_ptr_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
if string.strcmp(name, "snprintf") == 0:
if idx == 0:
return i8_ptr_kv
if idx == 1:
return i64_kv
if idx == 2:
return i8_ptr_kv
return None
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
if idx == 0:
return i8_ptr_kv
if idx == 1:
return i8_ptr_kv
return None
return None
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
@@ -252,13 +296,20 @@ class CallGenVa(CallGenBase.CallGenBase):
return 0
# 查找函数对象
func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
if func is None:
return 0
if func.IsVarArg != 1:
return 0
# 获取固定参数数量
fixed_count: int = self._get_func_fixed_param_count(func)
fixed_count: int = -1
if func is not None and func.IsVarArg == 1:
fixed_count = self._get_func_fixed_param_count(func)
else:
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):
# 1. func 为 None——跨模块 stub 未注入(CallGenMixin 注释:stubs
# 在翻译后才注入,翻译时跨模块函数 found_func 恒 None);
# 2. func 存在但 IsVarArg=0——该 declare 由 _ensure_method_declare
# 按实参类型临时创建(无 ... 标记),不能代表真实签名。
# 否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 →
# va_arg(i64) 高 4 字节读残留栈垃圾(clean 日志 0x7ff700000052 根因)。
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
if fixed_count < 0:
return 0
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
if can <= fixed_count:
@@ -319,6 +370,32 @@ class CallGenVa(CallGenBase.CallGenBase):
promoted.Next = next_node
if prev is not None:
prev.Next = promoted
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
# L798-811 的 found_func.IsVarArg 提升)。
i64_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
if pb_va != 0 and pb_va < 64:
promoted_va2: llvmlite.Value | t.CPtr = None
is_u_va2: int = 0
if arg_nodes is not None and trans is not None:
ast_idx_va2: int = v_idx - self_offset
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
an_va2: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va2)
if an_va2 is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
is_u_va2 = 1
if is_u_va2 != 0:
promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote)
else:
promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote)
if promoted_va2 is not None:
promoted_va2.Next = next_node
if prev is not None:
prev.Next = promoted_va2
promoted = promoted_va2
prev = promoted
cur = next_node
v_idx += 1
+21 -6
View File
@@ -137,17 +137,32 @@ class CallGenVt(CallGenBase.CallGenBase):
llvmlite.module_add_global(mod, vcopy_gv)
vcopy_gv.Linkage = None
vcopy_gv.Initializer = "zeroinitializer"
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
# 输出时加的,引用侧不自动加)
vcopy_ref_nm: str = pool.alloc(272)
if vcopy_ref_nm is None:
vcopy_ref_nm = vcopy_buf
else:
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
vcopy_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(
pool, llvmlite.Ptr(pool, vtable_ty), vcopy_buf)
pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
if vcopy_ref is not None:
vcopy_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, vcopy_ref, i8_ptr_ty)
if vcopy_i8 is not None:
vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, llvmlite.Int64(pool), method_count * 8, "vtsz")
if vt_size is not None:
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(
pool, builder, mod, vcopy_i8, class_vt, vt_size)
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
# → llc "expected value token"(devfs memcpy 根因)
vtsz_buf: str = pool.alloc(32)
if vtsz_buf is not None:
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
if vt_size is not None:
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(
pool, builder, mod, vcopy_i8, class_vt, vt_size)
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
llvmlite.build_br(builder, vt_skip_bb)
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
+1 -1
View File
@@ -328,7 +328,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# 解析字段类型
field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
+2 -2
View File
@@ -449,7 +449,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
# 解析字段类型
field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
if field_ty is None:
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
# 静默 return 1 让多遍收敛机制生效(renum 返回 1=跳过该类)
@@ -707,7 +707,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
mem_ty_l = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, vaa.annotation,
trans._imported_modules,
trans._from_imports, trans)
trans._from_imports, trans, 0)
self._register_renum_field_alias(
pool, class_name, vnm.id, field_idx + 1,
mem_ty_l, variant_cd.name)
+7 -1
View File
@@ -167,12 +167,18 @@ class ExprGenBase:
level: int = 0
cur: llvmlite.LLVMType | t.CPtr = ty
while cur is not None:
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
# 更新 while 条件变量(cur = pointee)时自举编译出的 match 把赋值
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
# 绑定到局部变量,match 外更新循环变量。
nxt_cur: llvmlite.LLVMType | t.CPtr = None
match cur:
case llvmlite.LLVMType.Ptr(pointee):
level += 1
cur = pointee
nxt_cur = pointee
case _:
break
cur = nxt_cur
return level
# ============================================================
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
+23 -2
View File
@@ -95,7 +95,8 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译常量(int/str/bool)"""
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None:
@@ -112,7 +113,27 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_CHAR:
# 单引号单字符 → i32 值(字符 ASCII 码),coerce_to_type 会 trunc 为 i8
# 单引号单字符字面量:默认按字符值(i32,coerce_to_type 截断为 i8)。
# 但当目标类型是指针(str/bytes/t.CPtr 等)时,对齐 TPC 引号推断
# (HandlesExpr:"目标是 ptr 但值是 char → 走字符串路径"):物化为
# 1 字符字符串。否则 coerce inttoptr 把字符值当指针(如 '"'=0x22),
# 运行期 strlen 访问低地址 → ASan 崩溃(二代编 StringTest PyI 生成根因)。
# 1a 阶段(declare_only=1)不 LoadDepVpi(vpi 未预生成)→ 依赖函数
# found_func=None → translate_call 缺省参数补全不生效 → translate_value
# 调用参数数 < define(如 target_ty 缺省)→ x64 ABI 被调方读第 9 参
# 栈槽残留垃圾(0x4 等)。declare_only 不产出 IR,target_ty 无用,
# 未对齐(8 对齐失败)即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
# (SlotsBugTest 1a string.vp 崩溃根因;1b found_func 非 None 正常填充)。
if target_ty is not None and (t.CUInt64T(target_ty) & 7) == 0 \
and self.is_ptr_type(target_ty) != 0:
cbuf: bytes = stdlib.malloc(2)
if cbuf is None:
return llvmlite.const_int32(pool, 0)
cbuf[0] = t.CChar(cn.int_val)
cbuf[1] = '\0'
r_cs: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, cbuf, trans)
stdlib.free(cbuf)
return r_cs
cv: t.CInt64T = cn.int_val
return llvmlite.const_int32(pool, cv)
elif cn.const_kind == ast.CONST_STR:
+27 -2
View File
@@ -79,13 +79,38 @@ class ExprGenMixin(ExprGenBase.ExprGenBase):
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
"""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(
node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
rv: llvmlite.Value | t.CPtr = self._translate_value_impl(
builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty)
if trans is not None:
trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans)
rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
return rv
elif k == ast.ASTKind.Name:
return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans)
File diff suppressed because it is too large. Load diff
+62 -35
View File
@@ -37,6 +37,12 @@ MAX_MATCH_CASES: t.CDefine = 32
# 作用域种类常量(与 HandlesVar 的 Scope.Kind 区分,原 HandlesMatch.SCOPE_MATCH)
SCOPE_MATCH: t.CDefine = 6
# match 翻译最大递归深度(match → case body → translate_value → 嵌套 match)。
# AST 自引用环/翻译 bug 可致无限递归 → ASan stack-overflow(崩溃点实测为
# translate_match 内 subject kind() 虚调用,栈 2MB 耗尽)。入口计数出口递减,
# 超限即 fail-fast(对齐 TypeGenResolve 深度守卫,防栈溢出)。
MATCH_TRANSLATE_MAX_DEPTH: t.CDefine = 64
@t.NoVTable
class MatchGenMixin:
@@ -86,6 +92,19 @@ class MatchGenMixin:
# ============================================================
def translate_match(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""match 语句翻译主入口(深度守卫包装器,原逻辑见 _translate_match_impl)"""
if trans is not None:
if trans.MatchTranslateDepth >= MATCH_TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(
node, "match 翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.MatchTranslateDepth += 1
mret: int = self._translate_match_impl(trans, node)
if trans is not None:
trans.MatchTranslateDepth -= 1
return mret
def _translate_match_impl(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
if node is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
@@ -365,17 +384,15 @@ class MatchGenMixin:
self.Gen.TypeGenErr.fatal_error(node, "match cases 缺失", "TypeError")
case_count: t.CSizeT = len(cases_list)
# [MATCHDBG] 临时诊断:match 元信息(对比崩溃对应文件/语句;用后移除)
stdio.printf("[MATCHDBG] renum_match node=%llx renum=%s subject=%llx cases=%llx count=%zu\n",
t.CUInt64T(node), renum_name, t.CUInt64T(subject_ptr),
t.CUInt64T(cases_list), case_count)
# BB 指针数组、case 值指针数组、default 标记数组、nobreak 标记数组
bb_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES)
cv_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES)
def_arr: t.CInt | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES)
nb_arr: t.CInt | t.CPtr = pool.alloc(4 * MAX_MATCH_CASES)
if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None:
# pass1 缓存的 mc 指针数组(pass2 复用,避免重索引)
mc_arr: t.CSizeT | t.CPtr = pool.alloc(8 * MAX_MATCH_CASES)
if bb_arr is None or cv_arr is None or def_arr is None or nb_arr is None \
or mc_arr is None:
# fail-fast: match 分派数组分配失败
self.Gen.TypeGenErr.fatal_error(node, "match 分派数组分配失败", "TypeError")
@@ -454,23 +471,14 @@ class MatchGenMixin:
body_list: list[ast.AST | t.CPtr] | t.CPtr = mc.children
nb: int = self._has_no_break(body_list)
# [MATCHDBG] 临时诊断:第一遍记录 case 快照(对比第二遍,定位列表破坏点;用后移除)
dbg_bl_len: t.CInt = -1
dbg_first0: t.CSizeT = 0
if body_list is not None:
dbg_bl_len = t.CInt(len(body_list))
if dbg_bl_len > 0:
dbg_first0 = t.CUInt64T(body_list.get(0))
stdio.printf("[MATCHDBG] pass1 i=%d mc=%llx pat=%llx children=%llx len=%d data0=%llx\n",
i, t.CUInt64T(mc), t.CUInt64T(pat),
t.CUInt64T(body_list), dbg_bl_len, dbg_first0)
# 存入数组
if collected < MAX_MATCH_CASES:
bb_arr[collected] = t.CSizeT(bb)
cv_arr[collected] = t.CSizeT(cv)
def_arr[collected] = is_def
nb_arr[collected] = nb
# 缓存 mc 指针(pass2 复用,避免重索引)
mc_arr[collected] = t.CSizeT(mc)
collected = collected + 1
# 构建 SwitchCase 链表(非 default case 且有值)
@@ -502,11 +510,12 @@ class MatchGenMixin:
# 7. 第二遍:翻译每个 case body
i = 0
while i < collected:
case_node: ast.AST | t.CPtr = cases_list.get(i)
if case_node is None:
# pass2 用 pass1 缓存的 mc(避免重索引错位)
mc: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(
t.CVoid(mc_arr[i], t.CPtr))
if mc is None:
i = i + 1
continue
mc: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(case_node)
pat: ast.AST | t.CPtr = mc.pattern
# 取 BB
@@ -540,11 +549,11 @@ class MatchGenMixin:
bc: t.CSizeT = len(body_list)
bi: int = 0
while bi < bc:
# 用 list.get() 读取元素值(已验证编译为 8 字节加载,与
# pass1 的 cases_list.get(i) 同型同码)。切勿改为
# `bl_data + bi*8` 裸地址:那会把数组槽地址当节点传入
# translate_stmt,虚调用跳进 vtable 表 → DEP 崩溃。
stmt: ast.AST | t.CPtr = body_list.get(bi)
# [MATCHDBG] 临时诊断:第二遍逐条打印(对比 pass1 快照;用后移除)
stdio.printf("[MATCHDBG] pass2 i=%d bi=%d bc=%zu mc=%llx children=%llx stmt=%llx\n",
i, bi, bc, t.CUInt64T(mc),
t.CUInt64T(body_list), t.CUInt64T(stmt))
if stmt is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt)
bi = bi + 1
@@ -582,13 +591,17 @@ class MatchGenMixin:
dmc: ast.MatchCase | t.CPtr = (
ast.MatchCase | t.CPtr)(dcase)
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
# dbody 预声明+重赋值会使 .get 误分派成 list[int].get
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children
dbody = dbody2
if dbody is not None:
dn: t.CSizeT = len(dbody)
if dbody2 is not None:
dn: t.CSizeT = len(dbody2)
dj: int = 0
while dj < dn:
stmt: ast.AST | t.CPtr = dbody.get(dj)
stmt: ast.AST | t.CPtr = dbody2.get(dj)
if stmt is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt)
dj = dj + 1
@@ -801,7 +814,10 @@ class MatchGenMixin:
if subj_vals is None:
return 0
for ei in range(ecount):
elt: ast.AST | t.CPtr = elts.get(ei)
# elts 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断);
# 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式)
elts_inl: list[ast.AST | t.CPtr] | t.CPtr = elts
elt: ast.AST | t.CPtr = elts_inl.get(ei)
sv: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, trans.Module, elt,
trans._funcs, trans._func_count, trans)
@@ -925,7 +941,10 @@ class MatchGenMixin:
cond = llvmlite.const_int(pool, 1, 0)
else:
for pi in range(ecount):
pat1: ast.AST | t.CPtr = pats.get(pi)
# pats 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断);
# 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式)
pats_inl: list[ast.AST | t.CPtr] | t.CPtr = pats
pat1: ast.AST | t.CPtr = pats_inl.get(pi)
if pat1 is None:
continue
pk1: int = pat1.kind()
@@ -1064,13 +1083,17 @@ class MatchGenMixin:
dmc: ast.MatchCase | t.CPtr = (
ast.MatchCase | t.CPtr)(dnode)
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
# dbody 预声明+重赋值会使 .get 误分派成 list[int].get
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children
dbody = dbody2
if dbody is not None:
dn: t.CSizeT = len(dbody)
if dbody2 is not None:
dn: t.CSizeT = len(dbody2)
dj: int = 0
while dj < dn:
stmt: ast.AST | t.CPtr = dbody.get(dj)
stmt: ast.AST | t.CPtr = dbody2.get(dj)
if stmt is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt)
dj = dj + 1
@@ -1301,13 +1324,17 @@ class MatchGenMixin:
dmc: ast.MatchCase | t.CPtr = (
ast.MatchCase | t.CPtr)(dcase)
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
# dbody 预声明+重赋值会使 .get 误分派成 list[int].get
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: list[ast.AST | t.CPtr] | t.CPtr = dmc.children
dbody = dbody2
if dbody is not None:
dn: t.CSizeT = len(dbody)
if dbody2 is not None:
dn: t.CSizeT = len(dbody2)
dj: int = 0
while dj < dn:
stmt: ast.AST | t.CPtr = dbody.get(dj)
stmt: ast.AST | t.CPtr = dbody2.get(dj)
if stmt is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt)
dj = dj + 1
+89 -2
View File
@@ -136,11 +136,98 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
node: ast.AST | t.CPtr) -> int:
"""预扫描语句中的 AnnAssign,提前创建 alloca
返回新增的变量数
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c)
在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的
i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 →
跨函数 SSA 错乱,llc "instruction expected to be numbered")。
返回新增的变量数(调用方均忽略返回值)。
"""
if node is None:
return 0
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
k: int = node.kind()
if k == ast.ASTKind.AnnAssign:
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
if k == ast.ASTKind.If:
ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node)
if ifn.children is not None:
bn_pre_i: t.CSizeT = len(ifn.children)
for bi_pre_i in range(bn_pre_i):
self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
if ifn.orelse is not None:
bn_pre_e: t.CSizeT = len(ifn.orelse)
for bi_pre_e in range(bn_pre_e):
self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
return 0
if k == ast.ASTKind.For:
for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node)
if for_n.children is not None:
bn_pre_f: t.CSizeT = len(for_n.children)
for bi_pre_f in range(bn_pre_f):
self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
if for_n.orelse is not None:
bn_pre_fe: t.CSizeT = len(for_n.orelse)
for bi_pre_fe in range(bn_pre_fe):
self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
return 0
if k == ast.ASTKind.While:
wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node)
if wh_n.children is not None:
bn_pre_w: t.CSizeT = len(wh_n.children)
for bi_pre_w in range(bn_pre_w):
self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
if wh_n.orelse is not None:
bn_pre_we: t.CSizeT = len(wh_n.orelse)
for bi_pre_we in range(bn_pre_we):
self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
return 0
if k == ast.ASTKind.Try:
tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
if tr_n.children is not None:
bn_pre_t: t.CSizeT = len(tr_n.children)
for bi_pre_t in range(bn_pre_t):
self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
if tr_n.handlers is not None:
bn_pre_th: t.CSizeT = len(tr_n.handlers)
for bi_pre_th in range(bn_pre_th):
eh_pre: ast.AST | t.CPtr = tr_n.handlers.get(bi_pre_th)
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
ehc_pre: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_pre)
if ehc_pre.children is not None:
bn_pre_ehb: t.CSizeT = len(ehc_pre.children)
for bi_pre_ehb in range(bn_pre_ehb):
self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
if tr_n.orelse is not None:
bn_pre_to: t.CSizeT = len(tr_n.orelse)
for bi_pre_to in range(bn_pre_to):
self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
if tr_n.finalbody is not None:
bn_pre_tf: t.CSizeT = len(tr_n.finalbody)
for bi_pre_tf in range(bn_pre_tf):
self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
return 0
if k == ast.ASTKind.With:
wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
if wth.children is not None:
bn_pre_wb: t.CSizeT = len(wth.children)
for bi_pre_wb in range(bn_pre_wb):
self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
return 0
if k == ast.ASTKind.Match:
mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node)
if mt_n.cases is not None:
bn_pre_mc: t.CSizeT = len(mt_n.cases)
for bi_pre_mc in range(bn_pre_mc):
mc_pre: ast.AST | t.CPtr = mt_n.cases.get(bi_pre_mc)
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
mcc_pre: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_pre)
if mcc_pre.children is not None:
bn_pre_mcb: t.CSizeT = len(mcc_pre.children)
for bi_pre_mcb in range(bn_pre_mcb):
self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
return 0
return 0
def pre_scan_scope_decls(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
+13
View File
@@ -41,6 +41,19 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
def translate_stmt(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)"""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(
node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
mret: int = self._translate_stmt_impl(trans, node)
if trans is not None:
trans.TranslateDepth -= 1
return mret
def _translate_stmt_impl(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译单条语句,返回新增的变量数"""
if node is None:
return 0
+11 -3
View File
@@ -280,16 +280,24 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
def _eval_const_int_expr(self, node: ast.AST | t.CPtr,
pool: memhub.MemBuddy | t.CPtr = None,
from_imports: str = None,
imported_modules: str = None) -> int:
imported_modules: str = None,
depth: int = 0) -> int:
"""递归计算常量整数表达式
pool/from_imports/imported_modules:跨模块 CDefine 查找上下文
(t.CArray count 引用依赖模块的 CDefine,如 SHA512_BLOCK_LEN 定义在
hashlib/__sha512.vp,本地 CDefine 表每模块清空,需跨模块回退查找)。
depth:递归深度守卫(防御 AST 损坏成自引用环导致的无限递归——
本函数被 resolve_annotation_type 的 t.CArray count 解析调用,其内部
BinOp 自递归不经过 resolve_annotation_type 的 depth 守卫)。
"""
if node is None:
self.ConstEvalOk = 0
return 0
# 递归深度守卫:BinOp 左右子树成环时无限递归直至栈耗尽。
# fail-fast 终止(fatal_type_error → sys.exit(1)),不静默回落。
if depth > 64:
self.Gen.TypeGenErr.fatal_type_error(node, "常量表达式递归过深(AST 环或嵌套超过 64 层)")
k: int = node.kind()
# Constant: 整数字面量
@@ -369,11 +377,11 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
# 递归计算左操作数
lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules)
lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 递归计算右操作数
rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules)
rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 根据运算符执行计算
+10 -4
View File
@@ -205,12 +205,18 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
ret_node_cr: ast.AST | t.CPtr = tp_cr.elts.get(1)
if ret_node_cr is None:
return None
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans)
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
def has_decorator_marker(self, node: ast.AST | t.CPtr,
marker_name: str) -> int:
marker_name: str,
depth: int = 0) -> int:
"""检测类型注解中是否包含指定的装饰器标记"""
if node is None or marker_name is None:
return 0
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
if depth > 128:
self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
k: int = node.kind()
@@ -225,9 +231,9 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if self.has_decorator_marker(bop.left, marker_name) != 0:
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0:
return 1
if self.has_decorator_marker(bop.right, marker_name) != 0:
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0:
return 1
return 0
+209 -29
View File
@@ -16,6 +16,7 @@ import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.CompilerFactory as CompilerFactory
@@ -70,6 +71,30 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
string.strcat(buf, at_attr_v)
return buf
return at_attr_v
# Subscript: 构造 "value[slice]" 格式(slice 递归,List/Tuple 保留完整下标)
# 如 t.Callable[[t.CInt], t.CInt] → "t.Callable[[t.CInt], t.CInt]"。
# CTypedef 值提取(_register_ctypedef_from_annassign → lookup → _parse_type_string
# 重建)需要完整类型表达式字符串,slice 不能丢(此前不支持 Subscript → 返回
# None → 注册失败 → 同 vpi 后续裸类名 lookup_ctypedef 失败 fail-fast,
# VKernel idt.vp irq_handler_t 根因)。
if k == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
val_s: str = self._get_type_name_from_annotation(pool, sub_s.value)
if val_s is None:
return None
slice_s: str = self._subscript_slice_to_str(pool, sub_s.slice)
if slice_s is None:
return None
total_s: t.CSizeT = string.strlen(val_s) + 1 + string.strlen(slice_s) + 2
buf_s: str = pool.alloc(total_s)
if buf_s is not None:
buf_s[0] = '\0'
string.strcat(buf_s, val_s)
string.strcat(buf_s, "[")
string.strcat(buf_s, slice_s)
string.strcat(buf_s, "]")
return buf_s
return None
# BinOp (BitOr): 构造 "left|right" 格式
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
@@ -87,6 +112,70 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return buf2
return None
return None
def _subscript_slice_to_str(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr) -> str:
"""Subscript slice 节点转类型字符串(递归,支持 Name/Attribute/BinOp/Constant/List/Tuple/Subscript)"""
if node is None:
return None
k2: int = node.kind()
# Name / Attribute / BinOp / 嵌套 Subscript:复用类型名提取
if k2 == ast.ASTKind.Name or k2 == ast.ASTKind.Attribute \
or k2 == ast.ASTKind.BinOp or k2 == ast.ASTKind.Subscript:
return self._get_type_name_from_annotation(pool, node)
# Constant:数值/字符串字面量(t.CArray[elem, 104] 的 count 等)
if k2 == ast.ASTKind.Constant:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn2.const_kind == ast.CONST_INT:
buf2: str = pool.alloc(24)
if buf2 is not None:
viperlib.snprintf(buf2, 24, "%lld", t.CInt64T(cn2.int_val))
return buf2
if cn2.const_kind == ast.CONST_STR and cn2.str_val is not None:
return cn2.str_val
return None
# List:Callable 参数列表([[A], B]),元素递归,逗号连接
if k2 == ast.ASTKind.List:
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
return self._list_elts_to_type_str(pool, lst.elts, "[", "]")
# Tuple:t.CArray[elem, N] 等双参形式。注意 TPV parser 把 Subscript 内
# 带逗号的 slice 统一打包为 Tuple("a[x, y]" 语法),故 slice 在 Subscript
# 内**不带括号**输出("x, y"),否则 "t.Callable[([t.CInt], t.CInt)]"
# 的显式括号 Tuple slice 会让 ast.parse_expression 解析失败。
if k2 == ast.ASTKind.Tuple:
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
return self._list_elts_to_type_str(pool, tup.elts, "", "")
return None
def _list_elts_to_type_str(self, pool: memhub.MemBuddy | t.CPtr,
elts: list[ast.AST | t.CPtr] | t.CPtr,
open_ch: str, close_ch: str) -> str:
"""元素列表转逗号分隔字符串(List/Tuple 共用),包上 open/close 括号"""
if elts is None or pool is None:
return None
buf3: str = pool.alloc(1024)
if buf3 is None:
return None
buf3[0] = '\0'
n3: t.CSizeT = len(elts)
for i3 in range(n3):
e3: ast.AST | t.CPtr = elts.get(i3)
if e3 is None:
continue
es3: str = self._subscript_slice_to_str(pool, e3)
if es3 is None:
continue
if string.strlen(buf3) > 0 and string.strlen(buf3) + 2 < 1024:
string.strcat(buf3, ", ")
if string.strlen(buf3) + string.strlen(es3) < 1024:
string.strcat(buf3, es3)
total3: t.CSizeT = string.strlen(buf3) + 2 + 2
buf4: str = pool.alloc(total3)
if buf4 is None:
return None
buf4[0] = '\0'
string.strcat(buf4, open_ch)
string.strcat(buf4, buf3)
string.strcat(buf4, close_ch)
return buf4
def _is_sha1_prefixed(self, s: str) -> int:
"""判断 s 是否已是 "{16位hex}." 前缀(已规范化,防二次前缀)
@@ -273,6 +362,41 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return ast.BinOp(pool, left_node, ast.OpKind.BitOr, right_node)
return None
# 检查是否包含 [ (Subscript):t.Callable[[t.CInt], t.CInt] 等。
# 必须在 . 检查之前("t.Callable[...]" 含 '.',但本质是 Subscript,
# 若先走 Attribute 会把 "Callable[[t.CInt], t.CInt]" 当 attr → resolve
# 失败)。用 ast.parse_expression 完整解析(支持 List/Tuple 字面量
# slice)。CTypedef 值如 "t.Callable[[t.CInt], t.CInt]"(irq_handler_t)
# 与返回注解 "t.CArray[t.CUInt8T, 16]" 依赖此路径。
bracket_idx: t.CSizeT = seg_len
bi: t.CSizeT = 0
while bi < seg_len:
if type_str[bi] == '[':
bracket_idx = bi
break
bi += 1
if bracket_idx < seg_len:
# 优先 ast.parse_expression(单表达式入口)
expr_node: ast.AST | t.CPtr = ast.parse_expression(type_str, pool)
if expr_node is not None:
expr_body: ast.AST | t.CPtr = expr_node.body
if expr_body is not None:
return expr_body
# 备选:ast.parse 文件级(TPV parser 的 _parse_test 单表达式入口对
# 复杂 slice 如 "t.Callable[[t.CInt], t.CInt]" 解析失败,但文件级
# _parse_expr_stmt 可解析裸表达式语句——vpi 同语法已验证
# parse_tokens 成功,故此处用文件级 parse 兜底)
tree_s: ast.AST | t.CPtr = ast.parse(type_str, pool)
if tree_s is not None:
ch_s: list[ast.AST | t.CPtr] | t.CPtr = tree_s.children
if ch_s is not None and len(ch_s) > 0:
st0: ast.AST | t.CPtr = ch_s.get(0)
if st0 is not None and st0.kind() == ast.ASTKind.Expr:
ex0: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(st0)
if ex0.value is not None:
return ex0.value
return None
# 检查是否包含 . (Attribute):只在有效段 [0, seg_len) 内查找
dot_idx: t.CSizeT = seg_len
di: t.CSizeT = 0
@@ -828,14 +952,28 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
val_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if val_nm2.id is not None and string.strcmp(val_nm2.id, "t") == 0:
return at.attr
# import 别名还原:val_nm2.id 可能是 "import X as Y" 的别名 Y
# (如 "import fat32_types as types" → types.fileinfo 实为
# fat32_types.fileinfo)。还原为模块真名后,AnnotClassName 存
# 真限定名,跨模块同名类查找可经 StubMergerStore 按模块归属解析。
real_mod2: str = val_nm2.id
alias_mod2: str = None
if from_imports is not None:
alias_mod2 = StubMergerStore.LookupModNameByAlias(from_imports, val_nm2.id)
if alias_mod2 is not None:
real_mod2 = alias_mod2
# 返回模块限定名 "module.Class"(如 "memhub.MemBuddy")
mod_qual_buf: str = stdlib.malloc(
string.strlen(val_nm2.id) + string.strlen(at.attr) + 2)
string.strlen(real_mod2) + string.strlen(at.attr) + 2)
if mod_qual_buf is not None:
viperlib.snprintf(mod_qual_buf,
string.strlen(val_nm2.id) + string.strlen(at.attr) + 2,
"%s.%s", val_nm2.id, at.attr)
string.strlen(real_mod2) + string.strlen(at.attr) + 2,
"%s.%s", real_mod2, at.attr)
if alias_mod2 is not None:
stdlib.free(alias_mod2)
return mod_qual_buf
if alias_mod2 is not None:
stdlib.free(alias_mod2)
return at.attr
# BinOp 节点: 联合类型 A | B
@@ -928,7 +1066,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
gen_cls_name: str,
trans: HT.Translator | t.CPtr,
imported_modules: str,
from_imports: str) -> llvmlite.LLVMType | t.CPtr:
from_imports: str,
depth: int = 0) -> llvmlite.LLVMType | t.CPtr:
"""解析泛型类类型注解(特化或查找已注册结构体),失败返回 None"""
if pool is None or sub is None or gen_cls_name is None:
return None
@@ -960,7 +1099,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if tup_elt is None:
continue
tup_ety: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, tup_elt, imported_modules, from_imports, trans)
pool, tup_elt, imported_modules, from_imports, trans, depth + 1)
if tup_ety is None:
return None
tup_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, tup_ety)
@@ -1060,7 +1199,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
node: ast.AST | t.CPtr,
imported_modules: str,
from_imports: str,
trans: HT.Translator | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr:
trans: HT.Translator | t.CPtr = None,
depth: int = 0) -> llvmlite.LLVMType | t.CPtr:
"""解析类型注解节点为 LLVMType
Args:
@@ -1069,9 +1209,16 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
imported_modules: 已导入模块名字符串(空格分隔)
from_imports: from-import 映射字符串("name:module name:module" 格式)
trans: 翻译器对象(可选,用于触发泛型特化)
depth: 递归深度(防御 AST 损坏成自引用环导致的无限递归)
"""
if node is None:
return None
# 递归深度守卫:AST 损坏成自引用环(子节点回指祖先)时,无守卫的
# BinOp/Subscript/Constant/Tuple 递归会无限加深直至栈耗尽(自举版
# TransPyV.exe 编译 FuncTest 时 ASan stack-overflow 首选嫌疑)。超过
# 128 层即 fail-fast 终止(fatal_type_error → sys.exit(1)),不静默兜底。
if depth > 128:
self.Gen.TypeGenErr.fatal_type_error(node, "类型注解递归过深(AST 环或嵌套超过 128 层)")
k: int = node.kind()
@@ -1083,7 +1230,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if cn.const_kind == ast.CONST_STR and cn.str_val is not None:
sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, cn.str_val, ast.ASTCtx.Load)
if sub_node is not None:
return self.resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans)
return self.resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans, depth + 1)
return None
# Name 节点: str, int, bytes, from-import 名称, class 名称等
@@ -1125,7 +1272,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 用具体类型名递归解析(支持 BinOp/Attribute 类型实参)
gp_sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, gp_ta, nm.ctx)
if gp_sub_node is not None:
return self.resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans)
return self.resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans, depth + 1)
# str = bytes = str = i8*
if string.strcmp(nm.id, "str") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
@@ -1189,7 +1336,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if inner_node is not None:
self.Gen.TypeGenCType.CTypedefResolveDepth += 1
inner_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, inner_node, imported_modules, from_imports, trans)
pool, inner_node, imported_modules, from_imports, trans, depth + 1)
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
self.Gen.TypeGenCType._ctypedef_pop_visiting()
if inner_ty is not None:
@@ -1253,9 +1400,35 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
full_attr_ty = self.Gen.StructGen.get_struct_type(full_q)
if full_attr_ty is not None:
return full_attr_ty
mod_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(at.attr)
if mod_struct_ty is not None:
return mod_struct_ty
# 跨模块限定名(Module.Class,Module 非 t/c/SHA1):按模块归属解析,
# 禁止裸名回退抓跨模块同名类(根因:VKernel sched.Thread 被
# includes/vthreading.vp 同名 Thread 顶掉 → 'tid' 字段缺失)。
# 模块名经 StubMergerStore sha1 映射表解析为模块 sha1,再精确命中
# "{sha1}.{Class}"(定义侧命名)或 name+sha1(vpi stub 注入)。
if is_builtin_attr == 0:
# 模块名 → sha1(精确/后缀/import 别名还原,统一经
# StructGenMixin._resolve_module_sha1 逐级尝试)
mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1(
val_name.id, from_imports)
if mod_sha1_v is not None:
full_q_v: str = self._concat_sha1_class(pool, mod_sha1_v, at.attr)
if full_q_v is not None:
full_ty_v: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(full_q_v)
if full_ty_v is not None:
return full_ty_v
mod_entry_v: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(at.attr, mod_sha1_v)
if mod_entry_v is not None and mod_entry_v.Ty is not None:
if mod_entry_v.HasNew != 0:
return llvmlite.Ptr(pool, mod_entry_v.Ty)
return mod_entry_v.Ty
# 模块可解析但类未注册:不裸名回退(防跨库抓错),
# 落到下方枚举/typedef 尝试;struct 缺失由调用侧暴露
# 模块名未登记(未编译/未扫描):同样不裸名回退
else:
# t/c 内置库限定:裸名回退合法(内置类名无跨模块冲突)
mod_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(at.attr)
if mod_struct_ty is not None:
return mod_struct_ty
# 模块限定枚举类型:Module.EnumClass(如 fat32_types.FRESULT,
# t.CEnum 枚举类非结构体,get_struct_type 查不到)
if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1:
@@ -1274,7 +1447,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if mod_td_inner is not None:
self.Gen.TypeGenCType.CTypedefResolveDepth += 1
mod_td_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, mod_td_inner, imported_modules, from_imports, trans)
pool, mod_td_inner, imported_modules, from_imports, trans, depth + 1)
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
self.Gen.TypeGenCType._ctypedef_pop_visiting()
if mod_td_ty is not None:
@@ -1305,7 +1478,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if nested_inner is not None:
self.Gen.TypeGenCType.CTypedefResolveDepth += 1
nested_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, nested_inner, imported_modules, from_imports, trans)
pool, nested_inner, imported_modules, from_imports, trans, depth + 1)
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
self.Gen.TypeGenCType._ctypedef_pop_visiting()
if nested_ty is not None:
@@ -1354,7 +1527,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if other_is_cvoid != 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
other_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, other_node, imported_modules, from_imports, trans)
pool, other_node, imported_modules, from_imports, trans, depth + 1)
# 仅裸名(Name)场景验证解析结果必须是已注册 struct:
# 损坏节点可能解析出基础类型垃圾(如 Int8 → Ptr(i8))。
# Attribute 场景(Module.Class / t.XXX)resolve 结果直接可用
@@ -1444,20 +1617,27 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
cptr_other = bop.left
if cptr_sub_side is not None and cptr_other is not None:
cptr_sub_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, cptr_sub_side, imported_modules, from_imports, trans)
pool, cptr_sub_side, imported_modules, from_imports, trans, depth + 1)
cptr_other_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, cptr_other, imported_modules, from_imports, trans)
pool, cptr_other, imported_modules, from_imports, trans, depth + 1)
if cptr_sub_ty is not None and cptr_other_ty is not None:
# 计算 CPtr 侧外层 Ptr 嵌套数(如 t.CPtr[t.CPtr]=i8** → 2 层)
cptr_level: int = 0
cur_lvl: llvmlite.LLVMType | t.CPtr = cptr_sub_ty
while cur_lvl is not None:
# 注意:不能在 case body 内直接更新 while 条件变量
# (cur_lvl = cur_lvl.Pointee)——自举编译出的 match 会把
# 赋值写到 subject 快照槽,while 条件仍读旧槽 → 死循环
# (gen2 编 FuncTest 卡死根因,gdb 反汇编 +3770-3819 确认)。
# 改法:case 绑定 pointee 到局部变量,match 外更新循环变量。
nxt_lvl: llvmlite.LLVMType | t.CPtr = None
match cur_lvl:
case llvmlite.LLVMType.Ptr(_):
case llvmlite.LLVMType.Ptr(pointee):
cptr_level += 1
cur_lvl = cur_lvl.Pointee
nxt_lvl = pointee
case _:
break
cur_lvl = nxt_lvl
# 结果 = 另一侧类型外包 N 层 Ptr
res_ty: llvmlite.LLVMType | t.CPtr = cptr_other_ty
li2: int = 0
@@ -1468,11 +1648,11 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略)
left_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, bop.left, imported_modules, from_imports, trans)
pool, bop.left, imported_modules, from_imports, trans, depth + 1)
if left_ty is not None:
return left_ty
right_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, bop.right, imported_modules, from_imports, trans)
pool, bop.right, imported_modules, from_imports, trans, depth + 1)
if right_ty is not None:
return right_ty
return None
@@ -1491,7 +1671,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if gen_nm.id is not None:
# 裸名 CArray(from t import * 场景,如 core.vp 的
# `__python_suffix__: CArray[str] = ["py", ...]`):与 t.CArray
# `__python_suffix__: CArray[str] = [...]):与 t.CArray
# 语义等价。否则走泛型模板查找失败(CArray 不是泛型类模板)→
# 字段类型退化 Ptr(i8) → __before_init__ 默认值 List 字面量误走
# 动态 list 构造(list[str].__new__/append 符号未生成 → 链接
@@ -1500,7 +1680,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 单参数: CArray[elem_ty] → Ptr(elem_ty)(静态推导长度数组)
if sub.slice.kind() != ast.ASTKind.Tuple:
elem_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports, trans)
pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
if elem_n is None:
return None
return llvmlite.Ptr(pool, elem_n)
@@ -1513,7 +1693,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if elem_node_n is None or count_node_n is None:
return None
elem_ty_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, elem_node_n, imported_modules, from_imports, trans)
pool, elem_node_n, imported_modules, from_imports, trans, depth + 1)
if elem_ty_n is None:
return None
# count=None → Ptr(elem)(等价单参)
@@ -1527,7 +1707,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return None
return llvmlite.Array(pool, elem_ty_n, count_n)
return self._resolve_generic_subscript_ty(pool, sub, gen_nm.id, trans,
imported_modules, from_imports)
imported_modules, from_imports, depth + 1)
# 检查 value 是否是 Attribute(t.CArray / t.Callable / 模块限定泛型类如 numpy.ndarray[T])
if sub.value.kind() != ast.ASTKind.Attribute:
self.Gen.TypeGenErr.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)")
@@ -1539,7 +1719,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if trans is not None:
if self.Gen.ClassGenGeneric._find_generic_template(arr_at.attr) is not None:
return self._resolve_generic_subscript_ty(pool, sub, arr_at.attr, trans,
imported_modules, from_imports)
imported_modules, from_imports, depth + 1)
if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray / t.Callable 必须通过模块名限定")
arr_val_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arr_at.value)
@@ -1560,7 +1740,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CPtr 模块未导入")
inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports, trans)
pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
if inner_cptr_ty is None:
self.Gen.TypeGenErr.fatal_type_error(sub.slice, "t.CPtr[?] 元素类型解析失败")
return llvmlite.Ptr(pool, inner_cptr_ty)
@@ -1572,7 +1752,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 相当于 t.CArray[elem_ty, None],表示静态推导长度数组
if sub.slice.kind() != ast.ASTKind.Tuple:
elem_ty_single: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports, trans)
pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
if elem_ty_single is None:
self.Gen.TypeGenErr.fatal_type_error(sub.slice, "t.CArray[?] 元素类型解析失败")
return llvmlite.Ptr(pool, elem_ty_single)
@@ -1590,7 +1770,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple 元素为 None")
# 递归解析元素类型
elem_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, elem_node, imported_modules, from_imports, trans)
pool, elem_node, imported_modules, from_imports, trans, depth + 1)
if elem_ty is None:
self.Gen.TypeGenErr.fatal_type_error(elem_node, "t.CArray 元素类型解析失败")
# 解析 count(支持整数常量、None、CDefine 常量名、常量表达式如 2*MAX)
+43
View File
@@ -39,6 +39,10 @@ import string
SCOPE_MODULE: t.CDefine = 0
SCOPE_FUNCTION: t.CDefine = 1
SCOPE_BLOCK: t.CDefine = 2
# match case 作用域(对齐 MatchGenMixin.SCOPE_MATCH;本地定义避免循环依赖)。
# 该作用域仅用于"变体字段绑定隔离",普通赋值语义上属于外层函数作用域,
# get_or_create_sym/_lookup_through_match_scope 会"看穿"它写回外层变量。
SCOPE_MATCH: t.CDefine = 6
# 每个作用域最大变量数
# 256 太小:NewAstTest main() 有 289 个局部变量(几十个测试用例),
@@ -377,6 +381,39 @@ class VarGenMixin:
return 1
return 0
# ============================================================
# _lookup_through_match_scope — 穿越 match case 作用域查找变量
#
# match case 作用域(SCOPE_MATCH)仅用于"变体字段绑定隔离"(case 绑定的
# 模式变量互不泄漏)。case 体内对"外层普通变量"的赋值(如 FuncGenDef
# 参数 match 的 param_ty2 = Ptr(...),或 translate_subscript 内层 case 的
# list_struct_name = lsn)语义上应写回外层 alloca,而非新建 case 局部槽
# ——否则外层变量恒为初始值。自举证据:gen1 编译出的 gen2 其 FuncGenDef
# 参数 match 把赋值写进 case 局部槽、build_alloca 读初始 i32_ty,导致
# 指针参数 alloca 退化为 i32 → FuncTest main.vp:32 `p[0] = p[0] + val`
# 的 translate_subscript 读 i32 非指针返回 None("赋值右侧为 None
# [kind=11]" 根因)。
# 遍历规则:逐级向上查找;SCOPE_MATCH 边界可穿越(可命中外层 case 声明的
# 变量),遇到第一个非 match 作用域(函数/模块)后停止——不跨函数边界
# (闭包 nonlocal 语义由 is_nonlocal_name 单独处理)。
# ============================================================
def _lookup_through_match_scope(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""从当前作用域向上,仅穿越 SCOPE_MATCH 作用域查找变量 alloca"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: llvmlite.Value | t.CPtr = self.find_var(
scope.Vars, scope.VarCount, name)
if result is not None:
return result
if scope.Kind == SCOPE_MATCH:
scope = scope.Parent
continue
return None
return None
# ============================================================
# get_or_create_sym — 查找或创建变量(在当前作用域)
# ============================================================
@@ -387,6 +424,12 @@ class VarGenMixin:
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找或创建变量 alloca(在当前作用域)"""
existing: llvmlite.Value | t.CPtr = self.lookup_current(symtab, name)
if existing is not None:
return existing
# SCOPE_MATCH 隔离作用域对"赋值"必须透明(详见 _lookup_through_match_scope):
# case 体内对外层变量的赋值要写回外层 alloca。仅穿越 match 边界,
# 不跨函数/模块作用域(global/nonlocal 在 AssignGenMixin 提前拦截)。
existing = self._lookup_through_match_scope(symtab, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty)
+16
View File
@@ -49,6 +49,12 @@ MAX_CORO_FUNCS: t.CDefine = 256
MAX_CORO_VARS: t.CDefine = 256
MAX_CORO_TRY: t.CDefine = 16
# 翻译主环最大递归深度(translate_stmt/translate_value 嵌套)。AST 自引用环
# 或翻译 bug 可致翻译主环(语句/表达式/调用)无限递归 → ASan stack-overflow。
# match 守卫(MatchTranslateDepth)只保护 match 递归,本常量保护语句/表达式
# 翻译环(fail-fast,对齐 TypeGenResolve 深度守卫模式)。
TRANSLATE_MAX_DEPTH: t.CDefine = 256
# ============================================================
# 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式)
@@ -150,6 +156,16 @@ class Translator:
# 追加90:控制流 merge 块可达性(1=有 br 进入可达/0=全分支 return 结构性不可达)
_ctrl_merge_reachable: t.CInt
# match 翻译递归深度(match → case body → translate_value → 嵌套 match)。
# 守卫:AST 自引用环(子节点回指祖先)或翻译 bug 导致的无限递归在此
# fail-fast(对齐 TypeGenResolve 深度守卫,防 ASan stack-overflow)。
MatchTranslateDepth: t.CInt
# 翻译主环递归深度(translate_stmt/translate_value 入口计数)。match 守卫
# 只覆盖 match 递归;语句/表达式/调用环(如 translate_value ↔ translate_call)
# 由本字段守卫,超 TRANSLATE_MAX_DEPTH 即 fail-fast(防 ASan stack-overflow)。
TranslateDepth: t.CInt
# global 声明的变量名集合(当前函数内)
_global_names: str
_global_name_count: t.CInt
+3 -2
View File
@@ -277,9 +277,9 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0:
subdir_failed += 1
else:
# 检查是否为 .vp 或 .py 文件(.vp 为 Viper 源,.py 向后兼容)
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
is_py: int = 0
if Utils.is_vp_or_py_at(fname, 0, fname_len) != 0:
if Utils.is_vp_at(fname, 0, fname_len) != 0:
is_py = 1
if is_py != 0:
@@ -314,6 +314,7 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
if fb_sdf is not None:
viperlib.snprintf(fb_sdf, 1024, "子目录扫描失败 %d 个", subdir_failed)
VLogger.error(fb_sdf, "scan")
return 0
+1 -8
View File
@@ -435,18 +435,11 @@ Target: %s
# === 单文件模式:--src 指定时使用指定文件;--project 但无 SourceDir 时回退 ===
if src_path is None and project is not None:
if Config.SourceDir is not None:
# 构造入口路径: 优先 SourceDir/main.vp,回退 SourceDir/main.py
# 构造入口路径: SourceDir/main.vp(不再做 vp -> py 后缀识别回退)
sd_len: t.CSizeT = string.strlen(Config.SourceDir)
path_buf: bytes = stdlib.malloc(sd_len + 16)
if path_buf is not None:
# 优先尝试 main.vp
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.vp", Config.SourceDir)
ef: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
if ef.closed:
# .vp 不存在,回退 main.py
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir)
else:
ef.close()
src_path = path_buf
if log is not None:
fb4: str = VLogger.fmt_buf()
+1 -1
View File
@@ -10,7 +10,7 @@
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000"],
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000", "-Wl,--no-insert-timestamp"],
"output": "TransPyV.exe"
},
"includes": [