Files
TransPyC/lib/core/Handles/HandlesExprBuiltin.py

1025 lines
59 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
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp
class ExprBuiltinHandle(BaseHandle):
_C_LIB_FUNCS: dict[str, object] = {
'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]),
'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]),
'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]),
'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
'abs': lambda: (ir.IntType(32), [ir.IntType(32)]),
'labs': lambda: (ir.IntType(64), [ir.IntType(64)]),
}
def _HandleBuiltinCallLlvm(self, Node: ast.Call) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not isinstance(Node.func, ast.Name):
return None
FuncName: str = Node.func.id
if FuncName == 'print':
return self._HandlePrintLlvm(Node)
elif FuncName == 'len':
if Node.args:
return self._HandleLenLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'sizeof':
if Node.args:
return self._HandleSizeofLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'abs':
if Node.args:
return self._HandleAbsLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'arg':
return self._HandleArgLlvm(Node.args)
elif FuncName == 'bool':
if Node.args:
Val = self.HandleExprLlvm(Node.args[0])
if Val:
if isinstance(Val.type, ir.IntType):
if Val.type.width == 1:
return Val
return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result")
elif isinstance(Val.type, ir.PointerType):
return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result")
return Val
return ir.Constant(ir.IntType(1), 0)
elif FuncName == 'int':
if Node.args:
Val = self.HandleExprLlvm(Node.args[0])
if Val:
if isinstance(Val.type, ir.IntType):
if Val.type.width < 32:
return Gen.builder.zext(Val, ir.IntType(32), name="cast_int")
elif Val.type.width > 32:
return Gen.builder.trunc(Val, ir.IntType(32), name="trunc_int")
return Val
elif isinstance(Val.type, ir.PointerType):
if isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8:
result = self._HandleAtoiLlvm(Val)
if result:
return result
elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fptosi(Val, ir.IntType(32), name="float_to_int")
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'float' or FuncName == 'double':
if Node.args:
Val = self.HandleExprLlvm(Node.args[0])
if Val:
target_type = ir.DoubleType() if FuncName == 'double' else ir.FloatType()
if isinstance(Val.type, (ir.FloatType, ir.DoubleType)):
if Val.type != target_type:
if isinstance(target_type, ir.DoubleType):
return Gen.builder.fpext(Val, target_type, name="fpext_double")
return Gen.builder.fptrunc(Val, target_type, name="fptrunc_float")
return Val
elif isinstance(Val.type, ir.IntType):
if Val.type.width == 1:
return Gen.builder.uitofp(Val, target_type, name="uitofp")
return Gen.builder.sitofp(Val, target_type, name="sitofp")
return ir.Constant(ir.DoubleType() if FuncName == 'double' else ir.FloatType(), 0.0)
elif FuncName == 'min':
if len(Node.args) >= 2:
a = self.HandleExprLlvm(Node.args[0])
b = self.HandleExprLlvm(Node.args[1])
if a and b:
is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType))
if is_float:
fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="min_int2float")
fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="min_int2float")
cmp = Gen.builder.fcmp_ordered('olt', fa, fb, name="min_cmp")
return Gen.builder.select(cmp, fa, fb, name="min_val")
else:
cmp = Gen.builder.icmp_signed('<', a, b, name="min_cmp")
return Gen.builder.select(cmp, a, b, name="min_val")
elif len(Node.args) == 1:
return self.HandleExprLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'max':
if len(Node.args) >= 2:
a = self.HandleExprLlvm(Node.args[0])
b = self.HandleExprLlvm(Node.args[1])
if a and b:
is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType))
if is_float:
fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="max_int2float")
fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="max_int2float")
cmp = Gen.builder.fcmp_ordered('ogt', fa, fb, name="max_cmp")
return Gen.builder.select(cmp, fa, fb, name="max_val")
else:
cmp = Gen.builder.icmp_signed('>', a, b, name="max_cmp")
return Gen.builder.select(cmp, a, b, name="max_val")
elif len(Node.args) == 1:
return self.HandleExprLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'str':
if Node.args:
arg_node = Node.args[0]
arg_val = self.HandleExprLlvm(arg_node)
if arg_val:
if isinstance(arg_val.type, ir.PointerType):
ClassName = self.Trans.ExprHandler._get_var_class(arg_node, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
if isinstance(arg_val.type, ir.PointerType) and isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8:
if ClassName in Gen.structs:
arg_val = Gen.builder.bitcast(arg_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_str")
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [arg_val], name=f"call_{ClassName}.__str__")
return str_val
return arg_val
return self._EmitStringFromValue(arg_val)
return ir.Constant(ir.IntType(8).as_pointer(), None)
elif FuncName == 'bytes':
# bytes() — 类型转换为 i8* 指针 (等同于 t.CPtr / str)
# bytes(int_addr) → inttoptr; bytes(ptr) → bitcast
if Node.args:
arg_val = self.HandleExprLlvm(Node.args[0])
if arg_val:
if isinstance(arg_val.type, ir.PointerType):
if isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8:
return arg_val
return Gen.builder.bitcast(arg_val, ir.IntType(8).as_pointer(), name="bytes_cast")
if isinstance(arg_val.type, ir.IntType):
return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="bytes_int2ptr")
return ir.Constant(ir.IntType(8).as_pointer(), None)
elif FuncName == 'input':
return self._HandleInputLlvm(Node)
elif FuncName == 'isinstance':
# isinstance(obj, Type) — 静态类型系统下编译时解析
# TransPyC 是静态类型系统,运行时无 RTTI。
# 对于编译时已知类型,若变量类型是目标类型的子类则生成 true否则 false。
# 若无法确定(如基类指针指向子类),保守生成 false。
# 注意:这主要让编译时辅助方法(如 c.py 中的 Asm._parse_asm_descr能通过编译
# 这些方法不在运行时被调用,行为不正确不影响程序。
if len(Node.args) >= 2:
ObjVal: ir.Value | None = self.HandleExprLlvm(Node.args[0])
TypeNode: ast.expr = Node.args[1]
TargetClassName: str = ''
if isinstance(TypeNode, ast.Name):
TargetClassName = TypeNode.id
elif isinstance(TypeNode, ast.Attribute):
TargetClassName = TypeNode.attr
if ObjVal is not None and TargetClassName:
ObjType: ir.Type = ObjVal.type
# 解引用指针获取 pointee 类型
if isinstance(ObjType, ir.PointerType):
ObjType = ObjType.pointee
# 检查 pointee 是否是目标结构体类型
if isinstance(ObjType, ir.IdentifiedStructType):
ObjStructName: str = getattr(ObjType, 'name', '')
ShortName: str = ObjStructName.split('.')[-1] if '.' in ObjStructName else ObjStructName
if ShortName == TargetClassName:
return ir.Constant(ir.IntType(1), 1)
# 检查 Gen.var_struct_class
if isinstance(Node.args[0], ast.Name):
VarClassName: str | None = Gen.var_struct_class.get(Node.args[0].id)
if VarClassName == TargetClassName:
return ir.Constant(ir.IntType(1), 1)
return ir.Constant(ir.IntType(1), 0)
return ir.Constant(ir.IntType(1), 0)
elif FuncName == 'ord':
if Node.args:
return self._HandleOrdLlvm(Node.args[0])
return ir.Constant(ir.IntType(32), 0)
elif FuncName == 'chr':
if Node.args:
return self._HandleChrLlvm(Node.args[0])
return ir.Constant(ir.IntType(8), 0)
elif FuncName == 'malloc':
return self._HandleMallocLlvm(Node.args)
elif FuncName == 'realloc':
return self._HandleReallocLlvm(Node.args)
elif FuncName == 'free':
return self._HandleFreeLlvm(Node.args)
elif FuncName == 'memcpy':
return self._HandleMemcpyLlvm(Node.args)
elif FuncName == 'memset':
return self._HandleMemsetLlvm(Node.args)
elif FuncName == 'va_start':
return self._HandleVaStartLlvm(Node.args)
elif FuncName == 'va_end':
return self._HandleVaEndLlvm(Node.args)
elif FuncName == 'va_arg':
return self._HandleVaArgLlvm(Node.args)
elif FuncName == 'dir':
return self._HandleDirLlvm(Node)
elif FuncName == 'type':
return self._HandleTypeLlvm(Node)
return None
def _HandlePrintLlvm(self, Node: ast.Call) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
args: list[ast.expr] = Node.args
end_arg: ast.expr | None = None
sep_arg: ast.expr | None = None
if Node.keywords:
for kw in Node.keywords:
if kw.arg == 'end':
end_arg = kw.value
elif kw.arg == 'sep':
sep_arg = kw.value
sep_str: str = ' '
if sep_arg and isinstance(sep_arg, ast.Constant) and isinstance(sep_arg.value, str):
sep_str = sep_arg.value
end_str: str = '\n'
if end_arg:
if isinstance(end_arg, ast.Constant) and isinstance(end_arg.value, str):
end_str = end_arg.value
elif isinstance(end_arg, ast.Constant) and end_arg.value is None:
end_str = ''
if not args:
if end_str:
nl_fmt = Gen.emit_constant(end_str, 'string')
Gen.builder.call(printf_func, [nl_fmt], name="print_nl")
return ir.Constant(ir.IntType(32), 0)
has_fstring = any(isinstance(arg, ast.JoinedStr) for arg in args)
if has_fstring:
return self._HandlePrintWithFString(Node, printf_func, args, sep_str, end_str)
fmt_parts = []
fmt_args = []
for i, arg in enumerate(args):
if i > 0:
fmt_parts.append(sep_str)
val = self.HandleExprLlvm(arg)
if not val:
fmt_parts.append('(null)')
continue
if isinstance(val, ir.Constant) and isinstance(val.type, ir.PointerType) and val.constant is None:
fmt_parts.append('nil')
continue
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
fmt_parts.append('%s')
fmt_args.append(val)
elif isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
elem_ptr = Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="print_arr_to_ptr")
fmt_parts.append('%s')
fmt_args.append(elem_ptr)
elif isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen)
if not ClassName and isinstance(arg, ast.Name):
ClassName = Gen.var_struct_class.get(arg.id)
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
if isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8:
if ClassName in Gen.structs:
val = Gen.builder.bitcast(val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [val], name=f"call_{ClassName}.__str__")
fmt_parts.append('%s')
fmt_args.append(str_val)
else:
int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int")
fmt_parts.append('0x%llx')
fmt_args.append(int_val)
else:
int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int")
fmt_parts.append('%lld')
fmt_args.append(int_val)
elif isinstance(val.type, ir.IntType):
is_unsigned = self._is_val_unsigned(arg, val)
if val.type.width == 1:
fmt_parts.append('%d')
val = Gen.builder.zext(val, ir.IntType(32), name="zext_bool_print")
elif val.type.width == 8:
if self._is_char_type(arg):
char_var = Gen._allocaEntry(ir.IntType(8), name="print_char_buf")
Gen.builder.store(val, char_var)
fmt_parts.append('%c')
fmt_args.append(char_var)
continue
fmt_parts.append('%d')
if is_unsigned:
val = Gen.builder.zext(val, ir.IntType(32), name="zext_i8_print")
else:
val = Gen.builder.sext(val, ir.IntType(32), name="sext_i8_print")
elif val.type.width == 16:
fmt_parts.append('%u' if is_unsigned else '%d')
if is_unsigned:
val = Gen.builder.zext(val, ir.IntType(32), name="zext_i16_print")
else:
val = Gen.builder.sext(val, ir.IntType(32), name="sext_i16_print")
elif val.type.width == 32:
fmt_parts.append('%u' if is_unsigned else '%d')
elif val.type.width == 64:
fmt_parts.append('%llu' if is_unsigned else '%lld')
else:
fmt_parts.append('%d')
val = Gen.builder.zext(val, ir.IntType(32), name="zext_int_print")
fmt_args.append(val)
elif isinstance(val.type, ir.FloatType):
fmt_parts.append('%f')
fmt_args.append(Gen.builder.fpext(val, ir.DoubleType(), name="fpext_printf_float"))
elif isinstance(val.type, ir.DoubleType):
fmt_parts.append('%f')
fmt_args.append(val)
else:
fmt_parts.append('(unknown)')
fmt_parts.append(end_str)
fmt_str = ''.join(fmt_parts)
fmt_ptr = Gen._create_string_global(fmt_str)
call_args = [fmt_ptr] + fmt_args
return Gen.builder.call(printf_func, call_args, name="print_call")
def _HandlePrintWithFString(self, Node: ast.Call, printf_func: ir.Function, args: list[ast.expr], sep_str: str, end_str: str) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
for i, arg in enumerate(args):
if i > 0:
sep_const = Gen.emit_constant(sep_str, 'string')
Gen.builder.call(printf_func, [sep_const], name="print_sep")
if isinstance(arg, ast.JoinedStr):
str_val = self.HandleExprLlvm(arg)
if str_val:
Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None)
else:
ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
obj_val = self.HandleExprLlvm(arg)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if ClassName in Gen.structs:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [obj_val], name=f"call_{ClassName}.__str__")
Gen._RegisterTempPtr(str_val)
Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None)
else:
val = self.HandleExprLlvm(arg)
if val:
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if not (isinstance(pointee, ir.IntType) and pointee.width == 8):
try:
val = Gen._load(val, name="print_Load")
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
is_u = Gen._check_node_unsigned(arg)
if not is_u and id(val) in Gen._unsigned_results:
is_u = True
Gen._emit_printf([val], unsigned_flags=[is_u], sep=None, end=None)
if end_str:
end_const = Gen.emit_constant(end_str, 'string')
Gen.builder.call(printf_func, [end_const], name="print_end")
return ir.Constant(ir.IntType(32), 0)
def _is_char_type(self, arg_node: ast.AST) -> bool:
if isinstance(arg_node, ast.Call):
if isinstance(arg_node.func, ast.Name):
return arg_node.func.id in ('CChar', 'CUInt8T', 'CInt8T')
if isinstance(arg_node.func, ast.Attribute):
return arg_node.func.attr in ('CChar', 'CUInt8T', 'CInt8T')
return False
def _is_val_unsigned(self, arg_node: ast.AST, val: ir.Value) -> bool:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if isinstance(arg_node, ast.Name):
var_name: str = arg_node.id
signedness: object = Gen.var_signedness.get(var_name)
if signedness:
if isinstance(signedness, bool):
return signedness
return 'unsigned' in signedness
if isinstance(val.type, ir.IntType):
return False
return False
def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None:
if func_name not in self._C_LIB_FUNCS:
return None
ret_type: ir.Type
param_types: list[ir.Type]
ret_type, param_types = self._C_LIB_FUNCS[func_name]()
func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types)
func: ir.Function = ir.Function(Gen.module, func_type, name=func_name)
Gen.functions[func_name] = func
return func
def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name) and arg_node.value.id == 'self':
attr_name = arg_node.attr
len_member = f'{attr_name}__len'
current_class = self.Trans._CurrentCpythonObjectClass
if current_class and current_class in Gen.class_members:
for m_name, m_type in Gen.class_members[current_class]:
if m_name == len_member:
self_val = Gen._get_var_ptr('self')
if self_val:
self_ptr = Gen._load(self_val, name="self_for_len")
offset = Gen._get_member_offset(len_member, current_class)
len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{len_member}_ptr")
return Gen._load(len_ptr, name=f"self_{len_member}")
val = self.HandleExprLlvm(arg_node)
if not val:
return None
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if isinstance(pointee, ir.PointerType):
val = Gen._load(val, name="len_deref")
pointee = val.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__len__', val, Gen)
if result is not None:
return result
break
if isinstance(pointee, ir.ArrayType):
return ir.Constant(ir.IntType(64), pointee.count)
if isinstance(pointee, (ir.FloatType, ir.DoubleType, ir.IntType)) and pointee.width != 8:
return ir.Constant(ir.IntType(64), 0)
if val.type != ir.PointerType(ir.IntType(8)):
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
strlen_func = None
if 'strlen' in Gen.functions:
existing = Gen.functions['strlen']
if existing.ftype == correct_strlen_type:
strlen_func = existing
if not strlen_func:
try:
strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen')
Gen.functions['strlen'] = strlen_func
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if strlen_func:
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
return result
elif isinstance(val.type, ir.ArrayType):
return ir.Constant(ir.IntType(64), val.type.count)
return ir.Constant(ir.IntType(64), 0)
def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None:
"""递归解析嵌套的 opaque struct从 stub 文件中加载其定义"""
if visited is None:
visited = set()
if isinstance(struct_type, ir.PointerType):
self._resolve_nested_opaque_structs(struct_type.pointee, Gen, visited)
return
if not isinstance(struct_type, ir.IdentifiedStructType):
return
if struct_type in visited:
return
visited.add(struct_type)
if struct_type.elements is None or len(struct_type.elements) == 0:
# 尝试从 stub 加载 opaque struct
try:
st_name = struct_type.name
except Exception:
st_name = None
if st_name:
# 提取短名称(去掉模块前缀)
if '.' in st_name:
short_name = st_name.split('.', 1)[1]
else:
short_name = st_name
self.Trans.ImportHandler._TryLoadStructFromStub(short_name, Gen)
return
# 递归解析嵌套的 struct 成员
for elem in struct_type.elements:
self._resolve_nested_opaque_structs(elem, Gen, visited)
def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
type_name: str | None
if isinstance(Node, ast.Name):
type_name = Node.id
elif isinstance(Node, ast.Attribute):
type_name = Node.attr
else:
return ir.Constant(ir.IntType(64), 0)
if type_name not in Gen.structs:
# str/bytes 是指针类型 (char*/void*)sizeof = 指针大小
if type_name in ('str', 'bytes'):
return ir.Constant(ir.IntType(64), 8)
ctype_cls = CTypeRegistry.GetClassByName(type_name)
if ctype_cls is not None:
try:
ctype_inst = ctype_cls()
size_bits = getattr(ctype_inst, 'Size', 0) or 0
size_bytes = size_bits // 8
return ir.Constant(ir.IntType(64), size_bytes)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析类型失败: {_e}", "Exception")
resolved = CTypeRegistry.ResolveName(type_name)
if resolved:
ctype_cls, ptr_level = resolved
try:
ctype_inst = ctype_cls()
size_bits = getattr(ctype_inst, 'Size', 0) or 0
size_bytes = size_bits // 8
if ptr_level > 0:
size_bytes = 8
return ir.Constant(ir.IntType(64), size_bytes)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析类型失败: {_e}", "Exception")
return ir.Constant(ir.IntType(64), 0)
struct_type = Gen.structs[type_name]
if isinstance(struct_type, ir.PointerType):
struct_type = struct_type.pointee
if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen)
struct_type = Gen.structs.get(type_name, struct_type)
# 递归解析所有嵌套的 opaque struct确保 sizeof 计算正确
self._resolve_nested_opaque_structs(struct_type, Gen)
size = Gen._get_struct_size(struct_type)
if size > 0:
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
val: ir.Value | None = self.HandleExprLlvm(arg_node)
if not val:
return None
if isinstance(val.type, ir.PointerType):
pointee = val.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen)
if result is not None:
return result
break
if isinstance(val.type, ir.IntType):
neg_val = Gen.builder.neg(val, name="abs_neg")
is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg")
return Gen.builder.select(is_neg, neg_val, val, name="abs_result")
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
zero = ir.Constant(val.type, 0.0)
neg_val = Gen.builder.fneg(val, name="fabs_neg")
is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg")
return Gen.builder.select(is_neg, neg_val, val, name="fabs_result")
return None
def _HandleDirLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
return ir.Constant(ir.IntType(8).as_pointer(), None)
def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
type_str: str
if not Node.args:
type_str = "<class 'unknown' size=0 signed=false, ptr=false>"
else:
arg = Node.args[0]
enum_type_name = None
def _get_attr_path(node: ast.AST) -> str | None:
if isinstance(node, ast.Name):
return node.id
elif isinstance(node, ast.Attribute):
return f"{_get_attr_path(node.value)}.{node.attr}"
return None
if isinstance(arg, ast.Name):
arg_name = arg.id
Gen = self.Trans.LlvmGen
if arg_name in Gen.var_type_info:
type_info = Gen.var_type_info[arg_name]
if isinstance(type_info, dict) and type_info.get('type') == 'enum':
enum_type_name = type_info.get('name')
if not enum_type_name and self.Trans.SymbolTable.has(arg_name):
TypeInfo = self.Trans.SymbolTable[arg_name]
if TypeInfo.IsEnum:
enum_type_name = arg_name
elif isinstance(arg, ast.Attribute):
LastPart = None
enum_class_name = None
if isinstance(arg.value, ast.Name):
LastPart = arg.attr
if self.Trans.SymbolTable.has(LastPart):
AttrInfo = self.Trans.SymbolTable[LastPart]
if AttrInfo.IsEnumMember and AttrInfo.EnumName:
enum_type_name = AttrInfo.EnumName
elif isinstance(arg.value, ast.Attribute):
attr_path = _get_attr_path(arg)
if attr_path:
parts = attr_path.split('.')
if len(parts) >= 2:
enum_class_name = parts[-2]
LastPart = parts[-1]
qualified_name_dot = f"{enum_class_name}.{LastPart}"
qualified_name_under = f"{enum_class_name}_{LastPart}"
enum_member_found = None
for qname in (qualified_name_dot, qualified_name_under):
if self.Trans.SymbolTable.has(qname):
info = self.Trans.SymbolTable[qname]
if info.IsEnumMember and info.EnumName == enum_class_name:
enum_member_found = info
break
if not enum_member_found:
for key in self.Trans.SymbolTable:
if key == LastPart:
info = self.Trans.SymbolTable[key]
if info.IsEnumMember and info.EnumName == enum_class_name:
enum_member_found = info
break
if enum_member_found:
enum_type_name = enum_class_name
if enum_type_name:
type_str = f"<enum '{enum_type_name}' size=4 signed=true, ptr=false>"
else:
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
if isinstance(llvm_type, ir.PointerType):
pointee = llvm_type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
llvm_type = pointee
type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type)
type_str = f"<class '{type_info['name']}' size={type_info['size']} signed={str(type_info['signed']).lower()}, ptr={str(type_info['ptr']).lower()}>"
return Gen.emit_constant(type_str, 'string')
def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str):
prompt = Node.args[0].value
prompt_ptr = Gen.emit_constant(prompt, 'string')
printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
Gen.builder.call(printf_func, [prompt_ptr])
scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer")
result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer])
return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result")
def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
atoi_func: ir.Function = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))]))
return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result")
def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
val: ir.Value | None = self.HandleExprLlvm(expr_node)
if val and isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8:
return Gen._load(val, name="ord_result")
return ir.Constant(ir.IntType(32), 0)
def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
val: ir.Value | None = self.HandleExprLlvm(expr_node)
if val:
char_ptr = Gen._alloca(ir.IntType(8), name="chr_char")
Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr)
return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result")
return ir.Constant(ir.IntType(8).as_pointer(), None)
def _EmitStringFromValue(self, val: ir.Value) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
buf = Gen._alloca(ir.IntType(8), name="str_buf", size=ir.Constant(ir.IntType(32), 2))
null_ptr = Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 1)], name="null_byte")
Gen.builder.store(ir.Constant(ir.IntType(8), 0), null_ptr)
Gen.builder.store(val, buf)
return Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 0)], name="str_result")
buf = Gen._alloca(ir.IntType(8), name="str_buf", size=ir.Constant(ir.IntType(32), 32))
snprintf_func = Gen.get_or_declare_c_func('snprintf', ir.FunctionType(ir.IntType(32), [
ir.PointerType(ir.IntType(8)),
ir.IntType(64),
ir.PointerType(ir.IntType(8))
], var_arg=True))
fmt_ptr = Gen.emit_constant("%d", 'string')
Gen.builder.call(snprintf_func, [buf, ir.Constant(ir.IntType(64), 32), fmt_ptr, val])
return Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 0)], name="str_result")
return ir.Constant(ir.IntType(8).as_pointer(), None)
def _HandleMallocLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type)
param_type: ir.Type = malloc_func.type.pointee.args[0]
size_val: ir.Value | None = None
if args:
size_val = self.HandleExprLlvm(args[0])
if size_val and isinstance(size_val.type, ir.IntType):
if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width:
if size_val.type.width < param_type.width:
size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size")
else:
size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size")
if not size_val:
size_val = ir.Constant(param_type, 1)
result: ir.Value = Gen.builder.call(malloc_func, [size_val], name="malloc_result")
return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast")
def _HandleReallocLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
realloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)])
realloc_func: ir.Function = Gen._get_or_declare_func('realloc', realloc_type)
size_param_type: ir.Type = realloc_func.type.pointee.args[1]
ptr_val: ir.Value | None = None
size_val: ir.Value | None = None
if len(args) >= 2:
ptr_val = self.HandleExprLlvm(args[0])
size_val = self.HandleExprLlvm(args[1])
elif len(args) == 1:
size_val = self.HandleExprLlvm(args[0])
if not ptr_val:
ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None)
if isinstance(ptr_val.type, ir.PointerType) and not (isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8):
ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr")
if size_val and isinstance(size_val.type, ir.IntType):
if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width:
if size_val.type.width < size_param_type.width:
size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size")
else:
size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size")
if not size_val:
size_val = ir.Constant(size_param_type, 0)
result: ir.Value = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result")
return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast")
def _HandleFreeLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
free_func: ir.Function = Gen._get_or_declare_func('free', free_type)
if args:
var_name: str | None = None
if isinstance(args[0], ast.Name):
var_name = args[0].id
ptr_val: ir.Value | None = self.HandleExprLlvm(args[0])
if ptr_val is not None:
if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr:
registered_val = Gen._var_to_heap_ptr[var_name]
Gen._unregister_local_heap_ptr(registered_val)
del Gen._var_to_heap_ptr[var_name]
if isinstance(ptr_val.type, ir.PointerType):
if isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8:
pass
else:
ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr")
elif isinstance(ptr_val.type, ir.IntType):
ptr_val = Gen.builder.inttoptr(ptr_val, ir.PointerType(ir.IntType(8)), name="free_inttoptr")
Gen.builder.call(free_func, [ptr_val], name="free_call")
return ir.Constant(ir.IntType(32), 0)
def _HandleMemcpyLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if 'llvm.memcpy' not in Gen.functions:
memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
Gen.functions['llvm.memcpy'] = ir.Function(Gen.module, memcpy_type, name='llvm.memcpy')
if len(args) >= 3:
dst: ir.Value | None = self.HandleExprLlvm(args[0])
src: ir.Value | None = self.HandleExprLlvm(args[1])
size: ir.Value | None = self.HandleExprLlvm(args[2])
if dst and src and size:
i8ptr: ir.PointerType = ir.PointerType(ir.IntType(8))
if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr:
dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast")
elif isinstance(dst.type, ir.IntType):
if dst.type.width < 64:
dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr")
dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr")
if isinstance(src.type, ir.PointerType) and src.type != i8ptr:
src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast")
elif isinstance(src.type, ir.IntType):
if src.type.width < 64:
src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr")
src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr")
if isinstance(size.type, ir.IntType) and size.type.width < 64:
size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size")
isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0)
Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call")
return ir.Constant(ir.IntType(32), 0)
def _HandleMemsetLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
memset_func: ir.Function = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]))
if len(args) >= 3:
dst: ir.Value | None = self.HandleExprLlvm(args[0])
val: ir.Value | None = self.HandleExprLlvm(args[1])
size: ir.Value | None = self.HandleExprLlvm(args[2])
if dst and val is not None and size:
if isinstance(dst.type, ir.PointerType) and not (isinstance(dst.type.pointee, ir.IntType) and dst.type.pointee.width == 8):
dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast")
elif isinstance(dst.type, ir.IntType):
if dst.type.width < 64:
dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_memset_dst")
elif dst.type.width > 64:
dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst")
dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr")
if isinstance(val.type, ir.PointerType):
# 0 字面量被推断为 null 指针i8*memset 第二参数需要 i32
val = ir.Constant(ir.IntType(32), 0)
elif isinstance(val.type, ir.IntType) and val.type.width != 32:
if val.type.width < 32:
val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val")
else:
val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val")
if isinstance(size.type, ir.IntType) and size.type.width < 64:
size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size")
elif isinstance(size.type, ir.IntType) and size.type.width > 64:
size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size")
Gen.builder.call(memset_func, [dst, val, size], name="memset_call")
return ir.Constant(ir.IntType(32), 0)
def _HandleVaStartLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
return ir.Constant(ir.IntType(32), 0)
def _HandleVaEndLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
return ir.Constant(ir.IntType(32), 0)
def _HandleArgLlvm(self, args: list[ast.expr], CallNode: ast.Call | None = None) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
va_list_ptr: ir.Value | None = None
variadic_info: dict | None = getattr(Gen, '_va_arg_info', None)
if not variadic_info:
variadic_info = getattr(Gen, '_variadic_info', None)
if variadic_info:
va_list_ptr = variadic_info.get('va_list_ptr')
if not va_list_ptr:
vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args'
if vararg_name in Gen._direct_values:
va_list_ptr = Gen._direct_values[vararg_name]
elif vararg_name in Gen.variables:
va_list_ptr = Gen.variables[vararg_name]
if not va_list_ptr:
return ir.Constant(ir.IntType(32), 0)
target_type: ir.Type = ir.IntType(32)
if args:
type_arg: ast.expr = args[0]
if isinstance(type_arg, ast.Name):
type_name: str = type_arg.id
if type_name in ('str', 'bytes'):
target_type = ir.PointerType(ir.IntType(8))
elif type_name == 'CPtr':
target_type = ir.PointerType(ir.IntType(8))
elif type_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[type_name])
else:
ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
else:
resolved: tuple | None = CTypeRegistry.ResolveName(type_name)
if resolved is not None:
ctype_cls, ptr_level = resolved
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str:
base: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if base is not None:
for _ in range(ptr_level):
if isinstance(base, ir.VoidType):
base = ir.IntType(8).as_pointer()
else:
base = ir.PointerType(base)
target_type = base
elif isinstance(type_arg, ast.Attribute):
if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable):
attr_name: str = type_arg.attr
ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
else:
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
if assign_node:
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
if ann:
ann_name: str | None = None
if isinstance(ann, ast.Name):
ann_name = ann.id
elif isinstance(ann, ast.Attribute):
ann_name = ann.attr
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
ann_name = ExtractTypeNameFromBinOp(ann)
if ann_name:
ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
elif ann_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[ann_name])
if not getattr(Gen, '_va_arg_counter', None):
Gen._va_arg_counter = 0
Gen._va_arg_counter += 1
result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type)
if result is not None and getattr(result, 'name', None):
result.name = f"va_arg_result_{Gen._va_arg_counter}"
return result
def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type:
"""将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。"""
target_type: ir.Type = ir.IntType(32)
if isinstance(type_arg, ast.Name):
type_name: str = type_arg.id
if type_name in ('str', 'bytes'):
target_type = ir.PointerType(ir.IntType(8))
elif type_name == 'CPtr':
target_type = ir.PointerType(ir.IntType(8))
elif type_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[type_name])
else:
ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
else:
resolved_tuple: tuple | None = CTypeRegistry.ResolveName(type_name)
if resolved_tuple is not None:
ctype_cls, ptr_level = resolved_tuple
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str:
base: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if base is not None:
for _ in range(ptr_level):
if isinstance(base, ir.VoidType):
base = ir.IntType(8).as_pointer()
else:
base = ir.PointerType(base)
target_type = base
elif isinstance(type_arg, ast.Attribute):
if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable):
attr_name: str = type_arg.attr
ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
return target_type
def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type:
"""从当前赋值上下文推断 va_arg/arg 的目标类型。"""
target_type: ir.Type = ir.IntType(32)
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
if assign_node:
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
if ann:
ann_name: str | None = None
if isinstance(ann, ast.Name):
ann_name = ann.id
elif isinstance(ann, ast.Attribute):
ann_name = ann.attr
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
ann_name = ExtractTypeNameFromBinOp(ann)
if ann_name:
ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name)
if ctype_cls is not None:
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
target_type = resolved
elif ann_name in Gen.structs:
target_type = ir.PointerType(Gen.structs[ann_name])
return target_type
def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not args:
return ir.Constant(ir.IntType(32), 0)
# 求值 va_list 指针args[0]
va_list_ptr: ir.Value = self.HandleExprLlvm(args[0])
if va_list_ptr is None:
return ir.Constant(ir.IntType(32), 0)
# 解析目标类型
if len(args) >= 2:
target_type: ir.Type = self._resolve_vararg_type(args[1], Gen)
else:
target_type: ir.Type = self._infer_vararg_type_from_context(Gen)
if not getattr(Gen, '_va_arg_counter', None):
Gen._va_arg_counter = 0
Gen._va_arg_counter += 1
result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type)
if result is not None and getattr(result, 'name', None):
result.name = f"va_arg_explicit_{Gen._va_arg_counter}"
return result if result is not None else ir.Constant(ir.IntType(32), 0)