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 vast as ast import llvmlite.ir as ir from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo 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.includes import t as _t from lib.core.SymbolUtils import IsTModule 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 == 'char': # char(x) → i8(与 int()/bool()/float()/double() 并列的内建标量强转; # char = t.CChar = i8) if Node.args: Val = self.HandleExprLlvm(Node.args[0]) if Val: t8_char: ir.Type = ir.IntType(8) if isinstance(Val.type, ir.IntType): if Val.type.width < 8: return Gen.builder.zext(Val, t8_char, name="zext_char") elif Val.type.width > 8: return Gen.builder.trunc(Val, t8_char, name="trunc_char") return Val elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): return Gen.builder.fptosi(Val, t8_char, name="float_to_char") return ir.Constant(ir.IntType(8), 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 # str 是类型名(= t.CChar* = i8*),str(x) ≡ (str)(x) 类型转换: # 整型(u64/sizet 地址、char 码)→ inttoptr,与 TPV # CallGenDispatch._tc_h3_stdarg_cast 的 str 分支同语义 # (CallGenCtor._translate_t_type_cast 的「整数 → 指针: inttoptr」)。 # 曾用 _EmitStringFromValue 把整数 snprintf 进栈缓冲并返回栈地址 # → free/写入即 bad-free(StubMergerBuild.vp 崩溃根因)。 if isinstance(arg_val.type, ir.IntType): return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="str_int2ptr") 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) — 运行时 vtable 继承链比较(2026-08-10) # CVTable 对象头部 vtable 指针唯一标识类;isinstance = 对象 vtable # ∈ 目标类及其 MRO 祖先的 vtable 集合(运行时真多态判断)。 # NoVTable/基础类型/无法解析时回退编译时静态判断。 return self._HandleIsinstanceLlvm(Node) 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 # ============================================================ # isinstance - 运行时 vtable 子类集合比较(2026-08-10) # # CVTable 对象头部 vtable 指针唯一标识对象**实际类**(创建对象时写入 # 实际类的 vtable)。isinstance(obj, Cls) 语义 = Cls 是实际类的祖先 # (实际类 == Cls 或继承自 Cls)。运行时只能拿到实际类 vtable 地址, # 无法向上推导祖先链,因此在编译期沿 class_parents 反向 BFS 枚举 # Cls 的全部已知子类(含自身),比较 obj->vtable ∈ {Cls 子类 vtable}。 # class_parents 由 Phase1 声明阶段对全部可达文件填充,子类集合完整。 # NoVTable / 基础类型 / 无 vtable 可解析 → 回退编译时静态判断。 # ============================================================ def _HandleIsinstanceLlvm(self, Node: ast.Call) -> ir.Value: """isinstance(obj, Cls):运行时 vtable 继承链比较(元组 Cls 支持 or 合并)""" Gen: LlvmGeneratorMixin = self.Trans.LlvmGen if len(Node.args) < 2: return ir.Constant(ir.IntType(1), 0) ObjVal: ir.Value | None = self.HandleExprLlvm(Node.args[0]) if ObjVal is None: return ir.Constant(ir.IntType(1), 0) ClsNames: list[str] = self._ExtractIsinstanceClassNames(Node.args[1]) if not ClsNames: return ir.Constant(ir.IntType(1), 0) Res: ir.Value | None = None for CN in ClsNames: R: ir.Value = self._IsinstanceOne(Gen, ObjVal, CN) if Res is None: Res = R else: Res = Gen.builder.or_(Res, R, name="isinstance_or") return Res if Res is not None else ir.Constant(ir.IntType(1), 0) def _ExtractIsinstanceClassNames(self, TypeNode: ast.expr) -> list[str]: """从 isinstance 第二参数提取类名列表(支持单类 / (A, B) 元组 / 泛型特化)""" if isinstance(TypeNode, ast.Tuple): names: list[str] = [] for elt in TypeNode.elts: names.extend(self._ExtractIsinstanceClassNames(elt)) return names if isinstance(TypeNode, ast.Name): return [TypeNode.id] if isinstance(TypeNode, ast.Attribute): return [TypeNode.attr] if isinstance(TypeNode, ast.Subscript): spec = self.Trans.ExprCallHandle._ResolveGenericSlice(TypeNode) if spec: return [spec] return [] def _IsinstanceOne(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: """单类判断:优先运行时 vtable 比较,失败回退编译时静态判断""" VtableAddrs: list = self._CollectSubclassVtables(Gen, ClassName) if VtableAddrs: try: return self._EmitVtableCompare(Gen, ObjVal, VtableAddrs) except Exception: pass return self._IsinstanceCompileTime(Gen, ObjVal, ClassName) def _CollectSubclassVtables(self, Gen, ClassName: str) -> list: """收集目标类及**所有已知子类**的 vtable 全局地址(沿 class_parents 反向 BFS)。 isinstance(obj, Cls) 的正确语义:obj 头部 vtable 标识对象实际类 C, 判断 Cls 是否 ∈ C 的祖先链。运行时无法从 C_vtable 向上推导祖先, 因此在编译期反向枚举 Cls 的子树(含 Cls 自身)的 vtable 地址, 运行时比较 obj->vtable ∈ 该集合(C 是 Cls 子类 → 命中)。 """ addrs: list = [] # 反向子类表:父类名 -> [子类名...](class_parents 由 Phase1 全量填充) children: dict[str, list[str]] = {} for cn, parents in getattr(Gen, 'class_parents', {}).items(): for p in parents: children.setdefault(p, []).append(cn) queue: list[str] = [ClassName] seen: set[str] = set() while queue: cn: str = queue.pop(0) if cn in seen: continue seen.add(cn) vt = self._ResolveVtableAddr(Gen, cn) if vt is not None: addrs.append(vt) for ch in children.get(cn, []): if ch not in seen: queue.append(ch) return addrs def _ResolveVtableAddr(self, Gen, ClassName: str): """解析类 ClassName 的 vtable 全局地址(本地 Vtables / 跨模块外部引用)。 仅对 CVTable 类返回地址;NoVTable 类(class_vtable 与 _cross_module_vtable_classes 均不含)没有 vtable 全局变量, 返回 None 让调用方回退编译时静态判断(避免生成对不存在的 sha1.Class_Vtable 符号的引用导致链接失败)。 """ has_vtable: bool = (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) if not has_vtable: return None vt = Gen.Vtables.get(ClassName) if hasattr(Gen, 'Vtables') else None if vt is not None: return vt sha1_map: dict = getattr(Gen, 'class_sha1_map', {}) sha1 = sha1_map.get(ClassName) if not sha1: return None vname: str = f"{sha1}.{ClassName}_Vtable" if vname in Gen.module.globals: return Gen.module.globals[vname] try: # 外部引用声明为 i8*(而非 [N x i8*]),避免与定义模块 # 不同长度的 array 类型在链接时 type mismatch ext = ir.GlobalVariable(Gen.module, ir.PointerType(ir.IntType(8)), name=vname) ext.linkage = 'external' return ext except Exception: return None def _EmitVtableCompare(self, Gen, ObjVal: ir.Value, VtableAddrs: list) -> ir.Value: """运行时:读对象头部 vtable 指针,与集合中每个 vtable 地址比较""" ObjPtr: ir.Value = ObjVal if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): # 二级指针(变量 alloca / 联合 Class|t.CPtr)→ 先 load 出对象指针 ObjPtr = Gen._load(ObjVal, name="isinstance_load") if not isinstance(ObjPtr.type, ir.PointerType): return self._IsinstanceCompileTime(Gen, ObjVal, '') ObjAsVt: ir.Value = Gen.builder.bitcast(ObjPtr, ir.PointerType(ir.PointerType(ir.IntType(8))), name="isinstance_cast") VtPtr: ir.Value = Gen._load(ObjAsVt, name="isinstance_vt") Res: ir.Value | None = None for addr in VtableAddrs: if addr.type == ir.PointerType(ir.IntType(8)): AddrI8: ir.Value = addr else: AddrI8 = Gen.builder.bitcast(addr, ir.PointerType(ir.IntType(8)), name="vt_addr") Cmp: ir.Value = Gen.builder.icmp_unsigned('==', VtPtr, AddrI8, name="isinstance_cmp") if Res is None: Res = Cmp else: Res = Gen.builder.or_(Res, Cmp, name="isinstance_or") return Res if Res is not None else ir.Constant(ir.IntType(1), 0) def _IsinstanceCompileTime(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: """编译时静态判断:obj 静态类型 == ClassName 或其 MRO 后代 → 1,否则 0""" if ClassName: ObjType: ir.Type = ObjVal.type if isinstance(ObjType, ir.PointerType): ObjType = ObjType.pointee if isinstance(ObjType, ir.IdentifiedStructType): ShortName: str = Gen._extract_short_name(ObjType.name) mro: list = Gen._get_mro_chain(ClassName) if hasattr(Gen, '_get_mro_chain') else [ClassName] if ShortName == ClassName or ShortName in mro: return ir.Constant(ir.IntType(1), 1) # obj 是 ClassName 的子类(obj 的 MRO 含 ClassName) obj_mro: list = Gen._get_mro_chain(ShortName) if hasattr(Gen, '_get_mro_chain') else [] if ClassName in obj_mro: return ir.Constant(ir.IntType(1), 1) # 无法判断 → 保守 false return ir.Constant(ir.IntType(1), 0) 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 # len(obj.attr):obj 可为 self 或任意局部变量(VpiReader 的 # len(simp.names) 即非 self 场景)。字段静态类型若退化为 i8* # (跨模块 stub 的 rawptr[list[...]],stub 不注入字段类型),通用 # strlen 分支会把 list 容器头当字符串扫 → 垃圾长度 → 越界 get → # 读任意地址(VpiReader.vp _ParseVpiIntoModuleImpl 读 -1 根因)。 # 此处按字段的 class_member_element_class 侧信道(记录 list 特化名) # 分派到 list.__len__;attr__len 计数成员特例(TypeGenCType)保留。 if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name): obj_name = arg_node.value.id attr_name = arg_node.attr len_member = f'{attr_name}__len' current_class = self.Trans._CurrentCpythonObjectClass if obj_name == 'self' else None if current_class is None: _vsc: dict = Gen.var_struct_class if Gen.var_struct_class else {} _gsc: dict = getattr(Gen, 'global_struct_class', {}) or {} current_class = _vsc.get(obj_name) or _gsc.get(obj_name) # 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如 # 'a351387236c6ce1c.TypeGenCType'),而 class_members / # class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离 # 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss, # 整个 obj.attr 分支(含 list.__len__ 修复)被跳过,fall through # 到 strlen(stdint.vp 0xC0000005 崩溃根因)。 bare_cls: str | None = current_class if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.': bare_cls = bare_cls[17:] if bare_cls and bare_cls in Gen.class_members: for m_name, m_type in Gen.class_members[bare_cls]: if m_name == len_member: self_val = Gen._get_var_ptr(obj_name) if self_val: self_ptr = Gen._load(self_val, name="obj_for_len") offset = Gen._get_member_offset(len_member, bare_cls) len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{len_member}_ptr") return Gen._load(len_ptr, name=f"{obj_name}_{len_member}") # 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段 # 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through # 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get # → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class # 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。 field_cls: str | None = None _elem_map: dict = getattr(Gen, 'class_member_element_class', {}) for _mro_cls in Gen._get_mro_chain(bare_cls): # class_member_element_class 的 key 是裸类名(如 'TypeGenBase'), # _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。 _mro_key: str = _mro_cls if len(_mro_cls) > 17 and _mro_cls[16] == '.': _mro_key = _mro_cls[17:] _sub: dict = _elem_map.get(_mro_key, {}) if attr_name in _sub: field_cls = _sub[attr_name] break if field_cls and (field_cls == 'list' or field_cls.startswith('list[')): self_val = Gen._get_var_ptr(obj_name) if self_val: self_ptr = Gen._load(self_val, name="obj_for_len") offset = Gen._get_member_offset(attr_name, bare_cls) if offset is not None: list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr") list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}") # 诊断增强(不改变任何成功路径):字段身份(class_member_element_class) # 说是 list 容器,但按 _get_member_offset 取出的字段静态类型不是 # list 特化接收者(典型为 i32,即偏移落在 lineno/col_offset 等 i32 字段) # 时,llvmlite 只会抛出无上下文的 # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 # 此处仅在"该调用本就会抛 TypeError 终止"时生效,把类/字段/偏移/ # 接收者类型一并报出,用于判定 class_members 偏移与 struct 布局不一致。 try: result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) except TypeError as _e_len: raise TypeError( f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:" f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};" f"(成员偏移与 struct 布局不一致)原错误: {_e_len}") if result is not None: return result 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: # 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])): # 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。 raise ValueError( f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}" f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 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") # 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。 raise ValueError( f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析" f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 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) # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。 return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type)) 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 = "" 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: _enum_bits: int = Gen._enum_int_type(enum_type_name).width type_str = f"" 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"" 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 _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) # 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type # (typedef/T*/struct 同一规则源,解析不出即 fail-fast),无参数走 # _infer_vararg_type_from_context(无注解可推断时保持 i32 默认)。 target_type: ir.Type = self._resolve_vararg_type(args[0], Gen) if args \ else 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_result_{Gen._va_arg_counter}" return result def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type: """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。 解析顺序(与注解解析同一规则源,避免各处手抄一份而漏项): 1) str/bytes/CPtr → i8*;类名(Gen.structs)→ 该类指针 2) CTypeRegistry.GetClassByName/ResolveName(t.* 类型类与 C 基本类型名) 3) CTypeInfo.FromNode:builtins/库/符号表 typedef(uint64/int64/sizet/ size_t…)、`T*` 指针后缀(parser 已展开为 `T | t.CPtr`)等 fail-fast:以上均解析不出 LLVM 类型时直接报错终止,**绝不回退 i32**。 回退 i32 的后果不是"精度差一点"而是**读错变参槽**:Win64 变参槽固定 8 字节, emit_va_arg 对 <64 位整数会生成 "va_arg i64 → trunc to i32",于是 `va_arg(ap, uint64)` 只保留低 32 位、高 32 位被丢弃。viperlib vsnprintf 的 `%016llx` 分支(`u_num_l: uint64 = va_arg(ap, uint64)`)因此输出 0x00000000XXXXXXXX —— llvmlite.ConstFloat 用该文本作"浮点常量位模式名", 所有 double 字面量遂退化为「真值低 32 位零扩展」(1.0/2.5/3.0 的低 32 位 全 0 ⇒ 全变成 0.0,除法/比较结果错乱)。 """ target_type: ir.Type | None = None 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 # 常规分支未命中:走注解解析同一规则源(typedef / `T*` 指针后缀 / t.* 组合写法)。 # 该规则源对未收录的名字会退化成 `%"{mod}.名"*` 假结构体(CStruct),故显式判掉。 if target_type is None: type_info: CTypeInfo | None = CTypeInfo.FromNode(type_arg, self.Trans.SymbolTable) if type_info is not None and type_info.BaseType is not None \ and not isinstance(type_info.BaseType, _t.CStruct): target_type = Gen._ctype_to_llvm(type_info) if target_type is None: type_name_dbg: str = type(type_arg).__name__ if isinstance(type_arg, ast.Name): type_name_dbg = type_arg.id elif isinstance(type_arg, ast.Attribute): type_name_dbg = type_arg.attr raise TypeError( f"[fast-fail] va_arg/arg 的类型参数 '{type_name_dbg}' 解析不出 LLVM 类型:" f"拒绝回退 i32(Win64 变参槽 8 字节,i32 会 trunc 掉高 32 位," f"如 va_arg(ap, uint64) 只留低 32 位)。请检查该类型名是否已声明/import。") return target_type def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type: """从当前赋值上下文推断 va_arg/arg 的目标类型(无显式类型参数时用) 有赋值注解时与显式类型参数走同一解析入口(含 typedef/T*/struct;解析不出即 fail-fast,避免 i32 回退读错 8 字节变参槽);无注解可推断时保持 i32 默认 (`va_arg(ap)` 读 i32 的既有语义,见 viperlib vsnprintf)。 """ 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 is not None: return self._resolve_vararg_type(ann, Gen) return ir.IntType(32) 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)