From 65d7342d0bd7b2a0cd2aff01f774f3b7f6bcaebf Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 11 Aug 2026 01:33:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(Gen):=20=E8=81=94=E5=90=88=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=95=B0=E7=BB=84=E4=B8=8B=E6=A0=87=E6=8C=89=208=20?= =?UTF-8?q?=E5=AD=97=E8=8A=82=E6=AD=A5=E9=95=BF=EF=BC=88=E5=86=99=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=20+=20CUInt64T|CPtr=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_subscript_ptr 写路径 IsPtrElement 分支 GEP base 误用 alloca(栈地址),list append 只增 count 不写 data(dump 虚调用读 0xbaadf00d 崩溃),改为先 load 指针值再 GEP。另扩展 _is_ptr_element_annotation 识别 CUInt64T|CPtr 等 64 位标量联合,避免 GEP 步长 1 + trunc i8 截断指针(BuddyTest OOM/fill ptrs 数组 free 全部无效)。 --- App/lib/core/Gen/AssignGenMixin.vp | 52 +++++++++++ App/lib/core/Gen/ExprGenMixin.vp | 140 +++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 6 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 377970e..8fabc69 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -356,6 +356,58 @@ class AssignGenMixin: return 1 if self._annotation_contains_name(annot, "bytes") != 0: return 1 + # 泛型类型参数/嵌套联合 | t.CPtr(如 _list.vp 的 elem_ptr: T | t.CPtr): + # T 特化后为具体类型(AST|t.CPtr 等指针),联合在解析时可能退化为 i8*, + # 但元素是 8 字节指针,下标必须按 8 字节步长访问—— + # 否则 list.get/append 只读写 1 字节,指针被截断 inttoptr 崩溃。 + if self._is_generic_tp_or_nested(bop.left) != 0 or self._is_generic_tp_or_nested(bop.right) != 0: + return 1 + # 64 位标量数值类型 | CPtr(如 CUInt64T | CPtr / CSizeT | CPtr): + # 联合解析后同样退化为指针表示,元素为 8 字节(i64 或指针)。 + # 下标必须按 8 字节步长读写——否则 GEP 步长 1 + trunc i8 截断, + # 指针存进数组只剩低 1 字节(BuddyTest OOM/fill 的 ptrs 数组全错位: + # store i8 trunc i64、读回 inttoptr i8 丢高 56 位,free 全部无效)。 + if self._is_64bit_scalar_ty(bop.left) != 0 or self._is_64bit_scalar_ty(bop.right) != 0: + return 1 + return 0 + + def _is_64bit_scalar_ty(self, node: ast.AST | t.CPtr) -> int: + """判断类型节点是否为 64 位标量数值类型(CUInt64T/CInt64T/CSizeT),元素 8 字节""" + if node is None: + return 0 + ty_nm: str = None + nk: int = node.kind() + if nk == ast.ASTKind.Name: + nm64: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + ty_nm = nm64.id + elif nk == ast.ASTKind.Attribute: + at64: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + ty_nm = at64.attr + if ty_nm is None: + return 0 + if string.strcmp(ty_nm, "CUInt64T") == 0: + return 1 + if string.strcmp(ty_nm, "CInt64T") == 0: + return 1 + if string.strcmp(ty_nm, "CSizeT") == 0: + return 1 + return 0 + + def _is_generic_tp_or_nested(self, node: ast.AST | t.CPtr) -> int: + """判断注解节点是否为泛型类型参数名(单字母大写,如 T)或嵌套联合(BinOp)""" + if node is None: + return 0 + nk: int = node.kind() + if nk == ast.ASTKind.BinOp: + return 1 + if nk == ast.ASTKind.Name: + nm_tp: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm_tp.id is None: + return 0 + tp_len: t.CSizeT = string.strlen(nm_tp.id) + if tp_len == 1: + if nm_tp.id[0] >= 65 and nm_tp.id[0] <= 90: + return 1 return 0 # ============================================================ diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 29ca143..aef36c8 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -2291,6 +2291,64 @@ class ExprGenMixin: pool, llvmlite.Int8(pool)) return llvmlite.build_bitcast(builder, fn_ref_v, fn_i8ptr_ty) + # ============================================================ + # 跨模块 from-import 全局变量读取(如 from .tokens import _op_head) + # 根因:模块级全局(非 CDefine/非函数)from-import 后无本模块 SymTab + # 条目,所有查找路径未命中 → translate_name_value 返回 None → 赋值被 + # 静默跳过 → 变量未初始化 → 垃圾指针解引用崩溃(AstTest 的 + # _read_operator 里 `cur = _op_head` 丢失导致 while 循环读未初始化 %6)。 + # 语义对齐模块属性全局(sys._mbuddy):external @{全局名} 引用 + load, + # 定义由源模块提供(COFF 按名链接)。别名导入用源模块内真实名。 + # ============================================================ + if trans is not None and trans._from_imports is not None: + if trans.Module is not None: + # 是 from-import 名称(禁用 star 回退,避免误归 star 模块) + fi_mod: str = HandlesImports.lookup_from_import( + trans._from_imports, nm_id, 0) + if fi_mod is not None: + # 排除已注册类名/枚举名:这些是类型,不作为全局变量值读取 + fi_is_class: int = 0 + if HandlesStruct.get_struct_type(nm_id) is not None: + fi_is_class = 1 + # 排除 ALL_CAPS CDefine(上方已处理本地+跨模块查找) + if self._is_cdefine_name_pattern(nm_id) != 0: + fi_is_class = 1 + if fi_is_class == 0: + fi_gname: str = nm_id + fi_orig: str = HandlesImports.lookup_from_import_original( + trans._from_imports, nm_id) + if fi_orig is not None: + og_len_fi: t.CSizeT = 0 + while fi_orig[og_len_fi] != ' ' and fi_orig[og_len_fi] != '\0': + og_len_fi += 1 + if og_len_fi > 0: + og_buf_fi: t.CChar | t.CPtr = pool.alloc(og_len_fi + 1) + if og_buf_fi is not None: + string.strncpy(og_buf_fi, fi_orig, og_len_fi) + og_buf_fi[og_len_fi] = '\0' + fi_gname = og_buf_fi + # stub 已注入全局(精确类型)→ 直接引用;未注入 → external 声明 + gv_fi: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module( + trans.Module, fi_gname) + ref_fi: llvmlite.Value | t.CPtr = None + if gv_fi is not None and gv_fi.Ty is not None: + ref_fi = self.make_global_ref(pool, fi_gname, gv_fi.Ty) + else: + i8_ptr_hint_fi: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + ext_gv_fi: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable( + pool, fi_gname, i8_ptr_hint_fi) + if ext_gv_fi is not None: + ext_gv_fi.Linkage = "external" + ext_gv_fi.Initializer = None + llvmlite.module_add_global(trans.Module, ext_gv_fi) + ref_fi = self.make_global_ref(pool, fi_gname, i8_ptr_hint_fi) + if ref_fi is not None and ref_fi.Ty is not None: + lty_fi: llvmlite.LLVMType | t.CPtr = ref_fi.Ty.Pointee + if lty_fi is not None: + return llvmlite.build_load(builder, lty_fi, ref_fi) + return ref_fi + # 所有查找路径均失败 # fail-fast: 仅对符合 CDefine 命名约定(ALL_CAPS)的名字报错 # 避免对 match/self/pool 等关键字和变量名误报 @@ -2609,10 +2667,18 @@ class ExprGenMixin: ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \ HandlesVar.lookup_var_entry(trans.SymTab, nm.id) if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1: - elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep( - builder, pointee, alloca, idx_val) - if elem_ptr_ts is not None: - return llvmlite.build_load(builder, pointee, elem_ptr_ts) + # bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值(i8*), + # elem[i] 语义 = load [指针值 + i*8](8 字节指针元素)。 + # 必须先 load alloca 得指针值,再 GEP i8*(步长 8)—— + # 否则 GEP base 用 alloca(&elem_ptr)读到栈变量而非数据区, + # list.get 返回"元素地址"而非"元素值"(dump 虚调用读错 vtable)。 + ptr_val_pe_ts: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if ptr_val_pe_ts is not None: + elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, pointee, ptr_val_pe_ts, idx_val) + if elem_ptr_ts is not None: + return llvmlite.build_load(builder, pointee, elem_ptr_ts) return None # 普通指针遍历: 先 load 指针值,再 GEP ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load( @@ -2624,6 +2690,25 @@ class ExprGenMixin: return llvmlite.build_load(builder, inner_ty, elem_ptr2) return None case _: + # 联合类型变量(IsPtrElement=1,如 _list.vp 的 + # elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但 + # 元素是 8 字节指针,须按 i8* 步长 GEP + load i8*—— + # 否则 list.get 只读 1 字节,指针截断 inttoptr 崩溃。 + if trans is not None: + ve_pe_rd: HandlesVar.VarEntry | t.CPtr = \ + HandlesVar.lookup_var_entry(trans.SymTab, nm.id) + if ve_pe_rd is not None and ve_pe_rd.IsPtrElement == 1: + match pointee: + case llvmlite.LLVMType.Int8(): + i8_ptr_rd: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + elem_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ptr_rd, alloca, idx_val) + if elem_ptr_rd is not None: + return llvmlite.build_load( + builder, i8_ptr_rd, elem_ptr_rd) + case _: + pass # 自定义类(结构体类型): 仅当类定义了 __getitem__ # (当前模块定义或跨模块 stub declare 注入)才转发方法调用; # 否则普通结构体指针按下标做 GEP 数组访问 @@ -3454,8 +3539,25 @@ class ExprGenMixin: ve_pe: HandlesVar.VarEntry | t.CPtr = \ HandlesVar.lookup_var_entry(trans.SymTab, nm.id) if ve_pe is not None and ve_pe.IsPtrElement == 1: - return llvmlite.build_gep( - builder, pointee, alloca, idx_val) + # bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值, + # 下标语义 = [指针值 + i*8](8 字节指针元素)。必须先 load + # alloca 得指针值,再 GEP——否则 GEP base 用 alloca(&elem_ptr) + # 读到栈变量而非数据区,赋值写不进 list 数据 + # (data 全 0xbaadf00d,dump 虚调用读错 vtable 崩溃)。 + ptr_val_pe: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if ptr_val_pe is not None: + match pointee: + case llvmlite.LLVMType.Int8(): + i8_ptr_pe: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + return llvmlite.build_gep( + builder, i8_ptr_pe, ptr_val_pe, idx_val) + case _: + pass + return llvmlite.build_gep( + builder, pointee, ptr_val_pe, idx_val) + return None # 普通指针遍历: 先 load 指针值,再 GEP ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, pointee, alloca) @@ -3468,6 +3570,32 @@ class ExprGenMixin: # pointee 是普通标量类型 (如 i64, i32, i8): # alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针 # 支持 arg_vals[i] = val 这类参数数组下标赋值 + # 联合类型变量(IsPtrElement=1,如 _list.vp 的 + # elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但 + # 元素是 8 字节指针,须按 i8* 步长 GEP(防只写 1 字节截断) + if trans is not None: + ve_pe_cs: HandlesVar.VarEntry | t.CPtr = \ + HandlesVar.lookup_var_entry(trans.SymTab, nm.id) + if ve_pe_cs is not None and ve_pe_cs.IsPtrElement == 1: + # T|t.CPtr(退化为 i8*,如 _list.vp 的 elem_ptr)变量 + # 存数据区指针,元素为 8 字节指针:必须先 load alloca + # 得指针值,再按 i8* 步长 GEP——否则 GEP base 用 alloca + # (&elem_ptr)算到栈地址,赋值写不进 list 数据区 + # (append 只增 count、data 全 0xbaadf00d)。 + ptr_val_cs: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if ptr_val_cs is not None: + match pointee: + case llvmlite.LLVMType.Int8(): + i8_ptr_cs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, llvmlite.Int8(pool)) + return llvmlite.build_gep( + builder, i8_ptr_cs, ptr_val_cs, idx_val) + case _: + pass + return llvmlite.build_gep( + builder, pointee, ptr_val_cs, idx_val) + return None return llvmlite.build_gep(builder, pointee, alloca, idx_val) # 通用路径