fix(tpv): vpi 注入完整类型定义 + list elem_size + 路径修复,清理临时诊断

- VpiReader: 值 struct 全局/数组元素/函数返回类型注册完整定义,修复 llc use of undefined type;vpi 注入类后补 add_field 字段表(LARGE_INTEGER.QuadPart 恒 0 根因)
- TypeGenResolve: list[str](pool, elem_size) 语义修复(参数个数误当 elem_size,Mat4 崩溃根因);t/c 内置命名空间 Subscript 特判
- Config: _join_path 防双份路径
- GenApi: 新增 add_field 代理
- 移除 DBG-VK/PHASEADBG/GAP-DIAG/DBG-EMP 临时诊断
This commit is contained in:
TermiNexus committed 2026-08-29 17:27:29 +08:00
1 parent 1fe7878074
commit c2b9a26c62
7 files changed
+103 -41

No files matched your search

+8
View File
@@ -245,6 +245,14 @@ def _join_path(rel: str, project_dir: str) -> str:
# 绝对路径(以 / 或 \ 开头)保持不变 # 绝对路径(以 / 或 \ 开头)保持不变
if rel[0] == '/' or rel[0] == '\\': if rel[0] == '/' or rel[0] == '\\':
return rel return rel
# [FIX] 命令行 --build-dir 按进程 cwd 给出的路径(如 ./TransPyV/.tpv_build2)
# 已含 project_dir 前缀(./TransPyV)时不再拼接,避免双份路径
# (./TransPyV/TransPyV/.tpv_build2)。边界取到目录分隔符或串尾。
if project_dir is not None:
pdl: t.CSizeT = string.strlen(project_dir)
if pdl > 0 and string.strncmp(rel, project_dir, pdl) == 0:
if rel[pdl] == '/' or rel[pdl] == '\\' or rel[pdl] == '\0':
return rel
rel_len: t.CSizeT = string.strlen(rel) rel_len: t.CSizeT = string.strlen(rel)
dir_len: t.CSizeT = string.strlen(project_dir) dir_len: t.CSizeT = string.strlen(project_dir)
+48
View File
@@ -1695,10 +1695,51 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy | t.CPtr,
pool, cls_name, struct_ty_c, src_sha1) pool, cls_name, struct_ty_c, src_sha1)
if entry_c is None: if entry_c is None:
return 0 return 0
# [FIX] vpi 注入类时同步注册字段到 StructEntry 字段表(名字+类型)。
# 仅设置 Ty(LLVMType 含字段)而字段表空(FieldCount=0)时,使用端
# lookup_field / lookup_field_by_class 按名字查字段失败 → 属性访问走
# _deps_pending → Phase 1 includes 翻译无重译 → 失败版本落盘恒 0
# (fileio.vp File.size 的 li.QuadPart → ret i64 zeroinitializer →
# read_all=0 → 二代 Config 加载失败根因)。
# add_field 内部按 StdlibAlloc 复制字段名到 perm_pool,跨 reset 存活。
afi_c: int = 0
for afi_c in range(field_count):
af_name_c: str = (str)(t.CVoid(field_names_buf[afi_c], t.CPtr))
af_ty_c: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(
t.CVoid(field_types_buf[afi_c], t.CPtr))
GenApi.add_field(pool, entry_c, af_name_c, af_ty_c)
GenApi.add_visible_struct(pool, cls_name) GenApi.add_visible_struct(pool, cls_name)
return 1 return 1
# ============================================================
# _ensure_full_value_types - 跨模块"值类型"引用注册完整定义
#
# opaque 不足以支撑 llc:值 struct global(@_per_cpu = global %per_cpu_data)、
# [N x opaque] 数组(@gdt = global [7 x %gdt_entry])、值 struct 返回类型
# (declare %Mat4 @Mat4_Identity())均报 "use of undefined type"(值类型大小
# 需已知)。vpi 注入类已 add_field 含字段(完整 Ty),在注册表可查时注册完整
# 定义。指针引用(Ptr(Struct))无需——_scan_type_for_cross_module_refs 的 Ptr
# 分支添加 opaque 即可。
# ============================================================
def _ensure_full_value_types(mod: llvmlite.LLVMModule | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> None:
"""递归为值 struct / 数组元素 struct 注册完整定义(递归深度守卫)"""
if mod is None or pool is None or ty is None:
return
match ty:
case llvmlite.LLVMType.Struct(_, _, _):
full_se_h: GenTypes.StructEntry | t.CPtr = GenApi.find_struct_by_type(ty)
if full_se_h is not None and full_se_h.Ty is not None:
llvmlite.module_add_named_type(mod, pool, full_se_h.Ty)
case llvmlite.LLVMType.Array(aelem_h, _):
if aelem_h is not None:
_ensure_full_value_types(mod, pool, aelem_h)
case _:
pass
# ============================================================ # ============================================================
# _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare # _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare
# #
@@ -1792,6 +1833,8 @@ def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr,
# undefined type named 'sha1.gdt_entry'"(fpu/fat32 合并 IR 根因)。 # undefined type named 'sha1.gdt_entry'"(fpu/fat32 合并 IR 根因)。
# 对齐 TranslatorGenMixin 创建全局前的 module_ensure_opaque_for_type # 对齐 TranslatorGenMixin 创建全局前的 module_ensure_opaque_for_type
# (_scan_type_for_cross_module_refs 递归 Array/Struct/Ptr)。 # (_scan_type_for_cross_module_refs 递归 Array/Struct/Ptr)。
# 值类型(struct 值 / 数组元素 struct)注册完整定义(见 _ensure_full_value_types)
_ensure_full_value_types(mod, pool, gv_ty_v)
llvmlite.module_ensure_opaque_for_type(mod, pool, gv_ty_v) llvmlite.module_ensure_opaque_for_type(mod, pool, gv_ty_v)
gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name_m, gv_ty_v) gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name_m, gv_ty_v)
if gv_v is None: if gv_v is None:
@@ -1889,6 +1932,11 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
if ret_ty is None: if ret_ty is None:
ret_ty = i32_ty ret_ty = i32_ty
# 值 struct 返回类型(如 gfx.vp Mat4_Identity -> Mat4)需要完整定义:
# declare %Mat4 opaque → llc "use of undefined type"。注册完整定义。
_ensure_full_value_types(mod, pool, ret_ty)
llvmlite.module_ensure_opaque_for_type(mod, pool, ret_ty)
# 创建 declare # 创建 declare
func: llvmlite.Function | t.CPtr = llvmlite.create_declare( func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, mangled_name, ret_ty) pool, mod, mangled_name, ret_ty)
-10
View File
@@ -5,7 +5,6 @@ import ast
import llvmlite import llvmlite
import memhub import memhub
import string import string
import stdio
import viperlib import viperlib
import lib.core.VLogger as VLogger import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT import lib.core.Gen.Translator as HT
@@ -87,7 +86,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
if existing is not None: if existing is not None:
if existing.FieldCount == 0 and trans._declare_only == 0: if existing.FieldCount == 0 and trans._declare_only == 0:
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0: if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
VLogger.info(f"[DBG-EMP] class={class_name} FC=0 ty={t.CUInt64T(existing.Ty)} 类体有字段,中和重建", "DBG")
if existing.Name is not None: if existing.Name is not None:
existing.Name[0] = '\0' existing.Name[0] = '\0'
existing.Ty = None existing.Ty = None
@@ -230,8 +228,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。 # L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。
if trans is not None: if trans is not None:
trans._deps_pending = 1 trans._deps_pending = 1
# [PHASEADBG] 临时诊断:父类未注册即 pending 的根因(用后移除)
stdio.printf("[PHASEADBG] 父类未注册: class=%s parent=%s\n", class_name, pname)
return 0 return 0
pfc: int = parent_entry.FieldCount pfc: int = parent_entry.FieldCount
for pfi in range(pfc): for pfi in range(pfc):
@@ -366,12 +362,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# 翻译: <file>,请检查依赖模块是否已编译/注册"),不静默掩盖。 # 翻译: <file>,请检查依赖模块是否已编译/注册"),不静默掩盖。
if trans is not None: if trans is not None:
trans._deps_pending = 1 trans._deps_pending = 1
# [PHASEADBG] 临时诊断:字段注解类型未解析即 pending 的根因(用后移除)
dbg_ak: t.CInt = -1
if aa.annotation is not None:
dbg_ak = aa.annotation.kind()
stdio.printf("[PHASEADBG] 字段类型未解析: class=%s field=%s annot_kind=%d\n",
class_name, tgt.id, dbg_ak)
return 0 return 0
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时 # 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底) # 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
-12
View File
@@ -807,18 +807,6 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
# 查找字段索引和类型 # 查找字段索引和类型
field_info: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( field_info: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
struct_ty, at.attr) struct_ty, at.attr)
# [GAP-DIAG] 定位 th.arg=arg 字段索引错(func/arg 都解析成 3)根因:
# 打印字段名/结构体类型名/查到的索引与实际字段名
if field_info is not None \
and (string.strcmp(at.attr, "arg") == 0 or string.strcmp(at.attr, "func") == 0):
dbg_gap: str = pool.alloc(200)
if dbg_gap is not None:
ty_nm_gap: str = self.Gen.StructGen._extract_struct_name(struct_ty)
if ty_nm_gap is None:
ty_nm_gap = "?"
viperlib.snprintf(dbg_gap, 200, "[GAP] attr=%s ty=%s idx=%d fe_name=%s\n",
at.attr, ty_nm_gap, field_info.Index, field_info.Name)
stdio.printf("%s", dbg_gap)
# REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名 # REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名
# REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee) # REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee)
if field_info is None: if field_info is None:
+14 -1
View File
@@ -398,7 +398,20 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr,
return None return None
# ---------------- register_struct_alias(委托 StructGenMixin.register_struct_alias) ---------------- # ---------------- add_field(委托 StructGenMixin.add_field) ----------------
def add_field(pool: memhub.MemBuddy | t.CPtr,
struct_entry: GenTypes.StructEntry | t.CPtr,
field_name: str,
field_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""委托到 gen.add_field(原 Handles 壳)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.StructGen.add_field(pool, struct_entry, field_name, field_ty)
return -1
# ---------------- register_struct_alias(委托 StructGenMixin.register_struct_alias) ----------------
def register_struct_alias(pool: memhub.MemBuddy | t.CPtr, def register_struct_alias(pool: memhub.MemBuddy | t.CPtr,
name: str, name: str,
sha1: str, sha1: str,
-13
View File
@@ -221,14 +221,6 @@ class StructGenMixin:
entry.Ty = struct_ty entry.Ty = struct_ty
entry.FieldCount = 0 entry.FieldCount = 0
# [DBG-EMP] 临时诊断:定位空类注册来源(win32base SECURITY_ATTRIBUTES /
# win32file WIN32_FIND_DATAA FieldCount=0)
if name is not None and (string.strstr(name, "SECURITY_ATTRIBUTES") is not None
or string.strstr(name, "WIN32_FIND_DATAA") is not None):
sha1_s: str = "?"
if sha1 is not None:
sha1_s = sha1
VLogger.info(f"[DBG-REG] {name} sha1={sha1_s} Ty={t.CUInt64T(struct_ty)}", "DBG")
# 注册时直接设置 SHA1(避免 set_struct_sha1 跨模块同名时找错 entry) # 注册时直接设置 SHA1(避免 set_struct_sha1 跨模块同名时找错 entry)
# SHA1 同样按 StdlibAlloc 选分配源(跨 1a/1b 存活,防 strcmp 悬空) # SHA1 同样按 StdlibAlloc 选分配源(跨 1a/1b 存活,防 strcmp 悬空)
@@ -359,11 +351,6 @@ class StructGenMixin:
return -1 return -1
idx: int = struct_entry.FieldCount idx: int = struct_entry.FieldCount
# [DBG-EMP] 临时诊断:定位空类字段添加(win32base SECURITY_ATTRIBUTES /
# win32file WIN32_FIND_DATAA FieldCount=0)
if struct_entry.Name is not None and (string.strstr(struct_entry.Name, "SECURITY_ATTRIBUTES") is not None
or string.strstr(struct_entry.Name, "WIN32_FIND_DATAA") is not None):
VLogger.info(f"[DBG-ADDF] {struct_entry.Name} field={field_name} idx={idx}", "DBG")
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(struct_entry, idx) fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(struct_entry, idx)
if fe is None: if fe is None:
return -1 return -1
+33 -5
View File
@@ -230,6 +230,21 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
""" """
if type_name is None: if type_name is None:
return None return None
# [FIX] 类型名指针越界(垃圾指针,ViperOS Mat4.m 嵌套泛型崩溃现场
# strlen 收到 0x12124AE128712850 > x64 用户空间上限 2^47;低 16 位
# 0x2850 残留 pool 偏移 → pool 越界写/寄存器破坏)。解引用前 fail-fast
# 暴露根因(不静默 strlen 段错误)。
if t.CUInt64T(type_name) > 0x00007FFFFFFFFFFF:
VLogger.error(f"_norm_type_arg_name 类型名指针越界 (ptr={t.CUInt64T(type_name)}),疑似 pool 越界写破坏类型名指针", "TypeGen")
stdlib.exit(1)
# [FIX] 泛型实参分割(L341 "float, 4" → " 4")第二个参数带前导空格,
# 空格开头的字符串作为类型名/查表 key 是脏数据(ViperOS Mat4.m 的
# t.CArray[t.CArray[float, 4], 4] 崩溃现场:_norm_type_arg_name 收到
# " 4")。跳过前导空格后再按规则规范化。
while type_name[0] == ' ':
type_name += 1
if type_name[0] == '\0':
return type_name
# ---- 1. 顶层联合 "A|B"('|' 仅在括号外才分割)---- # ---- 1. 顶层联合 "A|B"('|' 仅在括号外才分割)----
# 括号感知:list[A|B] 的 '|' 在 '[' 内不是顶层联合,应留给第 1.5 步 # 括号感知:list[A|B] 的 '|' 在 '[' 内不是顶层联合,应留给第 1.5 步
# 泛型 slice 递归处理;list[A]|t.CPtr 的 '|' 在括号外才是联合分割。 # 泛型 slice 递归处理;list[A]|t.CPtr 的 '|' 在括号外才是联合分割。
@@ -316,7 +331,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
elif inner_buf_g[ci_g] == ',' and cur_dep_g == 0: elif inner_buf_g[ci_g] == ',' and cur_dep_g == 0:
arg_cnt_g += 1 arg_cnt_g += 1
ci_g += 1 ci_g += 1
arg_list_g: list[str] | t.CPtr = list[str](pool, arg_cnt_g) # [FIX] 第二参数是 elem_size(元素字节大小),不是参数个数!
# str 是 i8*(8 字节)。此前误传 arg_cnt_g(参数个数,如 Mat4
# 的 2)→ __elem_size__=2 → append 只写指针低 2 字节(高 48 位
# 残留)→ get 读回垃圾指针(低 16 位=pool 偏移、高 48 位=相邻
# 数据)→ _norm_type_arg_name 递归 strlen 崩(ViperOS Mat4.m
# t.CArray[t.CArray[float, 4], 4] 崩溃根因:strlen 参数三次
# 均为低 16 位 0x2850、高 48 位漂移)。
arg_list_g: list[str] | t.CPtr = list[str](pool, 8)
if arg_list_g is not None: if arg_list_g is not None:
arg_start_g: t.CSizeT = 0 arg_start_g: t.CSizeT = 0
cur_dep_g = 0 cur_dep_g = 0
@@ -1092,7 +1114,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
tp_elts = tp_tup.elts tp_elts = tp_tup.elts
tp_count = len(tp_elts) tp_count = len(tp_elts)
else: else:
tp_single: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 1) # [FIX] elem_size=8(ast.AST | t.CPtr 指针 8 字节),原传 1 → get 只读
# 1 字节拼指针(高 56 位残留)→ tuple 元素解析垃圾
tp_single: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8)
if tp_single is not None: if tp_single is not None:
tp_single.append(sub.slice) tp_single.append(sub.slice)
tp_elts = tp_single tp_elts = tp_single
@@ -1791,7 +1815,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 函数指针类型: t.Callable[[A, B], Ret] → i8* # 函数指针类型: t.Callable[[A, B], Ret] → i8*
# 与 TPV 函数传指针惯例一致(函数值 bitcast 为 i8* 存储,调用时按实参重建签名) # 与 TPV 函数传指针惯例一致(函数值 bitcast 为 i8* 存储,调用时按实参重建签名)
if string.strcmp(arr_at.attr, "Callable") == 0: if string.strcmp(arr_at.attr, "Callable") == 0:
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: # t/c 是内置命名空间(不编译、不入 imported_modules),无需显式导入检查
if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 \
and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.Callable 模块未导入") self.Gen.TypeGenErr.fatal_type_error(node, "t.Callable 模块未导入")
return llvmlite.Ptr(pool, llvmlite.Int8(pool)) return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 指针类型: t.CPtr[T] → Ptr(T)(指向 T 的指针,T 自身可含指针层级) # 指针类型: t.CPtr[T] → Ptr(T)(指向 T 的指针,T 自身可含指针层级)
@@ -1800,7 +1826,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 此前缺失 → t.CUInt8T | t.CPtr[t.CPtr] 联合退化为左值 i8 → # 此前缺失 → t.CUInt8T | t.CPtr[t.CPtr] 联合退化为左值 i8 →
# c.Deref(out) 报"参数不是指针类型"(zdeflate.vp compress out 根因) # c.Deref(out) 报"参数不是指针类型"(zdeflate.vp compress out 根因)
if string.strcmp(arr_at.attr, "CPtr") == 0: if string.strcmp(arr_at.attr, "CPtr") == 0:
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 \
and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CPtr 模块未导入") self.Gen.TypeGenErr.fatal_type_error(node, "t.CPtr 模块未导入")
inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports, trans, depth + 1) pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
@@ -1817,7 +1844,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return llvmlite.Ptr(pool, inner_cptr_ty) return llvmlite.Ptr(pool, inner_cptr_ty)
if string.strcmp(arr_at.attr, "CArray") != 0: if string.strcmp(arr_at.attr, "CArray") != 0:
self.Gen.TypeGenErr.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") self.Gen.TypeGenErr.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)")
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 \
and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray 模块未导入") self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray 模块未导入")
# 单参数形式: t.CArray[elem_ty] → Ptr(elem_ty) # 单参数形式: t.CArray[elem_ty] → Ptr(elem_ty)
# 相当于 t.CArray[elem_ty, None],表示静态推导长度数组 # 相当于 t.CArray[elem_ty, None],表示静态推导长度数组