Files
TransPyC/lib/core/LLVMCG/StructGen.py

464 lines
22 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 self.SymbolTable.has(ClassName):
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 self.SymbolTable.has(ClassName):
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)