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

417 lines
18 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 os
import hashlib
import llvmlite.ir as ir
from llvmlite.ir.values import _Undefined
class VaArgInstruction(ir.Instruction):
def descr(self, buf):
ptr = self.operands[0]
buf.append("va_arg {0} {1}, {2}\n".format(
ptr.type, ptr.get_reference(), self.type))
class BaseGenMixin:
@staticmethod
def _parse_triple(triple):
"""解析目标三元组,返回平台信息字典"""
info = {
'is_windows': False, 'is_linux': False, 'is_macos': False,
'is_x86_64': False, 'is_arm64': False, 'is_32bit': False,
'ptr_size': 8, 'long_size': 8, 'wchar_t_size': 32,
}
if not triple:
return info
triple_lower = triple.lower()
# 检测操作系统
if 'windows' in triple_lower or 'win32' in triple_lower or 'mingw' in triple_lower or 'msvc' in triple_lower or 'cygwin' in triple_lower:
info['is_windows'] = True
# Windows LLP64: long = 32 位, wchar_t = 16 位
info['long_size'] = 32
info['wchar_t_size'] = 16
elif 'linux' in triple_lower:
info['is_linux'] = True
elif 'darwin' in triple_lower or 'macos' in triple_lower or 'apple' in triple_lower:
info['is_macos'] = True
# 检测架构
if 'x86_64' in triple_lower or 'amd64' in triple_lower or 'x64' in triple_lower:
info['is_x86_64'] = True
elif 'aarch64' in triple_lower or 'arm64' in triple_lower:
info['is_arm64'] = True
elif 'i386' in triple_lower or 'i686' in triple_lower or 'arm' in triple_lower:
info['is_32bit'] = True
info['ptr_size'] = 4
info['long_size'] = 32
if info['is_windows']:
info['wchar_t_size'] = 16
else:
info['wchar_t_size'] = 32
return info
def __init__(self, triple=None, datalayout=None):
self.module = ir.Module(name="transpyc_module")
if triple:
self.module.triple = triple
else:
self.module.triple = "x86_64-none-elf"
if datalayout:
self.module.data_layout = datalayout
else:
self.module.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
# 解析目标平台信息
self._platform_info = self._parse_triple(self.module.triple)
self.ptr_size = self._platform_info['ptr_size']
# 根据目标平台配置 t 模块中的平台相关类型大小
import t as _t
_t.configure_platform(self.module.triple)
self.builder = None
self.func = None
self.functions = {}
self.variables = {}
self._direct_values = {}
self._reg_values = {}
self.var_signedness = {}
self._unsigned_results = {}
self.string_const_counter = 0
self.Vtables = {}
self.class_methods = {}
self.class_members = {}
self.class_member_defaults = {}
self.class_member_signeds = {}
self.class_member_bitfields = {}
self.class_member_byteorders = {}
self.class_member_element_class = {}
self.class_member_bitoffsets = {}
self.structs = {}
self.typedef_int_widths = {} # typedef名称 -> 底层整数位宽 (如 'UINT' -> 32, 'BYTE' -> 8)
self._cross_module_vtable_classes = set()
self.class_vtable = set()
self.class_parent = {}
# Store member annotations for deferred type resolution (cross-module class references)
self.class_member_annotations = {}
self.loop_break_targets = []
self.loop_continue_targets = []
self.eh_jmp_buf_stack = []
self.eh_except_block_stack = []
self.eh_finally_stack = []
self.global_vars = set()
self.nonlocal_params = {}
self._variable_scope_stack = [] # Stack of outer scope variables for nested function nonlocal lookup
self.var_struct_class = {}
self.global_struct_class = {}
self.var_type_info = {}
self.var_const_flags = {}
self._current_source_file = None
self._current_source_lines = []
self._ns_cache = {}
self._type_cache = {}
self._typedef_cache = {}
self._func_sig_cache = {}
self.var_type_assignments = {}
self._temp_struct_ptrs = []
self._stop_iter_flag_param = None
self._local_heap_ptrs = []
self._var_to_heap_ptr = {}
self.known_return_types = {}
self._current_lineno = 0
self._current_node_info = ""
self.module_sha1 = None
self.ModuleSha1Map = {}
self._temp_dir = None
self._export_funcs = set()
self._export_extern_funcs = set()
self._current_module_func_names = set()
self.class_packed = set()
self._define_constants = {}
pi = self._platform_info
# 根据目标平台设置宏(声明式,从三元组推导)
if pi['is_windows']:
self._define_constants['_WIN32'] = 1
if not pi['is_32bit']:
self._define_constants['_WIN64'] = 1
self._define_constants['WIN64'] = 1
self._define_constants['WIN32'] = 1
if pi['is_linux']:
self._define_constants['__linux__'] = 1
if pi['is_macos']:
self._define_constants['__APPLE__'] = 1
self._define_constants['__MACH__'] = 1
if pi['is_x86_64']:
self._define_constants['__x86_64__'] = 1
self._define_constants['__x86_64'] = 1
self._define_constants['_M_X64'] = 1
elif pi['is_arm64']:
self._define_constants['__aarch64__'] = 1
self._define_constants['_M_ARM64'] = 1
if not pi['is_32bit'] and pi['is_linux']:
self._define_constants['__LP64__'] = 1
elif pi['is_32bit']:
self._define_constants['_ILP32'] = 1
self._define_constants['SIZEOF_VOID_P'] = pi['ptr_size']
self._define_constants['__SIZEOF_POINTER__'] = pi['ptr_size']
def find_struct_by_pointee(self, pointee):
"""根据 pointee 类型查找匹配的结构体,返回 (class_name, struct_type) 或 None"""
for class_name, struct_type in self.structs.items():
if pointee == struct_type:
return (class_name, struct_type)
return None
def _get_or_create_struct(self, name, source_sha1=None, packed=False):
clean_name = name.replace(' *', '').replace('*', '').replace(' ', '_').replace('const', 'const_').replace('volatile', 'volatile_')
if clean_name in self.structs:
existing = self.structs[clean_name]
if packed and isinstance(existing, ir.IdentifiedStructType) and not existing.packed:
existing.packed = True
return existing
if clean_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'Void', 'void'}:
return ir.IntType(8)
if hasattr(self, 'SymbolTable') and self.SymbolTable and clean_name in self.SymbolTable:
Entry = self.SymbolTable[clean_name]
if Entry.IsTypedef:
resolved_str = self._resolve_typedef(clean_name)
if resolved_str != clean_name:
return self._type_str_to_llvm(resolved_str)
if Entry.BaseType:
from lib.includes import t as _t
if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0:
resolved = self._resolve_ctype_to_str(Entry)
if resolved:
return self._type_str_to_llvm(resolved)
if Entry.IsRenum:
if clean_name in self.structs:
return self.structs[clean_name]
if Entry.IsEnum:
return ir.IntType(32)
if Entry.IsExceptionClass:
return ir.IntType(32)
if Entry.IsFunction:
return ir.PointerType(ir.IntType(8))
if Entry.IsVariable:
return ir.PointerType(ir.IntType(8))
if Entry.BaseType:
from lib.includes import t as _t
if isinstance(Entry.BaseType, type) and issubclass(Entry.BaseType, _t.CEnum):
return ir.IntType(32)
if Entry.BaseType is _t.CEnum:
return ir.IntType(32)
if source_sha1:
ns_name = f"{source_sha1}.{clean_name}"
elif hasattr(self, '_struct_sha1_map') and clean_name in self._struct_sha1_map:
ns_name = f"{self._struct_sha1_map[clean_name]}.{clean_name}"
else:
ns_name = self._mangle_name(clean_name)
st = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed)
self.structs[clean_name] = st
# 尝试解析 typedef 的底层整数位宽
width = self._resolve_typedef_int_width(clean_name)
if width is not None:
self.typedef_int_widths[clean_name] = width
return st
def _resolve_typedef_int_width(self, name: str):
"""解析 typedef 名称的底层整数位宽
通过 CTypeRegistry 和 BuiltinTypeMap 动态解析,
替代硬编码的 TYPEDEF_NAMES 映射表。
返回 int 位宽或 None无法解析
"""
from lib.includes.t import CTypeRegistry
from lib.core.Handles.HandlesBase import BuiltinTypeMap
# 1. 先查 BuiltinTypeMap覆盖 BYTEPTR 等指针别名)
bm = BuiltinTypeMap.Get(name)
if bm:
ctype_cls, ptr_count = bm
if ptr_count > 0:
return None # 指针类型,不是整数 typedef
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
return int(llvm_str[1:])
# 2. 再查 SymbolTable用户定义的 typedef
if hasattr(self, 'SymbolTable') and self.SymbolTable and name in self.SymbolTable:
Entry = self.SymbolTable[name]
if Entry.IsTypedef:
if Entry.BaseType:
from lib.includes import t as _t
if not isinstance(Entry.BaseType, (_t._CTypedef,)) and Entry.PtrCount == 0:
llvm_str = CTypeRegistry.CTypeToLLVM(type(Entry.BaseType) if isinstance(Entry.BaseType, _t.CType) else Entry.BaseType)
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
return int(llvm_str[1:])
if Entry.OriginalType:
from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo
if isinstance(Entry.OriginalType, _CTypeInfo) and Entry.OriginalType.BaseType:
if not isinstance(Entry.OriginalType.BaseType, (_t._CTypedef,)) and Entry.OriginalType.PtrCount == 0:
llvm_str = CTypeRegistry.CTypeToLLVM(type(Entry.OriginalType.BaseType) if isinstance(Entry.OriginalType.BaseType, _t.CType) else Entry.OriginalType.BaseType)
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
return int(llvm_str[1:])
# 3. 尝试 CTypeRegistry 直接按名称查找
ctype_cls = CTypeRegistry.GetClassByName(name)
if ctype_cls:
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
return int(llvm_str[1:])
# 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义)
_SPECIAL_TYPEDEFS = {
'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion',
'CStruct', 'enum', 'REnum', 'renum',
'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type',
}
if name in _SPECIAL_TYPEDEFS:
return 0 # 标记为 typedef但位宽为 0非整数
return None
def _set_node_info(self, node, extra_info=""):
if hasattr(node, 'lineno'):
self._current_lineno = node.lineno
else:
self._current_lineno = 0
if hasattr(node, 'col_offset'):
self._current_node_info = f"line {self._current_lineno}, col {node.col_offset}"
else:
self._current_node_info = f"line {self._current_lineno}"
if extra_info:
self._current_node_info += f" ({extra_info})"
def _set_source_info(self, filepath):
self._current_source_file = filepath
try:
with open(filepath, 'r', encoding='utf-8') as f:
self._current_source_lines = f.readlines()
except Exception: # 文件读取失败时回退为空行列表
self._current_source_lines = []
def _ns_hash(self):
if 'workspace' in self._ns_cache:
return self._ns_cache['workspace']
cwd = os.getcwd().replace('\\', '/').lower()
digest = hashlib.sha1(cwd.encode('utf-8')).hexdigest()[:12]
self._ns_cache['workspace'] = digest
return digest
def _skip_mangle(self, name):
skip_prefixes = ('llvm.', 'str_const_', 'printf', 'memset_pattern')
if any(name.startswith(p) for p in skip_prefixes):
return True
if '.' in name:
parts = name.split('.', 1)
if len(parts[0]) >= 8 and all(c in '0123456789abcdef' for c in parts[0]):
return True
return False
def _mangle_name(self, name):
if self._skip_mangle(name):
return name
if name in self._export_funcs:
return name
if self.module_sha1:
return f"{self.module_sha1}.{name}"
return name
def _find_function(self, name):
if name in self.functions:
return self.functions[name]
g = self.module.globals.get(name)
if g and isinstance(g, ir.Function):
self.functions[name] = g
return g
mangled = self._mangle_func_name(name)
if mangled != name:
if mangled in self.functions:
self.functions[name] = self.functions[mangled]
return self.functions[mangled]
g = self.module.globals.get(mangled)
if g and isinstance(g, ir.Function):
self.functions[mangled] = g
self.functions[name] = g
return g
for sha1 in self.ModuleSha1Map.values():
prefixed = f"{sha1}.{name}"
if prefixed in self.functions:
self.functions[name] = self.functions[prefixed]
return self.functions[prefixed]
g = self.module.globals.get(prefixed)
if g and isinstance(g, ir.Function):
self.functions[prefixed] = g
self.functions[name] = g
return g
return None
def _has_function(self, name):
return self._find_function(name) is not None
def _get_or_declare_function(self, mangled_name, func_type):
for g in self.module.globals:
if isinstance(g, ir.Function) and g.name == mangled_name:
return g
return ir.Function(self.module, func_type, name=mangled_name)
def _get_function(self, name):
func = self._find_function(name)
return func
def _mangle_func_name(self, name, module_name=None):
if name in self._export_funcs:
return name
if name in self._export_extern_funcs and name not in self._current_module_func_names:
return name
# main 函数作为程序入口点,永远不混淆
if name == 'main':
return name
# Try class_sha1_map first for class methods (e.g., 'md5.__init__' -> class 'md5')
class_sha1_map = getattr(self, 'class_sha1_map', {})
if class_sha1_map:
if '.' in name:
base_class = name.split('.')[0]
if base_class in class_sha1_map:
sha1 = class_sha1_map[base_class]
return f"{sha1}.{name}"
elif name in class_sha1_map:
sha1 = class_sha1_map[name]
return f"{sha1}.{name}"
# If module_name is already a SHA1 hash (16 hex chars), use it directly
if module_name and len(module_name) == 16 and all(c in '0123456789abcdef' for c in module_name):
return f"{module_name}.{name}"
if module_name and module_name in self.ModuleSha1Map:
sha1 = self.ModuleSha1Map[module_name]
return f"{sha1}.{name}"
# Try parent package name (e.g., 'hashlib' -> 'hashlib.__init__')
if module_name:
init_name = f"{module_name}.__init__"
if init_name in self.ModuleSha1Map:
sha1 = self.ModuleSha1Map[init_name]
return f"{sha1}.{name}"
if self._skip_mangle(name):
return name
if self.module_sha1:
return f"{self.module_sha1}.{name}"
return name
def _get_source_line(self, lineno):
if 0 < lineno <= len(self._current_source_lines):
return self._current_source_lines[lineno - 1].rstrip('\r\n')
return None
def _get_node_info(self):
info = ""
if self._current_node_info:
info = f" [{self._current_node_info}]"
if self._current_source_file:
info += f" in {self._current_source_file}"
lineno = self._current_lineno
if lineno > 0:
info += f", line {lineno}:"
source_line = self._get_source_line(lineno)
if source_line:
info += f"\n{source_line}"
return info