特化名({sha}.list[str].__init__)在 vpi 参数注解缓存中无精确键——键段取自 class 行文本,只存模板 list[T]——定义侧形参注解取不到,调用点 declare 停在调用点实参口径(elem_size 字面量 i32 vs 定义侧 t.CSizeT=i64;池实参 MemBuddy*/i8* vs 定义侧 MemManager*),并经 .stub.ll 传播到未调用该函数的消费方模块
新增 VpiReader._resolve_vpi_param_annot_seg_template:按 {sha1}.{base}[T].{method} 定位模板条目、用特化实参替换段内类型形参 token 后再解析,未命中返回 None 不兜底;_coerce_args_to_def_params 在精确键未命中且类名含 [ 时改走模板退查并置 tpl_src,使 _coerce_arg_value_to_ty 对同为具体结构体指针但基类型不同补 bitcast;_call_method_on_ptr 的跨模块分支补建 declare 前的 coerce(先例 a83d183 的三处调用点)
同时把 {sha}.{cls}.{method} 的切分由首个 . 改为末个 .(CallGenLib/CallGenBase 两处):method 不含 .,首个 . 会把含模块限定名的类型实参截断(list[3571eabe5a83d798.AST|t.CPtr] 截成无 ] 的类名 ⇒ 模板退查必 miss)
另修 VpiReader 一处 double-free:_subst_template_arg 早退分支返回入参本身,替换前释放原缓冲需先判同一性(三条路径各释放一次)
效果: 危险级签名不一致 TransPyV 8->4->0、Test/AstTest 6->2->0,ZlibTest/WebTest/FakeDuckTest/ListTest/GenericTest2/VectorGenericTest 保持 0;4 个目标符号 define/declare 共 148 处全部统一为 MemManager*+i64(0 处旧口径);自举达全树逐字节不动点(exe sha256 相同、temp/** 635 文件零差异);AstTest 及既有测试全 PASS 且无 ASan 报告
930 lines
51 KiB
Plaintext
930 lines
51 KiB
Plaintext
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 库声明 / 返回类型推断 /
|
||
# 变长参数 / 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: rowptr[memhub.MemBuddy],
|
||
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: rowptr[memhub.MemBuddy],
|
||
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: rowptr[memhub.MemBuddy],
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
call_name: str,
|
||
head: llvmlite.Value | t.CPtr,
|
||
count: int,
|
||
cargs: list[ast.AST | t.CPtr] | t.CPtr = None,
|
||
self_off: int = 0,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""把跨模块调用实参 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'
|
||
# 注意:不能用 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)
|
||
# 泛型特化类({sha}.list[str].__new__):定义侧注解写在**模板类**上
|
||
# (vpi 键段为 "list[T]",见 VpiReader._cache_vpi_param_annots_text 的
|
||
# 类名取值),特化名的精确键必然遗漏 ⇒ 逐段退查模板注解并把类型形参
|
||
# 替换为特化实参(VpiReader._resolve_vpi_param_annot_seg_template,
|
||
# 与 _lookup_vpi_ret_annot 的泛型特化归一化匹配同源)。tpl_src=1 使
|
||
# 指针口径也对齐定义侧(MemBuddy* → MemManager*)。
|
||
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 | 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 = 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 | t.CPtr = 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 转换到目标形参类型
|
||
#
|
||
# 整型:
|
||
# - 同宽 → 原样;
|
||
# - 窄化(目标更窄,本簇主场景 i32→i8/i16)→ 常量按目标宽度重建常量,
|
||
# SSA 用 trunc(trunc 与符号无关,窄化安全);
|
||
# - 扩宽(定义侧形参更宽)→ 常量按目标宽度重建常量;SSA 按**源**符号性
|
||
# 扩宽(zext/sext,真源 ExprGenBase._zext_or_sext_val;同规则先例
|
||
# ExprGenBase.coerce_to_type / CallGenCtor._translate_t_type_cast /
|
||
# CallGenLib._promote_clib_call_args)→ declare 由扩宽后的实参链建立,
|
||
# declare/call/define 三者同宽(此前 SSA 原样返回 → 与定义侧宽度不一致)。
|
||
# 指针:实参与目标皆指针且"泛型 i8* 性"不同时 bitcast(如 i32*→i8*),
|
||
# 使 declare 指针类型与 define 一致;同为 i8* 或同为具体 struct 指针
|
||
# 则原样保留(避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声)。
|
||
# 其它(结构体值/void 等)→ 原样保留。不可转换亦原样返回(不兜底改型)。
|
||
# ============================================================
|
||
def _coerce_arg_value_to_ty(self, pool: rowptr[memhub.MemBuddy],
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
val: llvmlite.Value | t.CPtr,
|
||
tgt_ty: llvmlite.LLVMType | t.CPtr,
|
||
node: ast.AST | t.CPtr = None,
|
||
trans: HT.Translator | t.CPtr = None,
|
||
tpl_src: int = 0) -> llvmlite.Value | t.CPtr:
|
||
"""把单个实参 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 | 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 判符号性。
|
||
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
|
||
# 非常量(SSA)扩宽:此前原样返回,declare 与 call 保持
|
||
# 调用点窄宽度,而定义侧(vpi 形参注解)更宽 → 同一
|
||
# 符号 declare/call 与 define 宽度不一致(includes/
|
||
# requests/__init__.vp `string.strncpy(..., host_len)`
|
||
# 3 参 i32 而 string.vp 定义 `n: t.CSizeT`=i64;同模块/
|
||
# 同依赖的 strncpy 常量实参分支已扩宽成 i64,SSA 实参
|
||
# 分支未扩宽 → 同一模块两条 declare 宽度不同)。
|
||
# 扩宽方向由**源**符号性决定(与 coerce_to_type /
|
||
# CallGenCtor 同一真源 ExprGenBase._zext_or_sext_val:
|
||
# i1/i8、无符号源 → zext;否则 sext),与目标类型无关。
|
||
# 扩宽后 declare 由本函数返回的新实参链建立(param_head
|
||
# → _ensure_method_declare),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 | t.CPtr = llvmlite.build_zext(
|
||
builder, val, tgt_ty)
|
||
if nv_zw is not None:
|
||
return nv_zw
|
||
else:
|
||
nv_sw: llvmlite.Value | t.CPtr = llvmlite.build_sext(
|
||
builder, val, tgt_ty)
|
||
if nv_sw is not None:
|
||
return nv_sw
|
||
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
|
||
elif tpl_src != 0:
|
||
# 模板注解来源:两侧同为具体结构体指针但基类型不同
|
||
# (实参 MemBuddy* vs 定义侧形参 MemManager*,注解
|
||
# `memhub.MemManager | t.CPtr`)→ bitcast 使 declare
|
||
# 指针口径与 define 一致(位不变、ABI 无影响)。
|
||
# 非模板来源保持既有"具体指针原样保留"策略,
|
||
# 避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声。
|
||
nv_p2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, val, tgt_ty)
|
||
if nv_p2 is not None:
|
||
return nv_p2
|
||
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: rowptr[memhub.MemBuddy],
|
||
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)
|
||
# llvmlite 函数名长度访问器返回 t.CSizeT(i64):stub/vpi 尚未注入时若
|
||
# 默认 i32 会与 declare 的 i64 签名不一致(call i32 vs declare i64),
|
||
# 与 function_get_name 指针白名单同理须登记返回类型。
|
||
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 | 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: rowptr[memhub.MemBuddy],
|
||
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 白名单)。
|
||
判定走唯一真源 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: rowptr[memhub.MemBuddy],
|
||
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: rowptr[memhub.MemBuddy],
|
||
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) |