From 58050a1ab2b4c50af84c8f713f3ca21192579bc9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 26 Sep 2026 21:29:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(member=5Fbase):=20base=20=E6=94=B9=E7=94=B1?= =?UTF-8?q?=E5=A3=B0=E6=98=8E=E7=B1=BB=E5=9E=8B=E4=B8=8E=20elements=20?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E8=87=B4=E5=BA=A6=E6=A0=A1=E5=87=86=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=95=B4=E7=B1=BB=E5=AD=97=E6=AE=B5=E5=81=8F=E7=A7=BB?= =?UTF-8?q?=E6=95=B4=E4=BD=93=E5=B7=AE=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delta 长度启发式只在成员表与布局恰好差 vtable 一项时成立;成员表多/少一个非 vtable 项时 base 判错,整个类字段偏移整体平移:ast.Constant 的 lineno 落到 pool(MemManager*),调用点报形参 lineno: i32 收到指针,把真因掩盖成指针/整数错配 改法:对 b 属于 {0,1,2} 逐项比较成员声明类型与 elements[b+i],取一致度最高者(完全相同 +2),仅在明显更优(至少多对齐 1 个成员)时覆盖启发式;指针槽区分度最高 同时移除上一轮临时加的运行时字段布局探针(_diag_member_layout 及其两处调用) --- lib/core/Handles/HandlesExprAttr.py | 51 ----------------------------- lib/core/LLVMCG/FuncGen.py | 35 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index 8726e5d..7ba840b 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -866,7 +866,6 @@ class ExprAttrHandle(BaseHandle): if Node.attr in Gen.class_member_bitoffsets.get(ResolvedKey, {}): return self._load_bitfield_member(CastedObj, ResolvedKey, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, ResolvedKey) - self._diag_member_layout(Gen, ResolvedKey, Node.attr, offset) if offset is None: return None if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType): @@ -897,7 +896,6 @@ class ExprAttrHandle(BaseHandle): if Node.attr in bitfield_offsets: return self._load_bitfield_member(ObjVal, CN, Node.attr) offset = Gen._get_member_offset(Node.attr, CN) - self._diag_member_layout(Gen, CN, Node.attr, offset) if offset is None: break # 泛型基础类型字段特化(根本修复): @@ -1627,55 +1625,6 @@ class ExprAttrHandle(BaseHandle): return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") return None - def _diag_member_layout(self, Gen: LlvmCodeGenerator, ClassName: str, FieldName: str, - Offset: int | None) -> None: - """字段布局对照探针(**只在"索引/类型对不上"时打一行**,默认完全静默)。 - - 判据(打印条件,任一成立才打): - · offset 为 None(该字段没能解析出索引);或 - · members[offset] 的名字不是本次查询的字段名(= 名字→索引表与结构体布局错位, - 即"往 elements[offset] 上取类型/取值"会取到别的字段);或 - · members[offset] 的声明类型与 elements[offset] 不是同一类型(含指针/整数错配, - 如 `Constant.lineno` 取到 `MemManager*`)。 - 只打关键三元组 + 两表长度;长度不等时再补一行字段名清单(短)。 - 上限 15 行,按 (类, 字段) 去重,避免刷屏。 - """ - try: - Seen: set = getattr(self, '_MemberLayoutDumped', None) - if Seen is None: - Seen = set() - self._MemberLayoutDumped = Seen - self._MemberLayoutDumpCount = 0 - Key: tuple = (ClassName, FieldName) - if Key in Seen: - return - Members: list = list(Gen.class_members.get(ClassName, [])) - ST: Any = Gen.structs.get(ClassName) - if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: - Elems: list = list(ST.elements) - else: - Elems = [] - NameAt: str = '' - TypeAt: Any = None - if Offset is not None and 0 <= Offset < len(Members): - NameAt, TypeAt = Members[Offset][0], Members[Offset][1] - ElemAt: Any = Elems[Offset] if (Offset is not None and 0 <= Offset < len(Elems)) else None - TypeSame: bool = (TypeAt is ElemAt) or (TypeAt is not None and str(TypeAt) == str(ElemAt)) - Suspicious: bool = (Offset is None) or (NameAt != FieldName) or (not TypeSame) - if not Suspicious: - return - if getattr(self, '_MemberLayoutDumpCount', 0) >= 15: - return - Seen.add(Key) - self._MemberLayoutDumpCount = getattr(self, '_MemberLayoutDumpCount', 0) + 1 - print(f'[DIAG] 字段布局错位 {ClassName}.{FieldName}: offset={Offset} ' - f'members[offset]名={NameAt!r} 声明类型={TypeAt} element类型={ElemAt} ' - f'| members={len(Members)} elements={len(Elems)}', flush=True) - if len(Members) != len(Elems): - print(f' members 名序=[{", ".join(n for n, _t in Members)}]', flush=True) - except Exception: - pass - def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: if isinstance(Node, ast.Name): VarName: str = Node.id diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 19fb242..4a528b4 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -380,6 +380,41 @@ class FuncGenMixin: elif _Delta == 1: _HasParent: bool = self.class_parent.get(_Cls) is not None base = 1 if (_IsVtablePtr or not _HasParent or _ByInheritVtable) else 0 + # ---- 类型一致性校准(治本)---- + # 上面的 delta 长度启发式只在"表与布局恰好差 vtable 一项"时成立:一旦成员表 + # 比布局多/少**非 vtable** 项(跨模块类展平不全、同名类复用等),base 会被判错, + # 结果是**整个类所有字段偏移整体平移**。实测:`ast.Constant` 的 + # struct = {i8*(vtable), AST*(parent), MemManager*(pool), i32(lineno), …} + # 而查询 `cnst.lineno` 得到的是 pool 那槽(`%MemManager*`)⇒ 调用点报 + # "形参 lineno: i32 收到指针",掩盖了真因。 + # 校准办法:对 b∈{0,1,2} 逐项比较"成员声明类型 vs elements[b+i] 类型", + # 取一致度最高者(完全相同 +2;同为指针但类型不同 +0)。正确对齐时 + # 指针槽几乎全中,错位对齐只能靠 i32 撞运气,分数明显更低;平手保留启发式。 + _MembersCal: list = self.class_members.get(_Cls, []) + if _MembersCal and _NumElems > 0: + _BestB: int = base + _BestScore: int = -1 + _HeurScore: int = -1 + _b: int + for _b in range(0, 3): + _Score: int = 0 + _i2: int = 0 + for _nm2, _mt2 in _MembersCal: + _j2: int = _b + _i2 + if _j2 >= _NumElems: + break + _et2: ir.Type = _StructBase.elements[_j2] + if _mt2 is _et2 or str(_mt2) == str(_et2): + _Score += 2 + _i2 += 1 + if _b == base: + _HeurScore = _Score + if _Score > _BestScore: + _BestScore = _Score + _BestB = _b + # 仅当"明显更优"(至少多对齐 1 个成员)才覆盖启发式结果,避免抖动。 + if _HeurScore >= 0 and _BestScore >= _HeurScore + 2: + base = _BestB return base def _get_member_offset(self, field_name: str, ClassName: str | None = None) -> int | None: