fix(member_base): base 改由声明类型与 elements 的一致度校准,修整类字段偏移整体差 1
delta 长度启发式只在成员表与布局恰好差 vtable 一项时成立;成员表多/少一个非 vtable 项时 base 判错,整个类字段偏移整体平移:ast.Constant 的 lineno 落到 pool(MemManager*),调用点报形参 lineno: i32 收到指针,把真因掩盖成指针/整数错配
改法:对 b 属于 {0,1,2} 逐项比较成员声明类型与 elements[b+i],取一致度最高者(完全相同 +2),仅在明显更优(至少多对齐 1 个成员)时覆盖启发式;指针槽区分度最高
同时移除上一轮临时加的运行时字段布局探针(_diag_member_layout 及其两处调用)
This commit is contained in:
1 parent
50cea6c7b3
commit
58050a1ab2
2 files changed
+35
-51
No files matched your search
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user