diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index ca6e902..e5d60e3 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -555,6 +555,73 @@ class FuncGenMixin: # 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow() # 在真正 return 处现算,不在此缓存。 _BaseNow() + # ---- 布局真源优先(配对式偏移):偏移只认"运行时真正被 GEP 的那份 struct body" ---- + # class_members 是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载), + # 而 struct body 一旦 set_body 即**永久冻结** ⇒ "base + 成员表下标"会随表漂移 + # 整体平移(现场:Converter 模块读 `Alias.name` 取到槽 9(= asname), + # `import t` 无 as 子句 ⇒ asname=NULL ⇒ vpi 生成误报"import 别名 name 为空"; + # 同族旧现场:ast.Constant.lineno 取到 pool 槽)。改为两条"只看布局"的路径: + # ① 建 body 时冻结下来的"成员名 → 槽位"映射(StructGen._generate_structs 挂载); + # ② 用**类型**把成员表逐项配对到 elements:类型不匹配的条目视为方法/占位/漂移项, + # 不占槽位;配对中类型相等的那个槽位即该字段的真实槽位。 + # 表与布局自洽时两条路径给出与旧下标法**逐字相同**的结果(body 本按该表建); + # 都覆盖不到时落回旧下标法(对既有正确行为零变化)。 + if field_name: + _OffStruct: Any = self.structs.get(ClassName) + if isinstance(StructHint, ir.IdentifiedStructType) and ClassName: + # 与 _member_base 同口径:只接受**同类**的 hint(接收者静态类型可能是基类, + # 实际指向派生类实例,其布局不属于本类)。 + if self._extract_short_name(getattr(StructHint, 'name', '') or '') == ClassName: + _OffStruct = StructHint + if isinstance(_OffStruct, ir.IdentifiedStructType): + _OffEls: Any = _OffStruct.elements + _Frozen: Any = getattr(_OffStruct, '_tpc_member_slots', None) + if isinstance(_Frozen, dict) and field_name in _Frozen: + _FrozenSlot: Any = _Frozen[field_name] + if isinstance(_FrozenSlot, int) and (not _OffEls or 0 <= _FrozenSlot < len(_OffEls)): + return _FrozenSlot + _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)): + _Cursor: int = _BaseNow() + _ElemN: int = len(_OffEls) + _PairedNames: set = set() + for _nmP, _mtP in _OffMembers: + if _Cursor >= _ElemN: + break + # 重名条目不是布局槽位(表构造/重载产生的重复项)⇒ 不占槽位 + if _nmP in _PairedNames: + continue + # None 成员在 body 里被物化为 i32(见 StructGen 建 body 分支), + # 配对类型必须同口径,否则该类字段会被整条跳过。 + _mtEff: Any = _mtP if _mtP is not None else ir.IntType(32) + _etP: Any = _OffEls[_Cursor] + if _mtEff is _etP or str(_mtEff) == str(_etP): + _PairedNames.add(_nmP) + if _nmP == field_name: + # 与旧下标法不一致时留痕:这正是"成员表与 struct 布局 + # 不自洽"的现场事实(每类每字段一次,便于定位漂移来源)。 + _OldIdx: int = -1 + for _i3, (_n3, _) in enumerate(_OffMembers): + if _n3 == field_name: + _OldIdx = _i3 + break + if _OldIdx >= 0 and _BaseNow() + _OldIdx != _Cursor: + _RepKey: tuple = (_ResolvedClass, field_name) + _RepSet: set = getattr(self, '_MemberSlotDriftReported', None) + if _RepSet is None: + _RepSet = set() + self._MemberSlotDriftReported = _RepSet + if _RepKey not in _RepSet: + _RepSet.add(_RepKey) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表与 struct " + f"布局不自洽:字段 '{field_name}' 下标法={_BaseNow() + _OldIdx}" + f"(成员表 {len(_OffMembers)} 项) vs 布局配对比={_Cursor}" + f"(struct {_ElemN} 槽)——已采用布局配对结果", + "MemberBase") + return _Cursor + _Cursor += 1 if _ResolvedClass and _ResolvedClass in self.class_members: for i, (name, _) in enumerate(self.class_members[_ResolvedClass]): if name == field_name: diff --git a/lib/core/LLVMCG/StructGen.py b/lib/core/LLVMCG/StructGen.py index 6fb5c0a..f2d54d7 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -755,6 +755,28 @@ class StructGenMixin: # 让后续 _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 def _create_Vtable_globals(self) -> None: # 「本模块有定义(非 declare)」的函数名所蕴含的全部**点分前缀**集合。