from __future__ import annotations from typing import Any import re import vast as ast import llvmlite.ir as ir from llvmlite.ir.values import _Undefined from lib.core import TypeSystem as _t from lib.core.VLogger import get_logger as _vlog # vtable 槽类型 `i8*` 的共享实例:llvmlite 的类型是不可变值语义(`_TypedPointerType` # 的 `__eq__` 按结构比较、打印结果只取决于结构),同一实例可在任意多少量复用。 # 原来在 `_generate_structs` 的逐类 `member_types` 构建里**每类每次现建** # (profile:`_TypedPointerType.__init__` 3,750,259 次中有一份来自这里)。 _I8_PTR: ir.Type = ir.PointerType(ir.IntType(8)) # `_strip_unnecessary_quotes` 识别 LLVM IR 裸标识符 / 给引用名去引号用的**预编译**正则 # (性能:该函数对整段 IR 文本做替换,字面量正则预编译避免 re 缓存查找与编译开销)。 _RE_IR_IDENTIFIER: re.Pattern[str] = re.compile(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$') _RE_QUOTED_IR_NAME: re.Pattern[str] = re.compile(r'([@%])"([^"]+)"') class StructGenMixin: def _get_align(self, llvm_type: ir.Type) -> int: """类型自然对齐(字节)。 与 `_get_struct_size` 同一约定:**不允许静默近似**。对齐猜错会让字段字节偏移 与 LLVM 实际布局不一致(本函数是 `_get_struct_size` 的字段偏移累加与 `_compute_field_byte_offset` 的输入),故拿不到对齐时一律响亮失败: - opaque/未解析的 IdentifiedStructType → 曾按 ptr_size 猜; - 未识别类型 → 曾按 4 猜。 """ if isinstance(llvm_type, ir.IntType): return max(1, llvm_type.width // 8) if isinstance(llvm_type, ir.FloatType): return 4 if isinstance(llvm_type, ir.DoubleType): return 8 if isinstance(llvm_type, ir.PointerType): return self.ptr_size if isinstance(llvm_type, ir.ArrayType): return self._get_align(llvm_type.element) if isinstance(llvm_type, ir.VectorType): # 与 LLVM 一致:向量对齐 = 元素对齐(同 _get_struct_size 的向量口径) return self._get_align(llvm_type.element) if isinstance(llvm_type, ir.LiteralStructType): return max((self._get_align(f) for f in llvm_type.elements), default=1) if isinstance(llvm_type, ir.IdentifiedStructType): resolved = self._resolve_opaque_struct(llvm_type) if resolved is not llvm_type: return self._get_align(resolved) if llvm_type.elements is None or len(llvm_type.elements) == 0: raise ValueError( f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' " f"布局未就绪(opaque / 未解析到定义),拒绝按指针对齐兜底 —— " f"对齐猜错会让字段字节偏移与 LLVM 实际布局不一致(读写落到错误字段)。" f"请先确保该结构体已注册/可从 stub 加载后再取对齐。" ) return max((self._get_align(f) for f in llvm_type.elements), default=1) if isinstance(llvm_type, ir.VoidType): return 0 raise ValueError( f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})" f"无法计算对齐,拒绝以常数 4 兜底 —— 对齐猜错会让字段字节偏移与 LLVM 实际布局不一致。") def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type: """尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题""" if not isinstance(llvm_type, ir.IdentifiedStructType): return llvm_type if llvm_type.elements is not None and len(llvm_type.elements) > 0: return llvm_type # opaque struct,尝试通过名称在 Gen.structs 中查找已解析的版本 st_name: str try: st_name = llvm_type.name except Exception: return llvm_type if not st_name: return llvm_type # 提取短名称 short_name: str = st_name if '.' in st_name: short_name = st_name.split('.', 1)[1] # 尝试短名称查找 if short_name in self.structs: resolved: ir.IdentifiedStructType = self.structs[short_name] if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: return resolved # 尝试全名查找 if st_name in self.structs: resolved: ir.IdentifiedStructType = self.structs[st_name] if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: return resolved # 尝试通过导入处理器从 stub 文件加载 struct 定义 import_handler: Any = self._import_handler_ref if import_handler is not None: try: import_handler._TryLoadStructFromStub(short_name, self) except Exception: pass # 加载后再次查找 if short_name in self.structs: resolved = self.structs[short_name] if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: return resolved if st_name in self.structs: resolved = self.structs[st_name] if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: return resolved return llvm_type def _get_struct_size(self, llvm_type: ir.Type) -> int: """类型字节尺寸(含字段对齐/填充)。 **不允许静默近似**:本函数的返回值会被 `X.__sizeof__()` 直接采用 (HandlesExprCall._HandleSizeofLlvm),而 memhub 的 alloc 又直接吃该值。 此前两处静默兜底会**少算尺寸**,进而按过小尺寸分配 → 越界写坏邻居块的 8 字节块头(块头 `(order<<1)|1` 被写成偶数 ⇒ reset_to 视为"已释放"跳过、 free 被 _is_valid_ptr 判无效而静默 no-op)⇒ 该块永久丢失 ⇒ 1 GiB 主池漏满: - opaque/未解析的 IdentifiedStructType → 曾一律返回 ptr_size; - 未识别类型(如 SIMD VectorType)→ 曾一律返回 4。 故尺寸拿不到时一律响亮失败(项目约定:类型信息缺失立即终止,不兜底)。 """ if isinstance(llvm_type, ir.IntType): return max(1, llvm_type.width // 8) if isinstance(llvm_type, ir.FloatType): return 4 if isinstance(llvm_type, ir.DoubleType): return 8 if isinstance(llvm_type, ir.PointerType): return self.ptr_size if isinstance(llvm_type, ir.ArrayType): return self._get_struct_size(llvm_type.element) * llvm_type.count if isinstance(llvm_type, ir.VectorType): # 与 LLVM 一致:元素尺寸 × 元素数,再向上取到 2 的幂(<3 x i32> = 16 字节) elem_size: int = self._get_struct_size(llvm_type.element) lanes: int = llvm_type.count padded: int = 1 while padded < lanes: padded = padded << 1 return elem_size * padded if isinstance(llvm_type, ir.LiteralStructType): total: int = 0 max_align: int = 1 # llvmlite 的 LiteralStructType 只有 `.elements`(**没有** `.fields`): # 旧代码写 `.fields` 必然 AttributeError,尺寸/对齐链在此静默断掉。 for f in llvm_type.elements: fa: int = self._get_align(f) fs: int = self._get_struct_size(f) max_align = max(max_align, fa) if fa > 1: total = (total + fa - 1) // fa * fa total += fs if max_align > 1: total = (total + max_align - 1) // max_align * max_align return total if isinstance(llvm_type, ir.IdentifiedStructType): # 尝试解析 opaque struct 为已注册的版本 resolved = self._resolve_opaque_struct(llvm_type) if resolved is not llvm_type: return self._get_struct_size(resolved) if llvm_type.elements is None or len(llvm_type.elements) == 0: raise ValueError( f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' " f"布局未就绪(opaque / 未解析到定义),拒绝按指针大小兜底 —— " f"近似会少算尺寸,调用方(memhub alloc)按过小尺寸分配并越界写坏邻居块头。" f"请先确保该结构体已注册/可从 stub 加载后再取尺寸。" ) total2: int = 0 max_align2: int = 1 for f in llvm_type.elements: fa2: int = self._get_align(f) fs2: int = self._get_struct_size(f) max_align2 = max(max_align2, fa2) if fa2 > 1: total2 = (total2 + fa2 - 1) // fa2 * fa2 total2 += fs2 if max_align2 > 1: total2 = (total2 + max_align2 - 1) // max_align2 * max_align2 return total2 raise ValueError( f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})" f"无法计算尺寸,拒绝以常数兜底 —— 近似会少算尺寸并越界写坏内存池块头。" ) @staticmethod def _strip_unnecessary_quotes(ir_str: str) -> str: def _unquote(m: re.Match) -> str: prefix: str = m.group(1) name: str = m.group(2) if '.' in name: return m.group(0) if _RE_IR_IDENTIFIER.match(name): return prefix + name return m.group(0) return _RE_QUOTED_IR_NAME.sub(_unquote, ir_str) def finalize(self) -> str: self._set_Vtable_initializers() self._fix_global_var_init() # 函数级 DWARF(--dwarf / --asan):把 Module 落成 .ll 文本之前补模块级 # !llvm.dbg.cu 与 !llvm.module.flags(Debug Info Version=3);未启用时 # _GetDebugInfo() 返回 None,不做任何事(零影响)。 _DbgInfo = self._GetDebugInfo() if _DbgInfo is not None: _DbgInfo.Finalize() # Ctrl+C 调用栈(--traceback):同样是"落成 .ll 文本之前"的模块级收尾—— # 当本模块就是 includes/traceback.vp 时,注入 @llvm.global_ctors 构造器调用 # 其 Install()(见 BaseGen._RegisterTracebackInstallViaCtor)。未启用/非该模块 # 时 traceback_ctor=False,不做任何事(零影响)。 if self.traceback_ctor: self._RegisterTracebackInstallViaCtor() ir_text: str try: ir_text = str(self.module) except TypeError as e: for func in self.module.functions: for block in func.blocks: for instr in block.instructions: try: str(instr) except TypeError as te: pass raise ir_text = self._strip_unnecessary_quotes(ir_text) struct_defs: list[str] = [] struct_names: set[str] = set() for ClassName, struct_type in self.structs.items(): if isinstance(struct_type, ir.IdentifiedStructType): sname: str = struct_type.name sname_ir: str if sname[0].isdigit() or '.' in sname or not sname.replace('_', '').replace('.', '').isalnum(): sname_ir = f'%"{sname}"' else: sname_ir = f'%{sname}' if struct_type.elements is None or len(struct_type.elements) == 0: if ClassName not in self.typedef_int_widths: struct_names.add(sname) struct_defs.append(f'{sname_ir} = type opaque') continue elems: str = ', '.join(str(e) for e in struct_type.elements) if struct_type.packed: struct_defs.append(f'{sname_ir} = type <{{ {elems} }}>') else: struct_defs.append(f'{sname_ir} = type {{ {elems} }}') struct_names.add(sname) # 协程帧结构体兜底(M1):帧注册进 Gen.structs 时上面已输出; # 未注册(被 _generate_structs 清空)时这里补齐定义,避免 # getelementptr base element must be sized。 coro_frames: dict = getattr(self, '_coro_frame_types', None) or {} for _cf_name, _cf_ty in coro_frames.items(): if _cf_name in struct_names: continue if isinstance(_cf_ty, ir.IdentifiedStructType) and _cf_ty.elements: _cf_sname: str = _cf_ty.name _cf_ir: str = f'%"{_cf_sname}"' if (_cf_sname[0].isdigit() or "." in _cf_sname) else f'%{_cf_sname}' struct_defs.append(f'{_cf_ir} = type {{ {", ".join(str(e) for e in _cf_ty.elements)} }}') struct_names.add(_cf_sname) if struct_defs: lines: list[str] = ir_text.split('\n') # 预计算全部 "= type" 定义行前缀(一次性),用 str.startswith(tuple) # 在 C 层批量比较,替代原 O(lines × structs × patterns) 的 Python 嵌套 # 循环(PROF: finalize tottime 84.8s + startswith 4114 万次 46.9s)。 name_prefixes: list[str] = [] for name in struct_names: if name[0].isdigit() or '.' in name or not name.replace('_', '').replace('.', '').isalnum(): name_prefixes.append(f'%"{name}" = type') name_prefixes.append(f'%"{name}"=type') name_prefixes.append(f'%{name} = type') name_prefixes.append(f'%{name}=type') else: name_prefixes.append(f'%{name} = type') name_prefixes.append(f'%{name}= type') name_prefixes.append(f'%{name}=type') prefixes_tuple: tuple[str, ...] = tuple(name_prefixes) filtered: list[str] = [] for line in lines: if not line.strip().startswith(prefixes_tuple): filtered.append(line) ir_text = '\n'.join(filtered) header_end: int = ir_text.find('\ndefine') if header_end == -1: header_end = ir_text.find('\ndeclare') if header_end == -1: header_end = ir_text.find('\n@') if header_end == -1: header_end = len(ir_text) insert_pos: int = ir_text.rfind('\n', 0, header_end) if insert_pos == -1: insert_pos = header_end struct_block: str = '\n'.join(struct_defs) + '\n' _ir_head = ir_text[:insert_pos + 1] _ir_tail = ir_text[insert_pos + 1:] ir_text = _ir_head + struct_block + _ir_tail # 仅含特殊字符(数字开头或含点)的 struct 名需要加引号: # 合并为单个正则一次扫描全文本,替代逐 name 各自 re.sub(每个 name # 一次 O(text) 扫描)。lookahead 边界排除保证各备选互斥,语义等价。 special_names: list[str] = [n for n in struct_names if n[0].isdigit() or '.' in n] if special_names: ir_text = re.sub( r'%(' + '|'.join(re.escape(n) for n in special_names) + r')(?=[^a-zA-Z0-9_$\.]|$)', lambda m: r'%"' + m.group(1) + r'"', ir_text ) return ir_text def _MemberSlotAccepts(self, Declared: Any, Slot: Any) -> bool: """声明类型能否**隐式写进**该 struct 槽(判据与存储侧同一套)。 用于判定已冻结的 struct body 是否与 `class_members` 自洽。判定放宽到与 存储侧一致,避免误清:通用指针槽(i8*/void*)照旧放行(历史 stub 普遍 如此),继承上转/数组退让亦放行;只有**存储时必被拒绝**的组合——层数 不同、指针与非指针混位——才判为不一致,触发重建。 """ if Declared is None or Slot is None: return True # 同一对象直接放行:llvmlite 的 Type.__eq__ 走类型键比较(profile: # 本函数被 _generate_structs 调 54.2M 次,其中 types.py __eq__ 合计 ~30s) # ⇒ 先做身份判定短路,等价且避免绝大多数 __eq__/str() 开销。 if Declared is Slot: return True if Declared == Slot or str(Declared) == str(Slot): return True _d_ptr: bool = isinstance(Declared, ir.PointerType) _s_ptr: bool = isinstance(Slot, ir.PointerType) if _d_ptr != _s_ptr: return False if not _d_ptr: # 数值/浮点宽度差异不在本次收紧范围:保持既有行为(不清 body)。 return True try: Allowed, _Reason = self._ClassifyPtrImplicitConvert(Declared, Slot) return bool(Allowed) except Exception: # 判据不可用(缺上下文)时保守放行,避免误清已正确的布局。 return True def _IsRenumLayoutClass(self, ClassName: str | None) -> bool: """该类是否属 **REnum 布局**(主 REnum 自身,或其变体嵌套结构体 `_`)。 REnum 的 struct body 是 **max-variant 镜像布局**:`class_members` 里那些名字是 *变体*(不是字段),变体嵌套结构体也必须逐字镜像主结构体。因此**一律不得**用 class_members 的"声明类型"去改写或校验它们的布局 —— 否则跨模块 GEP 偏移与 定义端失配(变体载荷槽本就允许与 max-variant 槽类型不同,取值处靠 bitcast)。 """ if not ClassName: return False _tbl: Any = getattr(self, 'SymbolTable', None) if _tbl is None: return False try: _e: Any = _tbl.lookup(ClassName) except Exception: return False if _e is not None and getattr(_e, 'IsRenum', False): return True if '_' in ClassName: _prefix: str = ClassName.rsplit('_', 1)[0] try: _pe: Any = _tbl.lookup(_prefix) except Exception: _pe = None if _pe is not None and getattr(_pe, 'IsRenum', False): return True return False def _StructBodyHasPlaceholderT(self, St: Any) -> bool: """该 struct body 是否含未特化的 `T*` 占位字段(结果按 body 对象记忆)。 `_generate_structs` 里有两处对"全部类的全部元素"做这一扫描(Step 4 的完整性与 set_body 前的复核),每次调用都全量重扫 —— profile(2026-09-29 main_prof_72472): 这两处是本函数 62.5M 次 `isinstance`(6.20s)与 4.98M 次 `rsplit`(1.12s)的主源。 判定精确性:扫描是 `st.elements` 的**纯函数**,而 `elements` 在 llvmlite 里是 **整体替换**(`set_body` 赋新 tuple;作废时置 None;从不原地改元素)⇒ 用 `elements` 的**对象身份**当版本号即可精确判定"body 未变"。缓存条目**强引用** struct 实例与 elements 对象,二者存活 ⇒ `id()` 不会被回收复用 ⇒ 不会误判命中。 (代价:被替换掉的旧 body tuple 会被缓存多留一份引用;量级 = 结构体数量级,可忽略。) """ _cache: dict | None = getattr(self, '_struct_t_placeholder_cache', None) if _cache is None: _cache = {} self._struct_t_placeholder_cache = _cache _key: int = id(St) _elems: Any = getattr(St, 'elements', None) _hit: Any = _cache.get(_key) if _hit is not None and _hit[0] is St and _hit[1] is _elems: return _hit[2] _found: bool = False if _elems: for _elem in _elems: if isinstance(_elem, ir.PointerType) and isinstance(_elem.pointee, ir.IdentifiedStructType): _pname: str = _elem.pointee.name _short: str = _pname.rsplit('.', 1)[-1] if '.' in _pname else _pname if _short == 'T': _found = True break _cache[_key] = (St, _elems, _found) return _found def _generate_structs(self) -> None: # 热路径局部化:profile 中本函数被调 35,240 次(cum 1893s),内部 # SymbolTable.lookup 15.9M 次(16.7s)+ 真值判断触发的 __len__ 15.9M 次 # (6.8s)⇒ 把 SymbolTable / lookup 提到局部,避免逐类重复属性查找。 # 真值语义与 `if self.SymbolTable:` 逐字一致(SymbolTable 定义了 __len__, # 空表为假);表在单次调用内只增不减,故提升到函数级等价。 _symtab: Any = self.SymbolTable _has_symtab: bool = bool(_symtab) _sym_lookup: Any = _symtab.lookup if _symtab is not None else None all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys()) preserved_structs: dict[str, ir.IdentifiedStructType] = {} for name, st in self.structs.items(): if name not in all_classes: preserved_structs[name] = st elif isinstance(st, ir.IdentifiedStructType): preserved_structs[name] = st self.structs.clear() for name, st in preserved_structs.items(): self.structs[name] = st # Step 1: 先为所有类创建空的 struct,确保后面可以正确引用 all_classes = set(self.class_methods.keys()) | set(self.class_members.keys()) for ClassName in all_classes: is_packed: bool = ClassName in self.class_packed if _has_symtab and _sym_lookup is not None: Entry = _sym_lookup(ClassName) if Entry and Entry.IsExceptionClass: continue # 注意:类名可能与 CEnum 成员名冲突(如 ASTKind.Module 枚举成员与 Module 类同名) # 如果 ClassName 在 class_methods 中,表明是 _EmitClassLlvm 显式处理的常规类, # 不应被 IsEnum/IsTypedef 检查跳过 if Entry and Entry.IsEnum and ClassName not in self.class_methods: if not Entry.IsRenum: continue if Entry and Entry.IsTypedef and ClassName not in self.class_methods: if Entry.BaseType and not isinstance(Entry.BaseType, (type(None),)): if not isinstance(Entry.BaseType, (_t._CTypedef,)): continue if ClassName in self.structs: if is_packed: self.structs[ClassName].packed = True continue # 修复跨模块类型定义冲突:如果类是从其他模块导入的(已在 class_sha1_map 注册), # 必须使用导入的 source_sha1 作为前缀,而非本地模块的 sha1。 # 否则会创建缺少基类字段(如 GSListNode.Next)的本地类型定义, # 导致字段索引错位 → 访问违规崩溃。 _imported_sha1: str = self.class_sha1_map.get(ClassName, '') if not _imported_sha1: # 兜底查全局类→模块映射:class_sha1_map 可能因共享 class_methods # 跨文件泄漏而缺失条目(LlvmGenerator 共享赋值),此时若处于 # 泛型特化窗口(module_sha1 被切为模板定义模块如 _list 的 sha1), # _mangle_name 会用错误前缀注册其它模块的类 # (7cadfe07e983fd90.Translator 跨模块类型错位根因)。 # _struct_sha1_map 由 Phase2 从 vpi 扫描构建,含全部类的正确模块映射。 _imported_sha1 = self._struct_sha1_map.get(ClassName, '') if _imported_sha1: mangled_name: str = f"{_imported_sha1}.{ClassName}" else: mangled_name: str = self._mangle_name(ClassName) struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, mangled_name, packed=is_packed) self.structs[ClassName] = struct_type # Step 2: 解析跨模块的成员类型 for ClassName, annotations in self.class_member_annotations.items(): if ClassName not in self.class_members: continue for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): if member_name in annotations: annotation: Any = annotations[member_name] ResolvedType: ir.Type | None = None if isinstance(annotation, ast.Attribute): ClassTypeName: str = annotation.attr if ClassTypeName in self.structs: ResolvedType = self.structs[ClassTypeName] elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): left: Any = annotation.left if isinstance(left, ast.Attribute): ClassTypeName: str = left.attr if ClassTypeName in self.structs: ResolvedType = self.structs[ClassTypeName] if ResolvedType: self.class_members[ClassName][i] = (member_name, ResolvedType) struct_name_map: dict[str, ir.IdentifiedStructType] = {} for struct_name, struct in self.structs.items(): try: struct_name_map[struct.name] = struct except AssertionError: struct_name_map[struct_name] = struct orig: Any = getattr(struct, '_original_name', None) if orig: struct_name_map[orig] = struct # Step 3: 更新成员类型,确保使用正确的 mangled struct 引用 for ClassName in all_classes: if ClassName not in self.class_members: continue for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): if isinstance(member_type, ir.PointerType): pointee: ir.Type = member_type.pointee if isinstance(pointee, ir.IdentifiedStructType): pname: str | None try: pname = pointee.name except AssertionError: pname = None struct: ir.IdentifiedStructType | None = struct_name_map.get(pname) if pname else None if struct is None: orig: Any = getattr(pointee, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: # 【不重建已是同一实例的指针】`ir.PointerType(同一 struct 实例)` # 与现有成员类型**结构等价**(llvmlite 的 `_TypedPointerType.__eq__` # 按 pointee 比较;打印结果只取决于结构)⇒ 覆盖写是纯 no-op,却要 # 现建一个指针对象。profile(2026-09-29 main_prof_72472):本函数内 # `_TypedPointerType.__init__` 3,750,259 次(2.60s)+ `_patched_new` # 3,750,260 次(2.03s)+ `_patched_init`/`_IsBadPointee`(约 4.4s) # 主要来自这里;而本分支每次调用都会遍历全部类的全部成员 # ⇒ 绝大多数是"pointee 本来就是它"的重复重建。 if pointee is not struct: self.class_members[ClassName][i] = (member_name, ir.PointerType(struct)) else: raw_name: str = pname or '' if raw_name.startswith('struct.'): raw_name = raw_name[7:] new_struct: ir.IdentifiedStructType = self._get_or_create_struct(raw_name) self.class_members[ClassName][i] = (member_name, ir.PointerType(new_struct)) elif isinstance(member_type, ir.IdentifiedStructType): mname: str | None try: mname = member_type.name except AssertionError: mname = None struct: ir.IdentifiedStructType | None = struct_name_map.get(mname) if mname else None if struct is None: orig: Any = getattr(member_type, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: # 同上:已是同一实例 ⇒ 覆盖写是纯 no-op(llvmlite 类型按结构相等)。 if member_type is not struct: self.class_members[ClassName][i] = (member_name, struct) else: raw_name: str = mname or '' if raw_name.startswith('struct.'): raw_name = raw_name[7:] new_struct: ir.IdentifiedStructType = self._get_or_create_struct(raw_name) self.class_members[ClassName][i] = (member_name, new_struct) # Step 4: 创建最终的 structs 并设置正确的成员类型 for ClassName in all_classes: if _has_symtab and _sym_lookup is not None: Entry = _sym_lookup(ClassName) if Entry and Entry.IsExceptionClass: continue # 注意:类名可能与 CEnum 成员名冲突(如 ASTKind.Constant 枚举成员与 Constant 类同名) # 如果 ClassName 在 class_methods 中,表明是 _EmitClassLlvm 显式处理的常规类, # 不应被 IsEnum 检查跳过(与 Step 1 的守卫保持一致) if Entry and Entry.IsEnum and ClassName not in self.class_methods: if not Entry.IsRenum: continue existing_st: ir.IdentifiedStructType | None = self.structs.get(ClassName) if isinstance(existing_st, ir.IdentifiedStructType) and existing_st.elements is not None and len(existing_st.elements) > 0: # 治本修复:检查 struct body 是否不完整(元素数少于 class_members + vtable 预期)。 # 常见于跨模块继承:子类 struct 在父类 class_members 加载前从 stub 生成, # stub 中的定义可能不完整或类型不正确(如 GSList* 代替 AST*、i8* 代替 i32)。 # 此处检测到不完整后清除 elements(公开属性,见下方 Step 4 说明)和 # stub 缓存,让后续代码重新从 stub 加载。 _has_vtable_precheck: bool = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes if not _has_vtable_precheck: _p_pre: str | None = self.class_parent.get(ClassName) while _p_pre: if _p_pre in self.class_vtable or _p_pre in self._cross_module_vtable_classes: _has_vtable_precheck = True break _p_pre = self.class_parent.get(_p_pre) _expected_count_pre: int = len(self.class_members.get(ClassName, [])) + (1 if _has_vtable_precheck else 0) # has_vtable 时,首字段必须是 i8*(vtable 指针)。 # 若首字段非指针(如 i8 占位符),struct body 不完整,需清除重新加载。 _vtable_first_ok: bool = True if _has_vtable_precheck and len(existing_st.elements) > 0: _vtable_first_ok = isinstance(existing_st.elements[0], ir.PointerType) # 检测 T* 占位类型(未特化的泛型类型参数)。 # 当 struct 包含 T* 字段时(如 BasicBlock 继承 GSListNode[BasicBlock] 但 stub 未特化), # 说明 stub 使用了未特化的基类,需清除重新生成。 # 结果按 body 对象记忆(见 _StructBodyHasPlaceholderT:本扫描是 # "全类 × 全元素"的纯函数,重复扫描曾占本函数 isinstance 的主源)。 _has_t_placeholder_pre: bool = self._StructBodyHasPlaceholderT(existing_st) # 逐元素类型一致性(治本):仅比"个数 / 首元素是 vtable 指针 / T 占位" # 会漏掉**类型错位**的已冻结 body —— 现场 Generator:47 个元素个数正确、 # 元素名字也全对,但第 4 槽是 i8** 而 class_members 声明 # %"d827f3045bd28467.TypeGenCDef"*,个数命中即被当作"完整"直接 continue; # 而 llvmlite 的 set_body 只能设一次 ⇒ 错布局被永久冻结(后续 .vpi 重载 # 只修 class_members,改不了 body)⇒ 赋值时报"层数不同"。 # 判据与存储侧同一套(_MemberSlotAccepts → _ClassifyPtrImplicitConvert): # 声明类型**能隐式写进该槽**才算一致 —— 通用指针槽(i8*)照旧放行,不误清。 _slot_types_ok: bool = True _members_pre: list = self.class_members.get(ClassName, []) # REnum 布局(主 REnum 及变体嵌套结构体)是 max-variant 镜像布局, # class_members 里是*变体*不是字段 ⇒ 不参与逐元素类型校验(见 # _IsRenumLayoutClass)。 if _members_pre and not self._IsRenumLayoutClass(ClassName): _base_pre: int = 1 if _has_vtable_precheck else 0 _elems_pre: list = list(existing_st.elements) for _i_pre, _item_pre in enumerate(_members_pre): _j_pre: int = _base_pre + _i_pre if _j_pre >= len(_elems_pre): break _decl_pre: Any = _item_pre[1] if isinstance(_item_pre, tuple) and len(_item_pre) >= 2 else None if not self._MemberSlotAccepts(_decl_pre, _elems_pre[_j_pre]): _slot_types_ok = False break if (len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok and not _has_t_placeholder_pre and _slot_types_ok): if ClassName in self.class_packed and not existing_st.packed: existing_st.packed = True continue # 成员表**重名**时长度判据不可信:重名条目不是布局槽位(body 由定义模块的 # stub 决定),却会让 `_expected_count_pre` 多算一项 ⇒ 误判"body 不完整" # 而用带重复项的表重建 body(本模块布局比其它模块多一槽 ⇒ 跨模块 ABI 撕裂)。 # 现场:astaux.ExceptHandler 表 10 项含两个 'children',而定义模块 stub 的 # body 恰为 10 槽 = vtable + 9 个不同字段名。判据:表有重名**且** body 槽数 # 恰等于「去重名数 + vtable」⇒ body 已是权威布局,只报事实、不重建。 _uniq_names_pre: set[str] = {n for n, _ in _members_pre} if (len(_uniq_names_pre) < len(_members_pre) and len(existing_st.elements) == len(_uniq_names_pre) + (1 if _has_vtable_precheck else 0) and _vtable_first_ok and not _has_t_placeholder_pre): _DupRep: set = getattr(self, '_StructDupMembersReported', None) if _DupRep is None: _DupRep = set() self._StructDupMembersReported = _DupRep if ClassName not in _DupRep: _DupRep.add(ClassName) # 重名条目**逐条列出**(名 + 表内下标):单看"表 50 / 去重 42" # 无法判断是哪 8 条、落在哪一端,而"祖先块前缀的陈旧副本" # (见 HandlesImports.DropStaleAncestralCopies)与"两条收集路径 # 各登记一次"两种成因的**修法完全不同** ⇒ 报文必须自带判据。 _SeenIdx: dict = {} _DupDetail: list = [] _idxS: int for _idxS, (_nmS, _) in enumerate(_members_pre): if _nmS in _SeenIdx: _DupDetail.append(f"{_nmS}@{_idxS}") else: _SeenIdx[_nmS] = _idxS _vlog().warning( f"[结构体布局] 类 '{ClassName}' 的成员表含**重名条目**:表 " f"{len(_members_pre)} 项 / 去重 {len(_uniq_names_pre)} 项," f"现有 body {len(existing_st.elements)} 槽恰等于「去重名数 + vtable」" f"⇒ 认定 body 为权威布局(来自定义模块 stub),**不按重复表重建**" f"(重建会多出一槽、与其它模块布局不一致)。" f"重名条目(名@表内下标)={_DupDetail[:24]!r}" f"(共 {len(_DupDetail)} 条)。请核对成员表为何重复登记同一字段名。", "StructGen") if ClassName in self.class_packed and not existing_st.packed: existing_st.packed = True continue # struct body 不完整,清除 elements 和 stub 缓存以便重新加载。 # 注意:本仓 llvmlite 版本的字段名是**公开属性 `elements`** # (types.py: `self.elements = None`;`is_opaque` = `elements is None`; # `set_body` 只在 `elements is not None` 时拒绝)。此前写 # `_elements = None` 在本版本**不存在该属性** ⇒ 清空是空操作 ⇒ # 后续 `if struct_type.elements is None` 恒假 ⇒ set_body 永不执行 ⇒ # 错误布局(现场 Generator elements[11]=i8**)被永久冻结,而 # class_members 已被权威重建为正确类型 ⇒ 赋值时报"层数不同"。 existing_st.elements = None # 同时清除 sha1 全名 key 的 elements: # self.structs 中可能同时存在 'Value'(短名) 和 'f9e36e2cd6fa659f.Value'(sha1全名) 两个 key, # _TryLoadStructFromStub 调用 _get_or_create_struct 时会返回 sha1全名 key 的 struct。 # 若只清除短名 key 的 elements,sha1全名 key 的 struct 仍保持错误 body(is_opaque=False), # 导致 _TryLoadStructFromStub 跳过 set_body,struct 保持不完整字段数。 _imported_sha1_pre: str = self.class_sha1_map.get(ClassName, '') if _imported_sha1_pre: _full_key_pre: str = f"{_imported_sha1_pre}.{ClassName}" _full_st_pre: ir.IdentifiedStructType | None = self.structs.get(_full_key_pre) if _full_st_pre is not None and isinstance(_full_st_pre, ir.IdentifiedStructType): _full_st_pre.elements = None _import_handler_pre: Any = self._import_handler_ref if _import_handler_pre is not None: _cache_key_pre: tuple = (id(self), ClassName) if _cache_key_pre in _import_handler_pre._struct_Load_cache: del _import_handler_pre._struct_Load_cache[_cache_key_pre] # 同时清除 sha1全名 key 的 stub 缓存 if _imported_sha1_pre: _cache_key_full_pre: tuple = (id(self), f"{_imported_sha1_pre}.{ClassName}") if _cache_key_full_pre in _import_handler_pre._struct_Load_cache: del _import_handler_pre._struct_Load_cache[_cache_key_full_pre] has_vtable: bool = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes if not has_vtable: p: str | None = self.class_parent.get(ClassName) while p: if p in self.class_vtable or p in self._cross_module_vtable_classes: has_vtable = True self.class_vtable.add(ClassName) break p = self.class_parent.get(p) has_bitfield: bool = ClassName in self.class_member_bitfields and any( self.class_member_bitfields[ClassName].get(name, 0) > 0 for name, _ in self.class_members.get(ClassName, []) ) all_members: list[tuple[str, ir.Type]] = self.class_members.get(ClassName, []) # 跳过正在发射中且成员尚未填充的类(递归特化时避免过早设置空 body) if not all_members and not has_vtable and ClassName in self._classes_in_progress: continue # 字段收集期间触发的嵌套 _generate_structs:class_members 可能不完整, # set_body 只能调用一次,跳过等字段收集完成后再设置 body if ClassName in self._collecting_members_set: continue all_bitfield: bool = all( self.class_member_bitfields.get(ClassName, {}).get(name, 0) > 0 for name, _ in all_members ) if all_members else False mangled_name: str = self._mangle_name(ClassName) member_types: list[ir.Type] if has_bitfield and all_bitfield: total_bits: int = sum(self.class_member_bitfields.get(ClassName, {}).get(name, 0) for name, _ in all_members) storage_type: ir.IntType if total_bits <= 8: storage_type = ir.IntType(8) elif total_bits <= 16: storage_type = ir.IntType(16) elif total_bits <= 32: storage_type = ir.IntType(32) else: storage_type = ir.IntType(64) member_types = [storage_type] bitfield_offsets: dict[str, int] = {} current_bit_offset: int = 0 for name, _ in all_members: bit_width: int = self.class_member_bitfields.get(ClassName, {}).get(name, 0) bitfield_offsets[name] = current_bit_offset current_bit_offset += bit_width self.class_member_bitoffsets[ClassName] = bitfield_offsets else: member_types = [] if has_vtable: # 复用共享 i8* 实例(见模块顶部 _I8_PTR 说明) member_types.append(_I8_PTR) if ClassName in self.class_members: for _, member_type in self.class_members[ClassName]: if member_type is None: member_type = ir.IntType(32) member_types.append(member_type) else: if has_vtable: # has_vtable 但 class_members 为空(跨模块导入类): # 保持 member_types 为空([i8*] 占位会导致 stub 加载跳过, # 因为 set_body 后 is_opaque=False)。让 struct 保持 opaque, # 由 _TryLoadStructFromStub 设置完整 body。 member_types = [] else: # 无 vtable 且 class_members 为空:常见于 register_method 早期 # 调用 _generate_structs(此时 class_members[ClassName]=[] 但尚未 # 填充实际字段)。保持 opaque,避免 set_body(*[i8]) 永久损坏 struct # (llvmlite 的 set_body 只能调用一次)。后续 _generate_structs 调用 # 会从 class_members 或 stub 设置正确 body。 member_types = [] # 获取之前创建的 struct 并设置成员 struct_type: ir.IdentifiedStructType = self.structs[ClassName] if ClassName in self.class_packed: struct_type.packed = True # 只在未设置 body 时设置,避免重复定义错误 if struct_type.elements is None: # 跨模块 struct 的 class_members 可能不完整(当前模块只解析了部分字段), # 优先从 stub.ll 加载完整定义。set_body 只能对 opaque struct 调用一次, # 若用不完整 class_members 设置 body,后续无法修正,会导致 GEP 越界。 # 但本地定义的类(_classes_in_progress)不从 stub 加载,因为 stub 可能 # 来自同名导入类,其 vtable 状态与本地定义不同(@NoVTable 冲突) import_handler: Any = self._import_handler_ref if import_handler is not None and ClassName not in self._classes_in_progress: try: # 传入 source_sha1 确保从正确的 stub 文件加载完整定义 _src_sha1: str = self.class_sha1_map.get(ClassName, '') import_handler._TryLoadStructFromStub(ClassName, self, source_sha1=_src_sha1 or None) except Exception: pass struct_type = self.structs.get(ClassName, struct_type) # 检测 stub body 中的 T* 占位类型(未特化的泛型类型参数)。 # 若 stub 使用了未特化的基类(如 GSListNode 而非 GSListNode[BasicBlock]), # 清除 elements 让 set_body 用正确的 class_members 重新设置 # (字段名必须是公开属性 `elements`,见上方 Step 4 的说明)。 # 复用 _StructBodyHasPlaceholderT 的按 body 记忆结果(与上方 Step 4 同一判据)。 if member_types and struct_type.elements is not None: if self._StructBodyHasPlaceholderT(struct_type): struct_type.elements = None if struct_type.elements is None: # 防御性检查:member_types 为空时不调用 set_body。 # set_body(*[]) 会设置 elements=() 使 is_opaque=False, # 后续无法再次 set_body(llvmlite 限制),导致跨模块类型永久损坏。 # 根因:register_method 在 class_members[ClassName]=[] 时立即调用 # _generate_structs,此时 member_types 为空。保持 opaque 状态, # 让后续 _TryLoadStructFromStub 能正确设置 body。 if member_types: struct_type.set_body(*member_types) # ---- 冻结"成员名 → 槽位下标"映射(布局真源,挂在 struct 实例上)---- # struct body 一旦 set_body 即**永久冻结**,而 class_members 是**可变** # 表(继承展平会在父类就绪后前置前缀、跨模块可按需重载/重跑), # 两者序位会漂移。而字段访问用的是 `_get_member_offset` 的 # `base + 成员表下标`,漂移即"整类字段偏移平移"——现场: # Converter 模块读 `Alias.name` 取到槽 9(= asname),`import t` # 无 as 子句 ⇒ asname=NULL ⇒ vpi 生成报"import 别名 name 为空"。 # 这里把"建 body 那一刻"的真实槽位表记到 struct 实例上 # (用实例属性而非全局表:同一类名可能存在不止一个 struct 实例, # 偏移必须与被 GEP 的那一份同源)。仅当 member_types 恰为 # `[vtable?] + 全部成员` 时记录(位域类折叠成单一 storage 槽, # 其字段下标另由 class_member_bitoffsets 表达,不在此记录)。 _ExpectN: int = (1 if has_vtable else 0) + len(all_members) if len(member_types) == _ExpectN: _SlotMap: dict[str, int] = {} _SlotIdx: int = 1 if has_vtable else 0 for _mnS, _ in all_members: if _mnS not in _SlotMap: _SlotMap[_mnS] = _SlotIdx _SlotIdx += 1 struct_type._tpc_member_slots = _SlotMap struct_type._tpc_member_base = 1 if has_vtable else 0 # 有效性锚点:本表只对"建表这一刻的 body"成立。body 之后可能被 # 清空重设(本文件上方预检的 `existing_st.elements = None`、 # HandlesImports._TryLoadStructFromStub 的 set_body),而挂在 # 实例上的属性不会随之更新 ⇒ 陈旧表会把字段指到别的槽(现场: # AnnAssign 自有 4 字段时冻结 {'value': 3},body 重设为 12 槽后 # 'value' 仍读槽 3 = lineno(i32))。记下建表时的 **body 对象** # (set_body 每次赋新 tuple),查询侧用 `is` 判同一性,O(1) 且无猜测。 struct_type._tpc_member_body = struct_type.elements def _create_Vtable_globals(self) -> None: # 「本模块有定义(非 declare)」的函数名所蕴含的全部**点分前缀**集合。 # 一次 O(函数数) 预计算替代原实现"每个候选类都遍历全部函数"的 # O(类数 × 函数数)(profile:tottime 75.7s / 1631 次调用 ≈ 46ms per call, # 因为它对每个跨模块类都要把 self.functions 全扫一遍)。 # 判据等价:原 `func_name.startswith(f"{ClassName}.") 且非 declare` ⟺ # `f"{ClassName}."` 出现在某个非 declare 函数名的点分前缀集合里。 _local_prefixes: set[str] = set() for _fn, _fo in self.functions.items(): if getattr(_fo, 'is_declaration', False): continue _i: int = _fn.find('.') while _i != -1: _local_prefixes.add(_fn[:_i + 1]) _i = _fn.find('.', _i + 1) for ClassName, methods in self.class_methods.items(): if ClassName in self.Vtables: continue if not methods: continue if ClassName not in self.class_vtable and ClassName not in self._cross_module_vtable_classes: continue # 跳过正在发射中的类:它们的方法列表可能还不完整(如继承方法已添加但自有方法未添加), # 此时创建 vtable 会导致大小不匹配。这些类的 vtable 会在其 _EmitClassLlvm 完成后 # 由 _create_vtable_for_class 显式创建,或在模块级 _create_Vtable_globals 调用中创建。 if ClassName in self._classes_in_progress: continue # 跨模块虚表过滤:_precollect_vtable_state 会将所有 includes 的有方法类同时加入 # _shared_class_vtable 和 _shared_cross_module_vtable,导致 class_vtable 与 # _cross_module_vtable_classes 内容相同,无法用以区分本模块类与跨模块类。 # _generate_structs 会为所有 class_methods 的类创建空 struct type,导致 self.structs # 也包含所有类。过滤条件是 self.functions:只考虑本模块定义的函数(define,有函数体), # 跳过跨模块的 declare(无函数体)。跨模块类的 declare 通过 HandlesImports 加入 # self.functions(短名如 "MemManager.alloc"),但其 is_declaration=True。 # 若只存在 declare 而无 define,说明本模块未定义该类,虚表应由定义模块创建。 # vtable linkage='internal',跨模块调用通过对象的 vtable 指针(首字段)完成分派, # 不依赖本模块的 vtable 全局变量。 # 只考虑本模块定义的函数(define,有函数体);跨模块 declare 不算 # ⇒ _local_prefixes 已按 is_declaration 过滤,此处只查前缀集合。 if f"{ClassName}." not in _local_prefixes: continue VtableType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) Vtable: ir.GlobalVariable = ir.GlobalVariable(self.module, VtableType, name=self._mangle_name(f"{ClassName}_Vtable")) Vtable.initializer = ir.Constant(VtableType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) # 保持默认 linkage(空字符串,带 initializer 自动打印为外部强定义 # global,llvmlite descr 规则:initializer 非空时 linkage 归一为空)。 # 2026-08-10:跨模块 isinstance 需引用 sha1.Class_Vtable 符号, # vtable 名含模块 sha1 前缀全局唯一,外部可见不会冲突。 self.Vtables[ClassName] = Vtable def _create_vtable_for_class(self, ClassName: str) -> None: """为单个类创建 vtable(用于 _EmitClassLlvm 中当前类的方法已全部注册后)。""" methods: list = self.class_methods.get(ClassName, []) if not methods: return if ClassName not in self.class_vtable and ClassName not in self._cross_module_vtable_classes: return _vtable_name: str = self._mangle_name(f"{ClassName}_Vtable") # 如果虚表已在导入阶段创建(方法列表不完整,缺少继承方法),删除旧虚表 if ClassName in self.Vtables: _old_vtable = self.Vtables[ClassName] _old_name = _old_vtable.name if _old_name in self.module.globals: del self.module.globals[_old_name] # llvmlite 的 NameScope._useset 追踪所有已使用的名字, # del module.globals[name] 不会从 _useset 移除, # 导致再次创建同名 GlobalVariable 时 scope.register() 报 DuplicatedNameError self.module.scope._useset.discard(_old_name) del self.Vtables[ClassName] # 防御性:如果全局变量已存在但不在 Vtables 字典中,也删除 if _vtable_name in self.module.globals: del self.module.globals[_vtable_name] self.module.scope._useset.discard(_vtable_name) VtableType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) Vtable: ir.GlobalVariable = ir.GlobalVariable(self.module, VtableType, name=_vtable_name) Vtable.initializer = ir.Constant(VtableType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) # 默认 linkage(外部强定义),跨模块 isinstance 引用(与 _create_Vtable_globals 一致) self.Vtables[ClassName] = Vtable def _fix_global_var_init(self) -> None: for gv_name, gv in list(self.module.globals.items()): if not isinstance(gv, ir.GlobalVariable): continue if gv.linkage in ('external', 'extern_weak'): continue needs_fix: bool = False if gv.initializer is None: needs_fix = True elif hasattr(gv.initializer, 'constant') and isinstance(gv.initializer.constant, _Undefined): needs_fix = True elif isinstance(gv.initializer, ir.Constant): if isinstance(gv.initializer.constant, list): if any(hasattr(c, 'constant') and isinstance(constant, _Undefined) for c in gv.initializer.constant): needs_fix = True if not needs_fix: continue gv.initializer = ir.Constant(gv.value_type, None) if gv_name.startswith('str_const_') or gv_name.endswith('_Vtable') or gv_name.endswith('_str'): gv.linkage = 'internal' def _set_Vtable_initializers(self) -> None: for ClassName, methods in self.class_methods.items(): if ClassName not in self.Vtables: continue if not methods: continue struct_type: ir.IdentifiedStructType | None = self.structs.get(ClassName) if struct_type is None: continue Vtable: ir.GlobalVariable = self.Vtables[ClassName] Vtable_entries: list[ir.Constant] = [] for MethodName in methods: short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName # 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b) func: ir.Function | None = None if MethodName in self.functions: func = self.functions[MethodName] if func is None: full_MethodName: str = f"{ClassName}.{short_name}" if full_MethodName in self.functions: func = self.functions[full_MethodName] if func is None: # 沿 MRO 祖先链查找定义类(先声明父类优先) for anc in self._get_mro_chain(ClassName)[1:]: parent_full: str = f"{anc}.{short_name}" if parent_full in self.functions: func = self.functions[parent_full] break if func is None: Vtable_entries.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) continue Vtable_entries.append(func.bitcast(ir.PointerType(ir.IntType(8)))) Vtable.initializer = ir.Constant(Vtable.type.pointee, Vtable_entries)