import t, c from stdint import * import ast import llvmlite import memhub import string import stdlib import lib.core.Gen.Translator as HT import lib.Stub.StubGen.VpiReader as VpiReader import lib.core.Gen.GenApi as GenApi from . import CallGenBase # ============================================================ # CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 / # ============================================================ novirtual class CallGenLib(CallGenBase.CallGenBase): """C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)""" def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, name: str) -> llvmlite.Function*: """为已知的 C 库函数创建 declare 声明""" if pool is None or mod is None or name is None: return None i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool) i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool) i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty) ret_ty: llvmlite.LLVMType* = 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* = 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*, builder: llvmlite.IRBuilder*, mod: llvmlite.LLVMModule*, call_name: str, head: llvmlite.Value*, count: int) -> llvmlite.Value*: """裸名 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 长度) 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* = 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* = llvmlite.Int64(pool) promoted: llvmlite.Value* = 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* = None new_tail: llvmlite.Value* = None cur2: llvmlite.Value* = head i2: int = 0 while cur2 is not None: node_v: llvmlite.Value* = 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:按 vpi 定义侧形参注解把跨模块调用实参 coerce 到目标类型并重建实参链,使 call/declare/define 宽度一致;支持 {sha1}.{func} 与 {sha1}.{cls}.{method} 两形态(段下标与实参下标一一对应),裸名 C 库走 _promote_clib_call_args/_ensure_c_lib_declare,未命中原样保留 def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*, call_name: str, head: llvmlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> llvmlite.Value*: """把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链 cargs/self_off/trans:实参 AST 列表与其在实参链中的偏移(模块级函数 0、 方法 1——链首元素是 self,而 vpi 段 0 即 self 占位段)。仅用于非常量 (SSA)整型扩宽时的**源符号性**判定(与 ExprGenBase._zext_or_sext_val 同源);缺省时该判定退化为有符号(sext),与 coerce_to_type 默认一致。 """ if pool is None or builder is None or call_name is None or head is None: return head 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 # 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断 dot_r: str = string.strrchr(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' # 直接 ensure 加载 + 直查缓存(不用 _vpi_method_param_annot:其 cls_name is None 时恒返回 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) # 泛型特化类({sha}.list[str].__new__):定义侧注解写在模板类上,精确键必遗漏 ⇒ tpl_src=1 时退查模板注解并替换类型形参(VpiReader._resolve_vpi_param_annot_seg_template) tpl_src: int = 0 if prm_annots is None: if cls_nm is not None and string.strchr(cls_nm, 91) is not None: tpl_src = 1 else: stdlib.free(sha1_buf) if cls_nm is not None: stdlib.free(cls_nm) return head i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) new_head: llvmlite.Value* = None new_tail: llvmlite.Value* = None cur: llvmlite.Value* = head idx: int = 0 while cur is not None and idx < count: node_v: llvmlite.Value* = cur tgt_ty: llvmlite.LLVMType* = None if tpl_src != 0: tgt_ty = VpiReader._resolve_vpi_param_annot_seg_template(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty) else: tgt_ty = 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: # 实参 AST(源符号性判定用):链下标 idx 减 self 偏移即 AST 下标 arg_idx: int = idx - self_off arg_node: ast.AST* = None if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs): arg_node = cargs.get(arg_idx) node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty, arg_node, trans, tpl_src) 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 转换到目标形参类型——整型同宽原样、窄化 trunc/按宽度重建常量、扩宽按源符号性 zext/sext;同层指针类型不一致则 bitcast 到目标类型;其余原样返回(不可转换亦不改型) def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*, val: llvmlite.Value*, tgt_ty: llvmlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None, tpl_src: int = 0) -> llvmlite.Value*: """把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回 node/trans:实参 AST 与其翻译器(仅 SSA 整型扩宽的源符号性判定用, 委托 ExprGenBase._zext_or_sext_val;缺省退化为有符号 sext)。 tpl_src:目标类型来自**泛型模板类**注解(特化名精确键遗漏 → 退查模板 + 类型形参替换,见 VpiReader._resolve_vpi_param_annot_seg_template)⇒ 指针口径亦可对齐定义侧(同为具体结构体指针但基类型不同时 bitcast)。 """ 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* = llvmlite.const_int(pool, bits_b, val.IntVal) if nv_c is not None: return nv_c else: nv_t: llvmlite.Value* = llvmlite.build_trunc(builder, val, tgt_ty) if nv_t is not None: return nv_t # 扩宽(实参窄于形参):编译期常量按十进制值重建,各自保持原类型数值,无需 AST 判符号性 if val.IsConst != 0: nv_w: llvmlite.Value* = llvmlite.const_int(pool, bits_b, val.IntVal) if nv_w is not None: return nv_w return val # 非常量(SSA)扩宽:扩宽方向由源符号性决定(i1/i8、无符号源 → zext,否则 sext),使 declare/call/define 三者同宽 if bits_b > bits_a: if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, node, trans) != 0: nv_zw: llvmlite.Value* = llvmlite.build_zext(builder, val, tgt_ty) if nv_zw is not None: return nv_zw else: nv_sw: llvmlite.Value* = llvmlite.build_sext(builder, val, tgt_ty) if nv_sw is not None: return nv_sw return val case _: pass case llvmlite.LLVMType.Ptr(_): match tgt_ty: case llvmlite.LLVMType.Ptr(_): # 同层指针:实参类型与定义侧形参类型不一致就 bitcast 对齐(一致则不比, # 避免生成非法 `bitcast T* to T*`)。跨模块调用的 call/declare 都以 # 定义侧为准(declare 见 CallGenBase._ensure_method_declare),故这里 # 必须对齐:首个调用点由它建立 declare、不走 coerce_to_type,原样传具体 # 指针就会与 declare 的类型不一致(llc 报 argument type mismatch)。 # 原判据只覆盖"一侧为 i8*"与"模板注解来源",漏了两侧同为具体结构体指针 # 但基类型不同的情形(实参 MemBuddy* vs 定义侧形参 MemManager*)。 if self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, tgt_ty) == 0: nv_p: llvmlite.Value* = llvmlite.build_bitcast(builder, val, tgt_ty) if nv_p is not None: return nv_p case _: pass case _: # 聚合值 → 指针:rvalue 结构体/数组落栈取址(FuncGenDef 形参一律 lowering 为 Ptr,跨模块时上游 coerce_to_type 不参与) 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* = 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*, func_name: str, node: ast.AST*, fatal_if_unknown: int) -> llvmlite.LLVMType*: """根据函数名推断外部 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* = 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) # llvmlite 函数名长度访问器返回 sizet(i64):默认 i32 与 declare 的 i64 签名不一致,须登记 if string.strcmp(bare_name, "function_get_name_len") == 0 or \ string.strcmp(func_name, "function_get_name_len") == 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* = 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,解引用读到无效地址 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_* 返回类型推断:多数返回 Value*,少数终止指令返回 void(stubs 未注入时防指针截断) 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*/Value*):Int*/Ptr/Func/Array/Struct/Void 等、const_*/Const*、function_get_*/param_get_* 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 报错;=0(Phase A 中间遍)保持占位,不输出 WARN 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*, func_name: str, param_idx: int) -> llvmlite.LLVMType*: """获取外部函数第 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* = llvmlite.Int64(pool) # Win32 API 函数列表:所有非指针参数在 declare 中都是 i64 # (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64) win32_apis: list[str]* = [ "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 与 define 宽度不匹配;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 白名单)。 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex)。 """ if call_name is None: return 0 if GenApi.sha1_prefix_len(call_name) != 0: return 1 return 0 def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*, mod: llvmlite.LLVMModule*, dst_val: llvmlite.Value*, src_val: llvmlite.Value*, num_val: llvmlite.Value*) -> llvmlite.Value*: """将 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* = llvmlite.Void(pool) # 声明 @llvm.memcpy(如未声明) memcpy_func: llvmlite.Function* = 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* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i64_ty_d: llvmlite.LLVMType* = llvmlite.Int64(pool) i1_ty_d: llvmlite.LLVMType* = 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* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool) # dst → i8* dst_coerced: llvmlite.Value* = dst_val dst_ty: llvmlite.LLVMType* = 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* = src_val src_ty: llvmlite.LLVMType* = 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* = 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* = 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* = llvmlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0) # 返回 dst(匹配 C memcpy 语义: 返回 dest 指针) return dst_coerced # _translate_call_with_kwargs:带关键字参数的函数调用,按 found_func.Params 顺序构建实参链表(位置参数优先,否则按参数名匹配关键字) # ============================================================ def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*, mod: llvmlite.LLVMModule*, func_name: str, found_func: llvmlite.Function*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*) -> llvmlite.Value*: """带关键字参数的函数调用,按参数顺序构建调用链表 使用 llvmlite 访问器函数绕过 stub 类型限制: found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub 类型中字段不足,直接访问会被 TransPyC 静默跳过。 """ head: llvmlite.Value* = None tail: llvmlite.Value* = None actual_count: sizet = 0 # memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染) mc_dst1: llvmlite.Value* = None mc_src1: llvmlite.Value* = None mc_num1: llvmlite.Value* = None # 遍历函数参数链表(使用访问器获取 Head) param_idx: sizet = 0 cur_param: llvmlite.Param* = llvmlite.function_get_param_head(found_func) while cur_param is not None: arg_val: llvmlite.Value* = None param_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(cur_param) if param_idx < can: # 优先使用位置参数 arg: ast.AST* = 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: rawptr[ast.Keyword] = rawptr[ast.Keyword](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) # declare 与 define 统一用 found_func.Name:普通函数带 SHA1 前缀,t.CExtern/t.State/t.CExport 用裸名(用裸名会造成 call 与 define 符号名不一致) 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* = 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)