import t, c from stdint import * import ast import llvmlite import memhub import string import stdlib import lib.core.Gen.Translator as HT from . import CallGenBase # ============================================================ # CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 / # 变长参数 / memcpy / kwargs 调用辅助方法 # # 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。 # 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。 # @t.NoVTable:静态绑定。 # ============================================================ @t.NoVTable class CallGenLib(CallGenBase.CallGenBase): """C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)""" def _ensure_c_lib_declare(self, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, name: str) -> llvmlite.Function | t.CPtr: """为已知的 C 库函数创建 declare 声明""" if pool is None or mod is None or name is None: return None i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) ret_ty: llvmlite.LLVMType | t.CPtr = None params: int = 0 # 参数数量 if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0: ret_ty = i64_ty params = 1 elif string.strcmp(name, "memset") == 0: ret_ty = i8_ptr_ty params = 3 elif string.strcmp(name, "memcpy") == 0: ret_ty = i8_ptr_ty params = 3 elif string.strcmp(name, "memmove") == 0: ret_ty = i8_ptr_ty params = 3 elif string.strcmp(name, "memcmp") == 0: ret_ty = i32_ty params = 3 elif string.strcmp(name, "strcmp") == 0: ret_ty = i32_ty params = 2 elif string.strcmp(name, "strncmp") == 0: ret_ty = i32_ty params = 3 elif string.strcmp(name, "strcpy") == 0: ret_ty = i8_ptr_ty params = 2 elif string.strcmp(name, "strncpy") == 0: ret_ty = i8_ptr_ty params = 3 elif string.strcmp(name, "strcat") == 0: ret_ty = i8_ptr_ty params = 2 elif string.strcmp(name, "puts") == 0: ret_ty = i32_ty params = 1 elif string.strcmp(name, "atoi") == 0: ret_ty = i32_ty params = 1 elif string.strcmp(name, "atol") == 0: ret_ty = i64_ty params = 1 elif string.strcmp(name, "abs") == 0: ret_ty = i32_ty params = 1 elif string.strcmp(name, "labs") == 0: ret_ty = i64_ty params = 1 else: return None # 创建 declare fn: llvmlite.Function | t.CPtr = llvmlite.create_declare(pool, mod, name, ret_ty) if fn is None: return None # 添加参数 if params == 1: if string.strcmp(name, "abs") == 0: llvmlite.add_param(pool, fn, i32_ty, "n") elif string.strcmp(name, "labs") == 0: llvmlite.add_param(pool, fn, i64_ty, "n") else: llvmlite.add_param(pool, fn, i8_ptr_ty, "s") elif params == 2: llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") llvmlite.add_param(pool, fn, i8_ptr_ty, "src") elif params == 3: if string.strcmp(name, "memset") == 0: llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") llvmlite.add_param(pool, fn, i32_ty, "c") llvmlite.add_param(pool, fn, i64_ty, "n") else: llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") llvmlite.add_param(pool, fn, i8_ptr_ty, "src") llvmlite.add_param(pool, fn, i64_ty, "n") return fn def _promote_clib_call_args(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, call_name: str, head: llvmlite.Value | t.CPtr, count: int) -> llvmlite.Value | t.CPtr: """裸名 C 库函数实参按标准签名提升(B3)。 跨模块裸名调用(string.memset → @memset)时 _ensure_method_declare 用 实参类型建 declare:第三参字面量 32=i32 把定义 sizet(i64) 固化窄 → declare/call @memset(i8*,i32,i32) vs 定义 @memset(i8*,i32,i64), ABI 宽度不一致(VKernel process.vp B3)。调用点先把第三参窄整数 实参提升到 i64,declare 与 call 均按 i64 生成 → 与定义一致。 未命中白名单原样返回 head。 """ if pool is None or builder is None or call_name is None or head is None: return head # 仅裸名(无 '.' 分隔 → 无 sha1 前缀);带前缀走 vpi 定义侧解析 if string.strchr(call_name, 46) is not None: return head # 白名单:size_t(i64) 长度参数所在下标(与 _ensure_c_lib_declare 一致)。 # vsnprintf 非变参(第二参即 sizet 长度)——viperlib.vp `export def # vsnprintf(buf, size: sizet, fmt, ap)`,跨模块裸名调用时字面量 1024 # 固化 i32 vs 定义 i64,与 memset 同族。 size_idx: int = -1 if string.strcmp(call_name, "memset") == 0: size_idx = 2 elif string.strcmp(call_name, "memcpy") == 0: size_idx = 2 elif string.strcmp(call_name, "memmove") == 0: size_idx = 2 elif string.strcmp(call_name, "memcmp") == 0: size_idx = 2 elif string.strcmp(call_name, "strncmp") == 0: size_idx = 2 elif string.strcmp(call_name, "strncpy") == 0: size_idx = 2 elif string.strcmp(call_name, "strncat") == 0: size_idx = 2 elif string.strcmp(call_name, "vsnprintf") == 0: size_idx = 1 if size_idx < 0 or count <= size_idx: return head # 定位长度参数(index size_idx) cur: llvmlite.Value | t.CPtr = head idx: int = 0 while cur is not None and idx < size_idx: cur = cur.Next idx += 1 if cur is None or cur.Ty is None: return head i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) promoted: llvmlite.Value | t.CPtr = None match cur.Ty: case llvmlite.LLVMType.Int(bits): if bits >= 64: return head if cur.IsConst != 0: # 常量:直接 i64 常量(位模式保留,无符号长度语义) promoted = llvmlite.const_int(pool, 64, cur.IntVal) else: # SSA 值:zext 到 i64(size_t 无符号长度) promoted = llvmlite.build_zext(builder, cur, i64_ty) case _: return head if promoted is None: return head # 重建实参链表:index size_idx 替换为提升后的值 new_head: llvmlite.Value | t.CPtr = None new_tail: llvmlite.Value | t.CPtr = None cur2: llvmlite.Value | t.CPtr = head i2: int = 0 while cur2 is not None: node_v: llvmlite.Value | t.CPtr = cur2 if i2 == size_idx: node_v = promoted if new_head is None: new_head = node_v else: new_tail.Next = node_v new_tail = node_v cur2 = cur2.Next i2 += 1 return new_head # ============================================================ # _coerce_args_to_def_params - 跨模块调用实参按定义侧形参类型 coerce # # B3 同族通用化:_ensure_method_declare 用调用点实参类型建 declare,字面量 # 实参(i32 0/1016/512)把定义侧窄整型形参(i8/i16)固化宽(或把宽形参固化 # 窄)→ declare 与 define 宽度不一致(跨模块 IR/ABI 失配,x86-64 下侥幸正确: # 调用侧写 32 位寄存器清高 32 位,callee 按 i64 读高位是 0)。本函数在调用点 # 按定义侧(vpi 参数注解)重建/转换实参,随后 _ensure_method_declare 依 head # 建 declare → call/declare/define 三者一致。 # # 处理"带 sha1 前缀的被调方限定名"两种形态: # 1) {sha1}.{func} —— 模块级函数,实参段 0 即首参(无 self 占位); # 2) {sha1}.{cls}.{method} —— 类方法,vpi 注解段 0 为 self 空段,而实参 # 链首元素正是 self → 段下标与实参下标一一对应(与 # _ensure_method_declare 里直接以 pi 作段索引的约定一致)。 # 此前仅支持形态 1(遇 '.' 直接 return head),方法调用点实参从不按定义侧 # 扩宽 → `default_fs.win_read(0)` 传 i32 而 define 为 uint64(N5)。 # 裸名 C 库函数由 _promote_clib_call_args 与 _ensure_c_lib_declare 标准 # 签名处理。未命中/不可解原样保留(不兜底改型)。 # ============================================================ def _coerce_args_to_def_params(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, call_name: str, head: llvmlite.Value | t.CPtr, count: int) -> llvmlite.Value | t.CPtr: """把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链""" if pool is None or builder is None or call_name is None or head is None: return head # 函数内懒 import:与 _lookup_method_ret_ty_via_vpi 同源,规避顶层循环依赖 import lib.Stub.StubGen.VpiReader as VpiReader if self._is_sha1_mangled_call(call_name) == 0: return head # 拆 {sha1}.{rest}:rest 含 '.' 则为 {cls}.{method},否则为 {func} rest: str = call_name + 17 cls_nm: str = None meth_nm: str = rest dot_r: str = string.strchr(rest, 46) if dot_r is not None: cls_len: sizet = uint64(dot_r) - uint64(rest) if cls_len <= 0: return head cls_buf: bytes = stdlib.malloc(cls_len + 1) if cls_buf is None: return head string.strncpy(cls_buf, rest, cls_len) cls_buf[cls_len] = '\0' cls_nm = cls_buf meth_nm = dot_r + 1 if meth_nm is None or meth_nm[0] == '\0': if cls_nm is not None: stdlib.free(cls_nm) return head sha1_buf: bytes = stdlib.malloc(17) if sha1_buf is None: if cls_nm is not None: stdlib.free(cls_nm) return head string.strncpy(sha1_buf, call_name, 16) sha1_buf[16] = '\0' # 注意:不能用 self._vpi_method_param_annot —— 其形参校验含 # `cls_name is None → return None`,而模块级函数必传 cls_name=None, # 于是恒返回 None → 本函数永不生效(ABI 收敛首轮/二轮均未生效的根因)。 # 改为直接 ensure 加载 + 直查缓存(键 {sha1}.{func},与 cls_name=None 匹配)。 if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir) prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm) if prm_annots is None: stdlib.free(sha1_buf) if cls_nm is not None: stdlib.free(cls_nm) return head i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) new_head: llvmlite.Value | t.CPtr = None new_tail: llvmlite.Value | t.CPtr = None cur: llvmlite.Value | t.CPtr = head idx: int = 0 while cur is not None and idx < count: node_v: llvmlite.Value | t.CPtr = cur tgt_ty: llvmlite.LLVMType | t.CPtr = VpiReader._resolve_vpi_param_annot_seg( pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty) if tgt_ty is not None and cur.Ty is not None: node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty) if new_head is None: new_head = node_v else: new_tail.Next = node_v new_tail = node_v cur = cur.Next idx += 1 stdlib.free(sha1_buf) if cls_nm is not None: stdlib.free(cls_nm) return new_head # ============================================================ # _coerce_arg_value_to_ty - 单个实参 Value 转换到目标形参类型 # # 整型: # - 同宽 → 原样; # - 窄化(目标更窄,本簇主场景 i32→i8/i16)→ 常量按目标宽度重建常量, # SSA 用 trunc(trunc 与符号无关,窄化安全); # - 扩宽交由 _ensure_method_declare 既有"定义侧更宽才采用"逻辑 + 下遍 # 收敛时按形参 coerce(需 AST 判定符号性,本函数无 AST,不越权处理)。 # 指针:实参与目标皆指针且"泛型 i8* 性"不同时 bitcast(如 i32*→i8*), # 使 declare 指针类型与 define 一致;同为 i8* 或同为具体 struct 指针 # 则原样保留(避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声)。 # 其它(结构体值/void 等)→ 原样保留。不可转换亦原样返回(不兜底改型)。 # ============================================================ def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, val: llvmlite.Value | t.CPtr, tgt_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr: """把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回""" if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None: return val match val.Ty: case llvmlite.LLVMType.Int(bits_a): match tgt_ty: case llvmlite.LLVMType.Int(bits_b): if bits_b == bits_a: return val if bits_b < bits_a: if val.IsConst != 0: nv_c: llvmlite.Value | t.CPtr = llvmlite.const_int( pool, bits_b, val.IntVal) if nv_c is not None: return nv_c else: nv_t: llvmlite.Value | t.CPtr = llvmlite.build_trunc( builder, val, tgt_ty) if nv_t is not None: return nv_t # 扩宽(实参窄于形参,N5):字面量/默认值此前保持 i32 # 而 declare/define 已是 i64(_ensure_method_declare # 只改 declare 不改实参)→ 调用实参与签名失配。 # 仅编译期常量在此重建:const_int 按十进制值打印, # i32 -1 → i64 -1(sext 语义)、i32 4294967295 → # i64 4294967295(zext 语义),各自保持原类型数值, # 无需 AST 判符号性;SSA 值(非常量)判符号性需 AST, # 交由上游 coerce_to_type(本函数无 AST,不越权处理)。 if val.IsConst != 0: nv_w: llvmlite.Value | t.CPtr = llvmlite.const_int( pool, bits_b, val.IntVal) if nv_w is not None: return nv_w return val case _: pass case llvmlite.LLVMType.Ptr(pointee_a): match tgt_ty: case llvmlite.LLVMType.Ptr(pointee_b): ia: int = 0 match pointee_a: case llvmlite.LLVMType.Int(pa_bits): if pa_bits == 8: ia = 1 case _: pass ib: int = 0 match pointee_b: case llvmlite.LLVMType.Int(pb_bits): if pb_bits == 8: ib = 1 case _: pass if ia != ib: nv_p: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( builder, val, tgt_ty) if nv_p is not None: return nv_p case _: pass case _: # 聚合值 → 指针:rvalue 结构体/数组落栈取址(N18 同源补齐)。 # FuncGenDef 形参一律 lowering 为 Ptr(struct/array),Viper 无 # "按值传结构体"形参;跨模块(declare 由 stub 建、found_func # 为 None)时上游 coerce_to_type 不参与,故在此按 tgt_ty 为 # 指针 + 实参为聚合形态补 materialize。 match tgt_ty: case llvmlite.LLVMType.Ptr(_): vk_a: int = val.Ty.kind() if vk_a == llvmlite.LLVMType.Struct or vk_a == llvmlite.LLVMType.Array: tmp_a: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry( builder, val.Ty) if tmp_a is not None: llvmlite.build_store(builder, val, tmp_a) return tmp_a case _: pass return val def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr, func_name: str, node: ast.AST | t.CPtr, fatal_if_unknown: int) -> llvmlite.LLVMType | t.CPtr: """根据函数名推断外部 includes 函数的返回类型 支持三种函数名形式: 1. 裸名: "parse", "strlen" 2. 别名: "json_parse"(from X import parse as json_parse) 3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name) fatal_if_unknown: 白名单全部未命中时的行为。 =1(纯裸名外部函数,无法收敛)→ fatal_error fail-fast(默认 i32 会截断指针/i64,静默生成导致运行时数据损坏,不兜底原则)。 =0(Phase A 中间遍,_deps_pending 下遍重译可收敛)→ 返回默认 i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。 """ if pool is None or func_name is None: return llvmlite.Int32(pool) # 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse") # SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔 bare_name: str = func_name dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.') if dot_pos is not None: # '.' 后的部分是裸函数名 bare_name = dot_pos + 1 i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 返回 i8* 的函数(指针返回值,截断会导致错误) if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0: return i8_ptr_ty if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0: return i8_ptr_ty if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0: return i8_ptr_ty if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0: return i8_ptr_ty if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0: return i8_ptr_ty if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0: return i8_ptr_ty if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0: return i8_ptr_ty if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0: return i8_ptr_ty if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0: return i8_ptr_ty # JSON 解析函数返回 JsonValue*(指针) # from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖 if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0: return i8_ptr_ty # MemBuddy 方法(当类型信息丢失时可能走外部函数路径) # 存疑 # malloc:裸名 CExport 跨模块调用(uhci/desktop 等 mm.malloc → @malloc) # 在模块归属解析失败(mod_ret_sha1=None)或 vpi/text.ll 未命中时回退到 # 此白名单。缺失导致返回类型被推断为 i32 → call i32 @malloc 截断 64 位 # 指针 → mm.malloc 实际返回非空但调用方判 None(uhci frame list / # desktop bg_row_buf 根因)。calloc/realloc/alloc 均已登记,malloc 遗漏。 if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "alloc") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "calloc") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "realloc") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "alloc_buf") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃) if string.strcmp(func_name, "get_logger") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "fmt_buf") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏) if string.strcmp(func_name, "get_includes_binary_dir") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # HandlesType 模块函数(返回 ast.AST|t.CPtr 联合类型,跨模块推断为 i32 导致指针截断) if string.strcmp(bare_name, "_parse_type_string") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 返回 i64 的函数 if string.strcmp(func_name, "strlen") == 0: return llvmlite.Int64(pool) if string.strcmp(func_name, "atoll") == 0: return llvmlite.Int64(pool) # 返回 double 的函数 if string.strcmp(func_name, "atof") == 0: return llvmlite.Double(pool) # 返回 i8 的函数 if string.strcmp(func_name, "samestr") == 0: return llvmlite.Int8(pool) # Win32 API 返回指针(VOIDPTR/HANDLE)的函数 # stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃 i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) if string.strcmp(func_name, "VirtualAlloc") == 0: return i8_ptr_ty if string.strcmp(func_name, "HeapAlloc") == 0: return i8_ptr_ty if string.strcmp(func_name, "HeapReAlloc") == 0: return i8_ptr_ty if string.strcmp(func_name, "HeapCreate") == 0: return i8_ptr_ty if string.strcmp(func_name, "GetProcessHeap") == 0: return i8_ptr_ty if string.strcmp(func_name, "GlobalAlloc") == 0: return i8_ptr_ty if string.strcmp(func_name, "GlobalFree") == 0: return i8_ptr_ty if string.strcmp(func_name, "GlobalLock") == 0: return i8_ptr_ty if string.strcmp(func_name, "LocalAlloc") == 0: return i8_ptr_ty if string.strcmp(func_name, "LocalFree") == 0: return i8_ptr_ty if string.strcmp(func_name, "GetProcAddress") == 0: return i8_ptr_ty if string.strcmp(func_name, "GetModuleHandleA") == 0: return i8_ptr_ty if string.strcmp(func_name, "GetModuleHandleW") == 0: return i8_ptr_ty # GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32 → # inttoptr 后 strlen 读到无效地址(SysLibTest argv 段 ASAN 崩溃, # 非 ASAN 环境随机表现:IDE 0xC0000005 / 部分环境侥幸可读)。 if string.strcmp(func_name, "GetCommandLineA") == 0: return i8_ptr_ty if string.strcmp(func_name, "GetCommandLineW") == 0: return i8_ptr_ty # gethostbyname 返回 hostent*(i8*) if string.strcmp(func_name, "gethostbyname") == 0: return i8_ptr_ty if string.strcmp(func_name, "LoadLibraryA") == 0: return i8_ptr_ty if string.strcmp(func_name, "LoadLibraryW") == 0: return i8_ptr_ty # Win32 Handle 返回函数(HANDLE = void* = i8*) if string.strcmp(func_name, "GetStdHandle") == 0: return i8_ptr_ty if string.strcmp(func_name, "CreateFileA") == 0: return i8_ptr_ty if string.strcmp(func_name, "CreateFileW") == 0: return i8_ptr_ty if string.strcmp(func_name, "FindFirstFileA") == 0: return i8_ptr_ty if string.strcmp(func_name, "FindFirstFileW") == 0: return i8_ptr_ty # Win32 API 返回 i64 (SIZE_T) 的函数 if string.strcmp(func_name, "VirtualQuery") == 0: return llvmlite.Int64(pool) if string.strcmp(func_name, "HeapSize") == 0: return llvmlite.Int64(pool) if string.strcmp(func_name, "HeapCompact") == 0: return llvmlite.Int64(pool) if string.strcmp(func_name, "GlobalSize") == 0: return llvmlite.Int64(pool) # llvmlite build_* 函数返回类型推断 # stubs 翻译时未注入,需推断返回类型避免指针被截断为 i32 # 大多数 build_* 函数返回 Value*(指针),少数终止指令返回 void if string.strncmp(func_name, "build_", 6) == 0: # 返回 void 的 build_* 函数(终止指令和无返回值指令) if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or \ string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or \ string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or \ string.strcmp(func_name, "build_switch") == 0: return llvmlite.Void(pool) # 其他 build_* 函数返回 Value*(指针) return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # llvmlite 类型工厂函数返回 LLVMType*(指针) # Int1/Int8/Int16/Int32/Int64/Ptr/Func/Array/Struct/Void/Half/Float/Double/FP128/Label # new_param_node/new_phi_incoming 返回 ParamNode*/PhiIncoming*(指针) # const_int32/const_int64/ConstFloat/ConstZero/ConstNull/ConstInt 返回 Value*(指针) # function_get_name/param_get_ty 等访问器返回指针 if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or \ string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or \ string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or \ string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or \ string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or \ string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or \ string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or \ string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or \ string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or \ string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or \ string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or \ string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or \ string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 默认 i32:无登记、无模块归属、白名单全部未命中的外部函数。 # fatal_if_unknown=1(纯裸名外部调用,无法收敛)→ fail-fast 报错: # 默认 i32 会截断指针/i64,静默生成导致运行时数据损坏(不兜底原则)。 # fatal_if_unknown=0(Phase A 中间遍,_deps_pending 下遍重译收敛)→ # 保持默认 i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。 if fatal_if_unknown != 0: self.Gen.TypeGenErr.fatal_error( node, f"外部函数返回类型未登记且无法收敛推断(默认 i32 会截断指针/i64,需提供 stub 声明或加入白名单): {func_name}", "TypeError") return llvmlite.Int32(pool) def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr, func_name: str, param_idx: int) -> llvmlite.LLVMType | t.CPtr: """获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型 对 Win32 API 函数的所有非指针参数返回 i64,匹配 declare 中 ULONG/SIZE_T 被统一翻译为 i64 的情况。 """ if pool is None or func_name is None: return None i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) # Win32 API 函数列表:所有非指针参数在 declare 中都是 i64 # (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64) win32_apis: list[str] | t.CPtr = [ "VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery", "VirtualLock", "VirtualUnlock", "HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc", "HeapFree", "HeapSize", "HeapValidate", "HeapCompact", "GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize", "LocalAlloc", "LocalFree", "GetLastError", "SetLastError", "Sleep", "SleepEx", "GetTickCount", "GetProcessHeap" ] # list 迭代不支持,用 range + get 替代 api_count: sizet = len(win32_apis) for ai in range(api_count): api_name: str = win32_apis.get(ai) if string.strcmp(api_name, func_name) == 0: return i64_ty # 内存分配函数:size 参数在声明中为 i64(size_t),裸名回退时 # 不提升则 call(i32 size) 与 define(i64 size) 不匹配(desktop start # 第 4 次 mm.malloc(w*4) 返回值被破坏收到 0x0 根因;HelloWorld # vpsdk 的 malloc 同理)。calloc/realloc 的 size 参数在 idx 1。 if string.strcmp(func_name, "malloc") == 0 \ or string.strcmp(func_name, "alloc") == 0 \ or string.strcmp(func_name, "alloc_buf") == 0: if param_idx == 0: return i64_ty if string.strcmp(func_name, "calloc") == 0: if param_idx == 0 or param_idx == 1: return i64_ty if string.strcmp(func_name, "realloc") == 0: if param_idx == 1: return i64_ty return None def _is_sha1_mangled_call(self, call_name: str) -> int: """判断 call_name 是否为跨模块 Viper 函数("{16位hex}.func" 形式) C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0, 不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。 """ if call_name is None: return 0 first_dot: str = string.strchr(call_name, '.') if first_dot is None: return 0 pre_len: sizet = sizet(uint64(first_dot) - uint64(call_name)) if pre_len != 16: return 0 for hx in range(16): ch: int = call_name[hx] if ch < 48 or ch > 57: if ch < 97 or ch > 102: if ch < 65 or ch > 70: return 0 return 1 def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, dst_val: llvmlite.Value | t.CPtr, src_val: llvmlite.Value | t.CPtr, num_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr: """将 memcpy 调用映射到 @llvm.memcpy 内联函数 直接接收三个 Value 参数,避免通过 Next 链表遍历(共享 alloca 对象的 Next 字段可能被其他操作污染,导致链表断裂)。 """ if pool is None or builder is None or mod is None: return None if dst_val is None or src_val is None or num_val is None: return None # 在函数顶部声明 void_ty,避免后续遮蔽 void_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) # 声明 @llvm.memcpy(如未声明) memcpy_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "llvm.memcpy") if memcpy_func is None: memcpy_func = llvmlite.create_declare(pool, mod, "llvm.memcpy", void_ty) if memcpy_func is not None: i8_ptr_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i64_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) i1_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "dst") llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "src") llvmlite.add_param(pool, memcpy_func, i64_ty_d, "len") llvmlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile") # 类型转换: dst, src → i8*; num → i64 # 注意: 使用 value_get_ty 访问器获取 Ty,避免本地 Value 类型字段索引偏移 i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) # dst → i8* dst_coerced: llvmlite.Value | t.CPtr = dst_val dst_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(dst_val) if self.Gen.ExprGenBase.is_ptr_type(dst_ty) != 0: dst_coerced = llvmlite.build_bitcast(builder, dst_val, i8_ptr_ty) else: dst_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dst_ty) if dst_bits != 0: if dst_bits < 64: dst_coerced = llvmlite.build_zext(builder, dst_val, i64_ty) dst_coerced = llvmlite.build_inttoptr(builder, dst_coerced, i8_ptr_ty) # src → i8* src_coerced: llvmlite.Value | t.CPtr = src_val src_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(src_val) if self.Gen.ExprGenBase.is_ptr_type(src_ty) != 0: src_coerced = llvmlite.build_bitcast(builder, src_val, i8_ptr_ty) else: src_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(src_ty) if src_bits != 0: if src_bits < 64: src_coerced = llvmlite.build_zext(builder, src_val, i64_ty) src_coerced = llvmlite.build_inttoptr(builder, src_coerced, i8_ptr_ty) # num → i64 num_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(builder, num_val, i64_ty) if dst_coerced is None or src_coerced is None or num_coerced is None: return None # 添加 i1 false 参数(isvolatile)— llvm.memcpy 需要 4 个参数 false_val: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, llvmlite.Int1(pool), 0, "false") # 使用 includes 中的访问器函数构建参数链表,避免本地 Value 类型字段索引偏移 # (f26cd4c78b4d554c.Value 缺少 GSListNode 的 Next 字段,直接访问 .Next 会修改 .Ty) llvmlite.value_set_next(dst_coerced, src_coerced) llvmlite.value_set_next(src_coerced, num_coerced) if false_val is not None: llvmlite.value_set_next(num_coerced, false_val) llvmlite.value_set_next(false_val, None) else: llvmlite.value_set_next(num_coerced, None) # 使用 build_call 发射 call 指令(内部用 includes Value 类型访问字段,索引正确) # _ll_name_needs_quote 已对 llvm. 前缀豁免引号 result: llvmlite.Value | t.CPtr = llvmlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0) # 返回 dst(匹配 C memcpy 语义: 返回 dest 指针) return dst_coerced # ============================================================ # _translate_call_with_kwargs — 带关键字参数的函数调用 # # 支持两种语法: # add(b=4, a=3) — 全关键字乱序 # greet("Hello", name="World") — 位置 + 关键字混合 # # 策略: 遍历 found_func.Params 链表,对每个参数: # 1. 若位置参数仍有剩余(param_idx < can),取位置参数 # 2. 否则按参数名匹配关键字参数 # 3. 按参数顺序构建调用参数链表 # ============================================================ # ============================================================ def _translate_call_with_kwargs(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, func_name: str, found_func: llvmlite.Function | t.CPtr, cargs: list[ast.AST | t.CPtr] | t.CPtr, can: sizet, keywords: list[ast.AST | t.CPtr] | t.CPtr, kw_count: sizet, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: """带关键字参数的函数调用,按参数顺序构建调用链表 使用 llvmlite 访问器函数绕过 stub 类型限制: found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub 类型中字段不足,直接访问会被 TransPyC 静默跳过。 """ head: llvmlite.Value | t.CPtr = None tail: llvmlite.Value | t.CPtr = None actual_count: sizet = 0 # memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染) mc_dst1: llvmlite.Value | t.CPtr = None mc_src1: llvmlite.Value | t.CPtr = None mc_num1: llvmlite.Value | t.CPtr = None # 遍历函数参数链表(使用访问器获取 Head) param_idx: sizet = 0 cur_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) while cur_param is not None: arg_val: llvmlite.Value | t.CPtr = None param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param) if param_idx < can: # 优先使用位置参数 arg: ast.AST | t.CPtr = cargs.get(param_idx) if arg is not None: # 传形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant) arg_val = self.Gen.ExprGen.translate_value( builder, pool, mod, arg, None, 0, trans, param_ty) else: # 查找匹配的关键字参数 param_name: str = llvmlite.param_get_name(cur_param) if param_name is not None: for ki in range(kw_count): kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(keywords.get(ki)) if kw is None or kw.arg is None or kw.value is None: continue if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1: arg_val = self.Gen.ExprGen.translate_value( builder, pool, mod, kw.value, None, 0, trans, param_ty) break if arg_val is not None: # 类型转换(按参数类型,使用访问器) if param_ty is not None: arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty) arg_val.Next = None if head is None: head = arg_val else: tail.Next = arg_val tail = arg_val # memcpy 专用: 保存前三个参数的独立引用 if actual_count == 0: mc_dst1 = arg_val elif actual_count == 1: mc_src1 = arg_val elif actual_count == 2: mc_num1 = arg_val actual_count += 1 param_idx += 1 cur_param = llvmlite.param_get_next(cur_param) # 使用模块中的实际函数名(可能带 SHA1 前缀,使用访问器) # declare 和 define 统一用 found_func.Name: # - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name) # - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名 # 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀, # 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference call_name: str = func_name found_name: str = llvmlite.function_get_name(found_func) if found_name is not None: call_name = found_name # 返回类型(使用访问器) call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) if call_ret_ty is None: # found_func 已有 declare 却读不到返回类型(访问器异常,罕见防御): # 白名单未命中也不 fail-fast,保持默认 i32(有声明即有类型来源)。 call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0) # memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失) if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3: return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1) return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)