diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 51fae57..14997bd 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -519,6 +519,14 @@ class FuncGenMixin: # 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow() # 在真正 return 处现算,不在此缓存。 _BaseNow() + # 布局自洽修复(成员表已在上方补齐/重载后执行):struct body 若在早期 pass 就 + # 被降级类型冻住(set_body 只能设一次),此处按 class_members 就地把不兼容的槽 + # 改回声明类型,避免"修正晚于冻结"导致后续 GEP 拿到 i8** 这类错位槽 + # (现场:Generator 第 3/11 槽,赋值时 fail-fast"层数不同")。 + # 仅在同名类 sha1 冲突路径之外执行(_ResolvedClass != ClassName 时成员表取自 + # 全名键,短名表可能是别的同名类,拿它去修会写坏布局)。 + if _ResolvedClass == ClassName: + self._SyncStructBodyTypes(ClassName) 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 acd99da..15b74d2 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -319,6 +319,48 @@ class StructGenMixin: # 判据不可用(缺上下文)时保守放行,避免误清已正确的布局。 return True + def _SyncStructBodyTypes(self, ClassName: str | None) -> None: + """把 struct body 中与 `class_members` 声明**不兼容**的槽改回声明类型(就地修复)。 + + 背景:llvmlite 的 `set_body` 只能设一次。若某个早期 pass 在 `class_members` + 尚未被 .vpi / stub 修正、类型已降级(如 `i8**` 顶替 `%X*`)时就把 body 冻住, + 错位会**永久留存** —— 后续重载只修 `class_members`,不动 body + (现场:Generator 第 3/11 槽;一次修好一槽、下一槽仍错,正是时序随机)。 + Step 4 的前置校验只在**当次** `_generate_structs` 生效,漏"修正晚于冻结"的时序, + 故在**使用点**(成员偏移查询,即任何 GEP 之前)再校一次并就地修复: + + - 只替换**存储时必被拒绝**的槽(判据与存储侧同一套 `_MemberSlotAccepts`), + 其余元素(含通用指针 i8* 槽)原样保留 —— 不做整表重建,避免丢 stub 侧信息; + - `_elements` 置空后 `set_body` 重设:命名类型对象不变,故已发出的 + `getelementptr %"name"*` 不受影响。 + + LLVM 具名类型只有一份定义,重设 body 后打印即为修正后的布局,故在模块落盘前 + 修复是安全的;越早(首次字段访问)修复越干净。 + """ + if not ClassName: + return + _st: Any = self.structs.get(ClassName) + if not isinstance(_st, ir.IdentifiedStructType) or _st.elements is None: + return + _members: list = self.class_members.get(ClassName) or [] + if not _members: + return + _base: int = self._member_base(ClassName) + _elems: list = list(_st.elements) + _changed: bool = False + for _i, _item in enumerate(_members): + _j: int = _base + _i + if _j >= len(_elems): + break + _decl: Any = _item[1] if isinstance(_item, tuple) and len(_item) >= 2 else None + if _decl is not None and not self._MemberSlotAccepts(_decl, _elems[_j]): + _elems[_j] = _decl + _changed = True + if not _changed: + return + _st._elements = None + _st.set_body(*_elems) + def _generate_structs(self) -> None: all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys())