问题一(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*、或模板注解来源两种情形)。
783 lines
42 KiB
Plaintext
783 lines
42 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 库声明 / 返回类型推断 /
|
||
# ============================================================
|
||
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) |