diff --git a/lib/core/LLVMCG/StructGen.py b/lib/core/LLVMCG/StructGen.py index 15ccbb3..6fb5c0a 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -6,6 +6,12 @@ import llvmlite.ir as ir from llvmlite.ir.values import _Undefined from lib.includes import t as _t +# vtable 槽类型 `i8*` 的共享实例:llvmlite 的类型是不可变值语义(`_TypedPointerType` +# 的 `__eq__` 按结构比较、打印结果只取决于结构),同一实例可在任意多少量复用。 +# 原来在 `_generate_structs` 的逐类 `member_types` 构建里**每类每次现建** +# (profile:`_TypedPointerType.__init__` 3,750,259 次中有一份来自这里)。 +_I8_PTR: ir.Type = ir.PointerType(ir.IntType(8)) + class StructGenMixin: @@ -353,6 +359,40 @@ class StructGenMixin: 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 次 @@ -469,7 +509,16 @@ class StructGenMixin: orig: Any = getattr(pointee, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: - self.class_members[ClassName][i] = (member_name, ir.PointerType(struct)) + # 【不重建已是同一实例的指针】`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.'): @@ -487,7 +536,9 @@ class StructGenMixin: orig: Any = getattr(member_type, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: - self.class_members[ClassName][i] = (member_name, struct) + # 同上:已是同一实例 ⇒ 覆盖写是纯 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.'): @@ -531,14 +582,9 @@ class StructGenMixin: # 检测 T* 占位类型(未特化的泛型类型参数)。 # 当 struct 包含 T* 字段时(如 BasicBlock 继承 GSListNode[BasicBlock] 但 stub 未特化), # 说明 stub 使用了未特化的基类,需清除重新生成。 - _has_t_placeholder_pre: bool = False - for _elem_pre in existing_st.elements: - if isinstance(_elem_pre, ir.PointerType) and isinstance(_elem_pre.pointee, ir.IdentifiedStructType): - _pname_pre: str = _elem_pre.pointee.name - _short_pre: str = _pname_pre.rsplit('.', 1)[-1] if '.' in _pname_pre else _pname_pre - if _short_pre == 'T': - _has_t_placeholder_pre = True - break + # 结果按 body 对象记忆(见 _StructBodyHasPlaceholderT:本扫描是 + # "全类 × 全元素"的纯函数,重复扫描曾占本函数 isinstance 的主源)。 + _has_t_placeholder_pre: bool = self._StructBodyHasPlaceholderT(existing_st) # 逐元素类型一致性(治本):仅比"个数 / 首元素是 vtable 指针 / T 占位" # 会漏掉**类型错位**的已冻结 body —— 现场 Generator:47 个元素个数正确、 # 元素名字也全对,但第 4 槽是 i8** 而 class_members 声明 @@ -650,7 +696,8 @@ class StructGenMixin: else: member_types = [] if has_vtable: - member_types.append(ir.PointerType(ir.IntType(8))) + # 复用共享 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: @@ -695,16 +742,9 @@ class StructGenMixin: # 若 stub 使用了未特化的基类(如 GSListNode 而非 GSListNode[BasicBlock]), # 清除 elements 让 set_body 用正确的 class_members 重新设置 # (字段名必须是公开属性 `elements`,见上方 Step 4 的说明)。 - if struct_type.elements is not None and member_types: - _has_t_placeholder_post: bool = False - for _elem_post in struct_type.elements: - if isinstance(_elem_post, ir.PointerType) and isinstance(_elem_post.pointee, ir.IdentifiedStructType): - _pname_post: str = _elem_post.pointee.name - _short_post: str = _pname_post.rsplit('.', 1)[-1] if '.' in _pname_post else _pname_post - if _short_post == 'T': - _has_t_placeholder_post = True - break - if _has_t_placeholder_post: + # 复用 _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。