from __future__ import annotations import re import ast import llvmlite.ir as ir from llvmlite.ir.values import _Undefined class StructGenMixin: def _get_align(self, llvm_type): if isinstance(llvm_type, ir.IntType): return max(1, llvm_type.width // 8) if isinstance(llvm_type, ir.FloatType): return 4 if isinstance(llvm_type, ir.DoubleType): return 8 if isinstance(llvm_type, ir.PointerType): return self.ptr_size if isinstance(llvm_type, ir.ArrayType): return self._get_align(llvm_type.element) if isinstance(llvm_type, ir.LiteralStructType): return max((self._get_align(f) for f in llvm_type.elements), default=1) if isinstance(llvm_type, ir.IdentifiedStructType): if llvm_type.elements is None or len(llvm_type.elements) == 0: return self.ptr_size # opaque struct, assume pointer alignment return max((self._get_align(f) for f in llvm_type.elements), default=1) if isinstance(llvm_type, ir.VoidType): return 0 return 4 def _get_struct_size(self, llvm_type): if isinstance(llvm_type, ir.IntType): return max(1, llvm_type.width // 8) if isinstance(llvm_type, ir.FloatType): return 4 if isinstance(llvm_type, ir.DoubleType): return 8 if isinstance(llvm_type, ir.PointerType): return self.ptr_size if isinstance(llvm_type, ir.ArrayType): return self._get_struct_size(llvm_type.element) * llvm_type.count if isinstance(llvm_type, ir.LiteralStructType): total = 0 max_align = 1 for f in llvm_type.fields: fa = self._get_align(f) fs = self._get_struct_size(f) max_align = max(max_align, fa) if fa > 1: total = (total + fa - 1) // fa * fa total += fs if max_align > 1: total = (total + max_align - 1) // max_align * max_align return total if isinstance(llvm_type, ir.IdentifiedStructType): if llvm_type.elements is None or len(llvm_type.elements) == 0: return self.ptr_size # opaque struct, assume pointer size total = 0 max_align = 1 for f in llvm_type.elements: fa = self._get_align(f) fs = self._get_struct_size(f) max_align = max(max_align, fa) if fa > 1: total = (total + fa - 1) // fa * fa total += fs if max_align > 1: total = (total + max_align - 1) // max_align * max_align return total return 4 @staticmethod def _strip_unnecessary_quotes(ir_str): def _unquote(m): prefix = m.group(1) name = m.group(2) if '.' in name: return m.group(0) if re.match(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$', name): return prefix + name return m.group(0) return re.sub(r'([@%])"([^"]+)"', _unquote, ir_str) def finalize(self): self._set_Vtable_initializers() self._fix_global_var_init() try: ir_text = str(self.module) except TypeError as e: for func in self.module.functions: for block in func.blocks: for instr in block.instructions: try: str(instr) except TypeError as te: pass raise ir_text = self._strip_unnecessary_quotes(ir_text) struct_defs = [] struct_names = set() for ClassName, struct_type in self.structs.items(): if isinstance(struct_type, ir.IdentifiedStructType): sname = struct_type.name if sname[0].isdigit() or '.' in sname or not sname.replace('_', '').replace('.', '').isalnum(): sname_ir = f'%"{sname}"' else: sname_ir = f'%{sname}' if struct_type.elements is None or len(struct_type.elements) == 0: if ClassName not in self.typedef_int_widths: struct_names.add(sname) struct_defs.append(f'{sname_ir} = type opaque') continue elems = ', '.join(str(e) for e in struct_type.elements) if struct_type.packed: struct_defs.append(f'{sname_ir} = type <{{ {elems} }}>') else: struct_defs.append(f'{sname_ir} = type {{ {elems} }}') struct_names.add(sname) if struct_defs: lines = ir_text.split('\n') filtered = [] for line in lines: stripped = line.strip() skip = False for name in struct_names: if name[0].isdigit() or '.' in name or not name.replace('_', '').replace('.', '').isalnum(): patterns = [f'%"{name}" = type', f'%"{name}"=type', f'%{name} = type', f'%{name}=type'] else: patterns = [f'%{name} = type', f'%{name}= type', f'%{name}=type'] for p in patterns: if stripped.startswith(p): skip = True break if skip: break if not skip: filtered.append(line) ir_text = '\n'.join(filtered) header_end = ir_text.find('\ndefine') if header_end == -1: header_end = ir_text.find('\ndeclare') if header_end == -1: header_end = ir_text.find('\n@') if header_end == -1: header_end = len(ir_text) insert_pos = ir_text.rfind('\n', 0, header_end) if insert_pos == -1: insert_pos = header_end struct_block = '\n'.join(struct_defs) + '\n' ir_text = ir_text[:insert_pos + 1] + struct_block + ir_text[insert_pos + 1:] for name in struct_names: if name[0].isdigit() or '.' in name: ir_text = re.sub( r'%' + re.escape(name) + r'(?=[^a-zA-Z0-9_$\.]|$)', r'%"' + name + r'"', ir_text ) return ir_text def _collect_classes_and_methods(self, ast_nodes, parent=None): current_class = None for node in ast_nodes: if isinstance(node, str): continue node.parent = parent node_type = type(node).__name__ if node_type == 'Struct': if hasattr(node, 'name'): current_class = node.name self.class_methods[current_class] = [] self.class_members[current_class] = [] if hasattr(node, 'decls') and node.decls: for decl in node.decls: if type(decl).__name__ == 'Decl' and hasattr(decl, 'name'): member_type = self._get_type(decl.type) if hasattr(decl, 'type') else ir.IntType(32) self.class_members[current_class].append((decl.name, member_type)) elif node_type == 'FuncDef': if current_class and current_class in self.class_methods: MethodName = node.decl.name if hasattr(node.decl, 'name') else 'unknown_method' if not MethodName.startswith(f"{current_class}."): MethodName = f"{current_class}.{MethodName}" self.class_methods[current_class].append(MethodName) if hasattr(node, 'body') and isinstance(node.body, list): self._collect_classes_and_methods(node.body, parent=node) elif hasattr(node, 'BlockItems') and node.BlockItems: self._collect_classes_and_methods(node.BlockItems, parent=node) def _generate_structs(self): all_classes = set(self.class_methods.keys()) | set(self.class_members.keys()) preserved_structs = {} for name, st in self.structs.items(): if name not in all_classes: preserved_structs[name] = st elif isinstance(st, ir.IdentifiedStructType): preserved_structs[name] = st self.structs.clear() for name, st in preserved_structs.items(): self.structs[name] = st # Step 1: 先为所有类创建空的 struct,确保后面可以正确引用 all_classes = set(self.class_methods.keys()) | set(self.class_members.keys()) for ClassName in all_classes: is_packed = ClassName in self.class_packed if hasattr(self, 'SymbolTable') and self.SymbolTable and ClassName in self.SymbolTable: Entry = self.SymbolTable[ClassName] if Entry.IsExceptionClass: continue if Entry.IsEnum: if not Entry.IsRenum: continue if Entry.IsTypedef: if Entry.BaseType and not isinstance(Entry.BaseType, (type(None),)): import lib.includes.t as _t if not isinstance(Entry.BaseType, (_t._CTypedef,)): continue if ClassName in self.structs: if is_packed: self.structs[ClassName].packed = True continue mangled_name = self._mangle_name(ClassName) struct_type = ir.IdentifiedStructType(self.module, mangled_name, packed=is_packed) self.structs[ClassName] = struct_type # Step 2: 解析跨模块的成员类型 for ClassName, annotations in self.class_member_annotations.items(): if ClassName not in self.class_members: continue for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): if member_name in annotations: annotation = annotations[member_name] ResolvedType = None if isinstance(annotation, ast.Attribute): ClassTypeName = annotation.attr if ClassTypeName in self.structs: ResolvedType = self.structs[ClassTypeName] elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): left = annotation.left if isinstance(left, ast.Attribute): ClassTypeName = left.attr if ClassTypeName in self.structs: ResolvedType = self.structs[ClassTypeName] if ResolvedType: self.class_members[ClassName][i] = (member_name, ResolvedType) struct_name_map = {} for struct_name, struct in self.structs.items(): try: struct_name_map[struct.name] = struct except AssertionError: struct_name_map[struct_name] = struct orig = getattr(struct, '_original_name', None) if orig: struct_name_map[orig] = struct # Step 3: 更新成员类型,确保使用正确的 mangled struct 引用 for ClassName in all_classes: if ClassName not in self.class_members: continue for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): if isinstance(member_type, ir.PointerType): pointee = member_type.pointee if isinstance(pointee, ir.IdentifiedStructType): try: pname = pointee.name except AssertionError: pname = None struct = struct_name_map.get(pname) if pname else None if struct is None: orig = getattr(pointee, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: self.class_members[ClassName][i] = (member_name, ir.PointerType(struct)) else: raw_name = pname or '' if raw_name.startswith('struct.'): raw_name = raw_name[7:] new_struct = self._get_or_create_struct(raw_name) self.class_members[ClassName][i] = (member_name, ir.PointerType(new_struct)) elif isinstance(member_type, ir.IdentifiedStructType): try: mname = member_type.name except AssertionError: mname = None struct = struct_name_map.get(mname) if mname else None if struct is None: orig = getattr(member_type, '_original_name', None) struct = struct_name_map.get(orig) if orig else None if struct is not None: self.class_members[ClassName][i] = (member_name, struct) else: raw_name = mname or '' if raw_name.startswith('struct.'): raw_name = raw_name[7:] new_struct = self._get_or_create_struct(raw_name) self.class_members[ClassName][i] = (member_name, new_struct) # Step 4: 创建最终的 structs 并设置正确的成员类型 for ClassName in all_classes: if hasattr(self, 'SymbolTable') and self.SymbolTable and ClassName in self.SymbolTable: Entry = self.SymbolTable[ClassName] if Entry.IsExceptionClass: continue if Entry.IsEnum: if not Entry.IsRenum: continue existing_st = self.structs.get(ClassName) if isinstance(existing_st, ir.IdentifiedStructType) and existing_st.elements is not None and len(existing_st.elements) > 0: # 即使 struct 已有元素,也需要修正 packed 属性 if ClassName in self.class_packed and not existing_st.packed: existing_st.packed = True continue has_vtable = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes if not has_vtable: p = self.class_parent.get(ClassName) while p: if p in self.class_vtable or p in self._cross_module_vtable_classes: has_vtable = True self.class_vtable.add(ClassName) break p = self.class_parent.get(p) has_bitfield = ClassName in self.class_member_bitfields and any( self.class_member_bitfields[ClassName].get(name, 0) > 0 for name, _ in self.class_members.get(ClassName, []) ) all_members = self.class_members.get(ClassName, []) all_bitfield = all( self.class_member_bitfields.get(ClassName, {}).get(name, 0) > 0 for name, _ in all_members ) if all_members else False mangled_name = self._mangle_name(ClassName) if has_bitfield and all_bitfield: total_bits = sum(self.class_member_bitfields.get(ClassName, {}).get(name, 0) for name, _ in all_members) if total_bits <= 8: storage_type = ir.IntType(8) elif total_bits <= 16: storage_type = ir.IntType(16) elif total_bits <= 32: storage_type = ir.IntType(32) else: storage_type = ir.IntType(64) member_types = [storage_type] bitfield_offsets = {} current_bit_offset = 0 for name, _ in all_members: bit_width = self.class_member_bitfields.get(ClassName, {}).get(name, 0) bitfield_offsets[name] = current_bit_offset current_bit_offset += bit_width self.class_member_bitoffsets[ClassName] = bitfield_offsets else: member_types = [] if has_vtable: member_types.append(ir.PointerType(ir.IntType(8))) if ClassName in self.class_members: for _, member_type in self.class_members[ClassName]: if member_type is None: member_type = ir.IntType(32) member_types.append(member_type) else: member_types = [ir.IntType(8)] # 获取之前创建的 struct 并设置成员 struct_type = self.structs[ClassName] if ClassName in self.class_packed: struct_type.packed = True # 只在未设置 body 时设置,避免重复定义错误 if struct_type.elements is None: struct_type.set_body(*member_types) def _create_Vtable_globals(self): for ClassName, methods in self.class_methods.items(): if ClassName in self.Vtables: continue if not methods: continue if ClassName not in self.class_vtable and ClassName not in self._cross_module_vtable_classes: continue VtableType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) Vtable = ir.GlobalVariable(self.module, VtableType, name=self._mangle_name(f"{ClassName}_Vtable")) Vtable.initializer = ir.Constant(VtableType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) Vtable.linkage = 'internal' self.Vtables[ClassName] = Vtable def _fix_global_var_init(self): for gv_name, gv in list(self.module.globals.items()): if not isinstance(gv, ir.GlobalVariable): continue if gv.linkage in ('external', 'extern_weak'): continue needs_fix = False if gv.initializer is None: needs_fix = True elif hasattr(gv.initializer, 'constant') and isinstance(gv.initializer.constant, _Undefined): needs_fix = True elif isinstance(gv.initializer, ir.Constant): if isinstance(gv.initializer.constant, list): if any(hasattr(c, 'constant') and isinstance(c.constant, _Undefined) for c in gv.initializer.constant): needs_fix = True if not needs_fix: continue gv.initializer = ir.Constant(gv.value_type, None) if gv_name.startswith('str_const_') or gv_name.endswith('_Vtable') or gv_name.endswith('_str'): gv.linkage = 'internal' def _zero_value_for_type(self, var_type): if isinstance(var_type, ir.IntType): return ir.Constant(var_type, 0) elif isinstance(var_type, (ir.FloatType, ir.DoubleType)): return ir.Constant(var_type, 0.0) elif isinstance(var_type, ir.PointerType): return ir.Constant(var_type, None) elif isinstance(var_type, ir.ArrayType): elem_zv = self._zero_value_for_type(var_type.element) if elem_zv is None: return None return ir.Constant(var_type, [elem_zv] * var_type.count) elif isinstance(var_type, ir.BaseStructType): if var_type.elements is not None and len(var_type.elements) > 0: elem_zvs = [self._zero_value_for_type(m) for m in var_type.elements] if any(zv is None for zv in elem_zvs): return None return ir.Constant(var_type, elem_zvs) return None return None def _set_Vtable_initializers(self): for ClassName, methods in self.class_methods.items(): if ClassName not in self.Vtables: continue if not methods: continue struct_type = self.structs.get(ClassName) if not struct_type: continue Vtable = self.Vtables[ClassName] Vtable_entries = [] for MethodName in methods: short_name = MethodName.split('.')[-1] if '.' in MethodName else MethodName full_MethodName = f"{ClassName}.{short_name}" func = None if full_MethodName in self.functions: func = self.functions[full_MethodName] if func is None: parent_cls = self.class_parent.get(ClassName) while parent_cls: parent_full = f"{parent_cls}.{short_name}" if parent_full in self.functions: func = self.functions[parent_full] break parent_cls = self.class_parent.get(parent_cls) if func is None: Vtable_entries.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) continue Vtable_entries.append(func.bitcast(ir.PointerType(ir.IntType(8)))) Vtable.initializer = ir.Constant(Vtable.type.pointee, Vtable_entries)