diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 3919cf5..95cf436 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -594,66 +594,62 @@ class FuncGenMixin: _OffMembers: list = self.class_members.get(_ResolvedClass) or [] _BitFields: Any = getattr(self, 'class_member_bitfields', None) if _OffEls and _OffMembers and not (_BitFields and _BitFields.get(ClassName)): - # ---- 尾部对齐(布局真源之二):表**短于** body 字段区时 ---- - # 展平规则是"父类前缀 + 自有字段"(EnsureClassMembersFlattened 前置前缀、 - # StructGen 按展平表建 body)⇒ 自有字段恒为 body 尾部 len(表) 项。表比 - # 字段区(len(elements) - vtable 前缀)短,即"父类前缀尚未补齐"(父类 - # 未就绪/展平失败):此时 base(0/1) + 表内下标 会把整类字段整体前移 - # (现场:AnnAssign 表仅 4 项自有字段 ⇒ 'value' 读槽 3,见上)。改为按 - # 尾部求前缀数,并**逐项类型比对**自校验(判据与建 body 侧同一套 - # _MemberSlotAccepts):表自洽(长度 == 字段区)时本路径不介入, - # 对既有正确行为零变化;REnum 是 max-variant 镜像布局,不适用。 - _FieldArea: int = len(_OffEls) - _BaseNow() - # 【前置校验·必需】尾部对齐只对"**缺父类前缀**"的表成立,判据是 - # 前缀数 == 父类 struct 的槽数(展平前缀就是父类整份布局,含其继承链)。 - # 缺了这条,一张**只含前若干字段**的残缺表会被锚到 body 末尾: - # 现场 `SubTypeGen.Gen = self`(Generator.vp:136)——TypeGenMixin 表 - # 仅 ['Gen'](且其声明类型是通用指针/未解析),body 41 槽 ⇒ 被锚到 - # 槽 40(= Specs,%TypeSpecArena*)⇒ 报"Generator* 不能隐式转 - # TypeSpecArena*"。父类 struct 不可得(跨模块未加载/opaque)时 - # **不介入**(保守落回配对 / 旧路径)。 - _ParentCls: str | None = self.class_parent.get(_ResolvedClass) - _ParentSt: Any = self.structs.get(_ParentCls) if _ParentCls else None - _ParentLen: int = -1 - if isinstance(_ParentSt, ir.IdentifiedStructType) and _ParentSt.elements is not None: - _ParentLen = len(_ParentSt.elements) - if (len(_OffMembers) < _FieldArea - and not self._IsRenumLayoutClass(_ResolvedClass) - and _ParentLen > 0 - and len(_OffEls) - len(_OffMembers) == _ParentLen): - _TailBase: int = len(_OffEls) - len(_OffMembers) - _TailOk: bool = _TailBase >= 0 - _iT: int = 0 - while _TailOk and _iT < len(_OffMembers): - _nmT, _mtT = _OffMembers[_iT] - if not self._MemberSlotAccepts(_mtT, _OffEls[_TailBase + _iT]): - _TailOk = False - _iT += 1 - if _TailOk: - _TIdx: int = -1 - for _i5, (_n5, _) in enumerate(_OffMembers): - if _n5 == field_name: - _TIdx = _i5 - break - if _TIdx >= 0: - # 每类只留一次痕(大类的每个字段都会命中本路径) - _TailKey: str = _ResolvedClass - _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) - if _TailRep is None: - _TailRep = set() - self._MemberTailAlignedReported = _TailRep - if _TailKey not in _TailRep: - _TailRep.add(_TailKey) - _vlog().warning( - f"[成员偏移] 类 '{_ResolvedClass}' 的成员表短于 struct 字段区" - f"(表 {len(_OffMembers)} 项 vs 字段区 {_FieldArea} 槽," - f"body {len(_OffEls)} 槽)⇒ 按**尾部对齐**取槽:" - f"字段 '{field_name}' = {_TailBase + _TIdx}" - f"(前缀 {_TailBase} 项 = body 尾部之外的部分)", - "MemberBase") - return _TailBase + _TIdx - _Cursor: int = _BaseNow() + # ---- 布局真源之二:两种对齐候选按"逐项类型可写入"**打分**取高者 ---- + # 成员表可能是①展平表(含父类前缀)②仅自有字段(父类前缀未补齐 ⇒ 真身是 + # body **尾部**)③残缺前缀(如仅 ['Gen'] 一项 ⇒ 真身在 body **头部**)。 + # 只用"长度关系/父类槽数"来断言①③是猜,已被两轮现场打脸 + # (Generator.vp:136 TypeGenMixin 被锚到槽 40;:164 TypeGenErr 因恰好 + # 只有 1 个自有字段、`body-len(表) == 父类槽数` 巧合成立而被锚到槽 41)。 + # 改为**比较**两个候选锚(前缀锚 `_BaseNow()` vs 尾部锚 `len(body)-len(表)`) + # 的逐项得分(判据与建 body/存储侧同一套 `_MemberSlotAccepts`): + # 只有尾部锚**严格更高分**才采用它,同分/更低一律保持原行为 + # (配对 → 下标法),因此对既有正确布局零风险。 _ElemN: int = len(_OffEls) + _AnchorA: int = _BaseNow() + _AnchorB: int = _ElemN - len(_OffMembers) + _ScoreA: int = 0 + _aA: int = 0 + while _aA < len(_OffMembers): + if _AnchorA + _aA >= _ElemN: + break + _nA, _tA = _OffMembers[_aA] + if self._MemberSlotAccepts(_tA, _OffEls[_AnchorA + _aA]): + _ScoreA += 1 + _aA += 1 + _ScoreB: int = 0 + if _AnchorB >= 0 and _AnchorB != _AnchorA and not self._IsRenumLayoutClass(_ResolvedClass): + _aB: int = 0 + while _aB < len(_OffMembers): + if _AnchorB + _aB >= _ElemN: + break + _nB, _tB = _OffMembers[_aB] + if self._MemberSlotAccepts(_tB, _OffEls[_AnchorB + _aB]): + _ScoreB += 1 + _aB += 1 + if _ScoreB > _ScoreA and _ScoreB > 0: + _TIdx: int = -1 + for _i5, (_n5, _) in enumerate(_OffMembers): + if _n5 == field_name: + _TIdx = _i5 + break + if _TIdx >= 0: + # 每类只留一次痕(大类的每个字段都会命中本路径) + _TailKey: str = _ResolvedClass + _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) + if _TailRep is None: + _TailRep = set() + self._MemberTailAlignedReported = _TailRep + if _TailKey not in _TailRep: + _TailRep.add(_TailKey) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表({len(_OffMembers)} 项)" + f"在 body({_ElemN} 槽)上按**尾部对齐**得分更高" + f"(尾部锚 {_AnchorB} 得 {_ScoreB} vs 前缀锚 {_AnchorA} 得 {_ScoreA})" + f"⇒ 判定该表缺的是**父类前缀**:字段 '{field_name}' = " + f"{_AnchorB + _TIdx}", + "MemberBase") + return _AnchorB + _TIdx + _Cursor: int = _BaseNow() _PairedNames: set = set() for _nmP, _mtP in _OffMembers: if _Cursor >= _ElemN: