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

313 lines
17 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 llvmlite.ir as ir
class MemoryOpsMixin:
def _register_local_heap_ptr(self, val, ClassName=None, VarName=None):
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
self._local_heap_ptrs.append((val, 'struct', ClassName))
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
self._local_heap_ptrs.append((val, 'i8', ClassName))
if VarName:
self._var_to_heap_ptr[VarName] = val
def _unregister_local_heap_ptr(self, val):
self._local_heap_ptrs = [(v, t, cn) for v, t, cn in self._local_heap_ptrs if v is not val]
def _emit_local_heap_frees(self):
if not self._local_heap_ptrs or not self.builder:
return
# 创建一个集合来跟踪已经删除的对象
deleted_objects = set()
for ptr, ptr_type, ClassName in self._local_heap_ptrs:
# 先检查指针是否为 null
if ptr_type == 'i8':
# 对于 i8* 类型的指针,加载其值并检查是否为 null
Loaded_ptr = self.builder.load(ptr, name="load_ptr")
null_ptr = ir.Constant(ir.PointerType(ir.IntType(8)), None)
is_null = self.builder.icmp_unsigned('==', Loaded_ptr, null_ptr, name="is_null")
# 创建一个基本块来处理非 null 的情况
not_null_block = self.builder.append_basic_block(name="not_null")
# 创建一个基本块来处理下一个对象
next_block = self.builder.append_basic_block(name="next")
# 如果指针为 null跳转到下一个对象
self.builder.cbranch(is_null, next_block, not_null_block)
# 切换到非 null 的基本块
self.builder.position_at_end(not_null_block)
# 然后调用 __del__ 方法
if ClassName and self._has_function(f'{ClassName}.__del__'):
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
cast_ptr = self.builder.bitcast(ptr, ir.PointerType(self.structs[ClassName]), name=f"cast_{ClassName}")
else:
cast_ptr = ptr
if cast_ptr not in deleted_objects:
self.builder.call(self._get_function(f'{ClassName}.__del__'), [cast_ptr], name=f"call_{ClassName}.__del__")
deleted_objects.add(cast_ptr)
# 最后释放内存
if ptr_type == 'struct':
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="local_free_cast")
elif ptr_type == 'i8':
raw = ptr
else:
continue
free_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
free_func = self._get_or_declare_func('free', free_type)
self.builder.call(free_func, [raw])
# 如果是 i8* 类型的指针,跳转到下一个对象
if ptr_type == 'i8':
self.builder.branch(next_block)
# 切换到下一个对象的基本块
self.builder.position_at_end(next_block)
self._local_heap_ptrs = []
def _RegisterTempPtr(self, val):
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in self.structs.items():
if pointee == ST:
self._temp_struct_ptrs.append(val)
return
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
self._temp_struct_ptrs.append(val)
def _UnregisterTempPtr(self, val):
self._temp_struct_ptrs = [t for t in self._temp_struct_ptrs if t is not val]
def _EmitTempFrees(self):
if not self._temp_struct_ptrs or not self.builder:
return
for ptr in self._temp_struct_ptrs:
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="free_cast")
elif isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
raw = ptr
else:
continue
free_type = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
free_func = self._get_or_declare_func('free', free_type)
self.builder.call(free_func, [raw])
self._temp_struct_ptrs = []
def _ZeroConst(self, typ):
if isinstance(typ, ir.PointerType):
return ir.Constant(typ, None)
return ir.Constant(typ, 0)
def _alloca(self, typ, name='', align=None, size=None):
if align is None:
align = self._get_align(typ)
if size is not None:
instr = self.builder.alloca(typ, size=size, name=name)
else:
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
return instr
def _allocaEntry(self, typ, name='', align=None):
if align is None:
align = self._get_align(typ)
saved_block = self.builder.block
entry_block = self.func.entry_basic_block
if entry_block.instructions:
first_instr = entry_block.instructions[0]
self.builder.position_before(first_instr)
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
self.builder.position_at_end(saved_block)
else:
self.builder.position_at_start(entry_block)
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
self.builder.position_at_end(saved_block)
return instr
def _load(self, ptr, name='', align=None):
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
if isinstance(ptr.type, ir.PointerType):
pointee_type = ptr.type.pointee
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
width = self.typedef_int_widths[pointee_type.name]
if width > 0:
actual_type = ir.IntType(width)
bitcast_ptr = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
return self.builder.load(bitcast_ptr, name=name, align=align)
return self.builder.load(ptr, name=name, align=align)
def _Load_var(self, name):
if name in self._direct_values:
return self._direct_values[name]
if name in self.variables and self.variables[name] is not None:
VarPtr = self.variables[name]
if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return VarPtr
if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return VarPtr
return self._load(VarPtr, name=name)
if name in self.global_vars and name in self.module.globals:
GVar = self.module.globals[name]
self.variables[name] = GVar
if isinstance(GVar, ir.Function):
return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar
return self._load(GVar, name=name)
if name in self.module.globals:
GVar = self.module.globals[name]
self.variables[name] = GVar
if isinstance(GVar, ir.Function):
return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar
return self._load(GVar, name=name)
return None
def _store_var(self, name, value):
if name in self._direct_values:
old_val = self._direct_values.pop(name)
if name not in self.variables or self.variables[name] is None:
var = self.builder.alloca(old_val.type, name=name)
self._store(old_val, var)
self.variables[name] = var
if name in self.variables and self.variables[name] is not None:
self._store(value, self.variables[name])
else:
var = self.builder.alloca(value.type, name=name)
self._store(value, var)
self.variables[name] = var
def _ensure_alloca(self, name, llvm_type):
if name in self._direct_values:
val = self._direct_values.pop(name)
var = self.builder.alloca(llvm_type, name=name)
self._store(val, var)
self.variables[name] = var
return var
if name in self.variables and self.variables[name] is not None:
return self.variables[name]
var = self.builder.alloca(llvm_type, name=name)
self.variables[name] = var
return var
def _coerce_value(self, val, target_type):
if val.type == target_type:
return val
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
if isinstance(val.type, ir.IntType):
return val
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
if target_type.width < val.type.width:
return self.builder.trunc(val, target_type, name="trunc_store")
else:
# 使用 sext符号扩展以正确处理负数
return self.builder.sext(val, target_type, name="sext_store")
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.PointerType):
if isinstance(val, ir.Constant) and val.constant is None:
return ir.Constant(target_type, None)
return self.builder.bitcast(val, target_type, name="ptr_bitcast_store")
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.IntType):
if val.type.width < 64:
val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store")
return self.builder.inttoptr(val, target_type, name="int_to_ptr_store")
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType):
if isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == target_type.width:
return self.builder.load(val, name="load_ptr_as_int_store")
if target_type.width <= 8 and isinstance(val.type.pointee, ir.IntType):
Loaded = self.builder.load(val, name="load_ptr_byte_store")
if Loaded.type != target_type:
if isinstance(Loaded.type, ir.IntType) and isinstance(target_type, ir.IntType):
if target_type.width < Loaded.type.width:
return self.builder.trunc(Loaded, target_type, name="trunc_ptr_byte_store")
return self.builder.zext(Loaded, target_type, name="zext_ptr_byte_store")
return Loaded
return self.builder.ptrtoint(val, target_type, name="ptr_to_int_store")
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
return self.builder.fpext(val, target_type, name="fpext_store")
if isinstance(target_type, ir.IntType) and isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return self.builder.fptosi(val, target_type, name="fptosi_store")
if isinstance(target_type, (ir.FloatType, ir.DoubleType)) and isinstance(val.type, ir.IntType):
return self.builder.sitofp(val, target_type, name="sitofp_store")
if isinstance(target_type, ir.ArrayType) and isinstance(val.type, ir.ArrayType):
if target_type.count == val.type.count and target_type.element == val.type.element:
return val
if (isinstance(target_type.element, ir.IntType) and target_type.element.width == 8
and isinstance(val.type.element, ir.IntType) and val.type.element.width == 8):
if target_type.count > val.type.count:
padded = list(val.constant) if hasattr(val, 'constant') else []
if padded and len(padded) == val.type.count:
padded.extend([ir.Constant(ir.IntType(8), 0)] * (target_type.count - val.type.count))
return ir.Constant(target_type, padded)
elif target_type.count < val.type.count:
trimmed = list(val.constant) if hasattr(val, 'constant') else []
if trimmed and len(trimmed) == val.type.count:
return ir.Constant(target_type, trimmed[:target_type.count])
if isinstance(val.type, ir.PointerType) and not isinstance(target_type, ir.PointerType):
if val.type.pointee == target_type:
return self.builder.load(val, name="Load_struct_for_store")
if isinstance(val.type.pointee, ir.IdentifiedStructType) and isinstance(target_type, ir.IdentifiedStructType):
if val.type.pointee.name == target_type.name:
Loaded = self.builder.load(val, name="Load_struct_for_store")
if Loaded.type == target_type:
return Loaded
return None
def _store(self, val, ptr, align=None):
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
if isinstance(ptr.type, ir.PointerType):
target_type = ptr.type.pointee
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
if isinstance(val.type, ir.IntType):
width = self.typedef_int_widths[target_type.name]
if width > 0:
actual_type = ir.IntType(width)
bitcast_ptr = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast")
if val.type.width != actual_type.width:
if val.type.width > actual_type.width:
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
else:
val = self.builder.zext(val, actual_type, name="zext_typedef")
return self.builder.store(val, bitcast_ptr, align=align)
if val.type != target_type:
if isinstance(val, ir.Constant) and val.constant is None:
if isinstance(target_type, ir.PointerType):
val = ir.Constant(target_type, None)
elif isinstance(target_type, ir.IdentifiedStructType):
val = ir.Constant(ir.PointerType(target_type), None)
ptr = self.builder.bitcast(ptr, ir.PointerType(ir.PointerType(target_type)), name="null_struct_ptr_cast")
else:
coerced = self._coerce_value(val, target_type)
if coerced is not None:
val = coerced
else:
src_info = self._get_node_info()
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
return self.builder.store(val, ptr, align=align)
def _get_var_ptr(self, name):
if name in self._reg_values:
val = self._reg_values[name]
var = self._alloca(val.type, name=name)
self._store(val, var)
self.variables[name] = var
del self._reg_values[name]
return var
if name in self.variables and self.variables[name] is not None:
return self.variables[name]
return None