Files
ViperC/App/lib/core/Gen/Call/CallGenLib.vp
T
TermiNexus 8e3811c490 fix(CallGen): 跨模块调用的 declare 形参与实参统一以定义侧为准
问题一(declare 形参被首个调用点冻结):CallGenBase._ensure_method_declare 新建跨模块方法
declare 时形参类型取"首个调用点的实参类型",而这条 declare 被同模块所有调用点共用 ⇒ 后续
调用传入另一种具体指针类型时,被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"。
现场:VarGenMixin.repl_ensure_entry 的 node: ast.AST*,先被 translate_annassign 以 ast.AnnAssign*
建 declare,后 translate_assign 传 ast.AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*"。
⇒ 指针形参与整数同口径,一律采用定义侧(.vpi 注解段)类型;解析不出指针时保留实参类型。

问题二(实参未对齐 declare):declare 改用定义侧类型后实参也必须对齐,否则首个调用点
(由它建立 declare、不走 coerce_to_type)原样传具体指针会与 declare 类型不一致(llc 报
argument type mismatch)。⇒ CallGenLib._coerce_arg_value_to_ty 的同层指针分支改为"类型内容
不一致即 bitcast 到定义侧类型"(原判据只覆盖一侧为 i8*、或模板注解来源两种情形)。
2026-10-01 20:29:15 +08:00

783 lines
42 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)