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
+102 -40

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] == '\\':
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)
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)
if entry_c is None:
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)
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
#
@@ -1792,6 +1833,8 @@ def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr,
# undefined type named 'sha1.gdt_entry'"(fpu/fat32 合并 IR 根因)。
# 对齐 TranslatorGenMixin 创建全局前的 module_ensure_opaque_for_type
# (_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)
gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name_m, gv_ty_v)
if gv_v is None:
@@ -1889,6 +1932,11 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
if ret_ty is None:
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
func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, mangled_name, ret_ty)
-10
View File
@@ -5,7 +5,6 @@ import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
@@ -87,7 +86,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
if existing is not None:
if existing.FieldCount == 0 and trans._declare_only == 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:
existing.Name[0] = '\0'
existing.Ty = None
@@ -230,8 +228,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。
if trans is not None:
trans._deps_pending = 1
# [PHASEADBG] 临时诊断:父类未注册即 pending 的根因(用后移除)
stdio.printf("[PHASEADBG] 父类未注册: class=%s parent=%s\n", class_name, pname)
return 0
pfc: int = parent_entry.FieldCount
for pfi in range(pfc):
@@ -366,12 +362,6 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# 翻译: <file>,请检查依赖模块是否已编译/注册"),不静默掩盖。
if trans is not None:
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
# 旧行为: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(
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 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee)
if field_info is None:
+13
View File
@@ -398,6 +398,19 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr,
return None
# ---------------- 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,
name: str,
-13
View File
@@ -221,14 +221,6 @@ class StructGenMixin:
entry.Ty = struct_ty
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 同样按 StdlibAlloc 选分配源(跨 1a/1b 存活,防 strcmp 悬空)
@@ -359,11 +351,6 @@ class StructGenMixin:
return -1
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)
if fe is None:
return -1
+33 -5
View File
@@ -230,6 +230,21 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
"""
if type_name is 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"('|' 仅在括号外才分割)----
# 括号感知:list[A|B] 的 '|' 在 '[' 内不是顶层联合,应留给第 1.5 步
# 泛型 slice 递归处理;list[A]|t.CPtr 的 '|' 在括号外才是联合分割。
@@ -316,7 +331,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
elif inner_buf_g[ci_g] == ',' and cur_dep_g == 0:
arg_cnt_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:
arg_start_g: t.CSizeT = 0
cur_dep_g = 0
@@ -1092,7 +1114,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
tp_elts = tp_tup.elts
tp_count = len(tp_elts)
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:
tp_single.append(sub.slice)
tp_elts = tp_single
@@ -1791,7 +1815,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 函数指针类型: t.Callable[[A, B], Ret] → i8*
# 与 TPV 函数传指针惯例一致(函数值 bitcast 为 i8* 存储,调用时按实参重建签名)
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 模块未导入")
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 指针类型: t.CPtr[T] → Ptr(T)(指向 T 的指针,T 自身可含指针层级)
@@ -1800,7 +1826,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 此前缺失 → t.CUInt8T | t.CPtr[t.CPtr] 联合退化为左值 i8 →
# c.Deref(out) 报"参数不是指针类型"(zdeflate.vp compress out 根因)
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 模块未导入")
inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
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)
if string.strcmp(arr_at.attr, "CArray") != 0:
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 模块未导入")
# 单参数形式: t.CArray[elem_ty] → Ptr(elem_ty)
# 相当于 t.CArray[elem_ty, None],表示静态推导长度数组