回滚 13080f6 的 110 处访问器调用,恢复裸字段访问 `x.Next`。
理由:该批访问器是把访问移到定义模块内执行,**绕开**了"继承字段名解析",
属于规避而非修复;缺陷本体在 TPC 侧(导入侧继承展平把泛型基类的继承字段
记成 __inherited_N 占位名,名字丢失)。TPC 已按语义修正:
· e6451a07 继承展平沿继承链取真实字段名(泛型特化回退模板名),
展平表直接落 `[("Next", <槽位类型>), ...]`,属性访问是普通表命中;
· 3ca46c01 顺带修掉 _get_or_create_struct 里"同名枚举成员遮蔽类定义"的 i32 出口。
因此源码侧恢复为唯一写法(裸字段访问),由编译器负责解析。
注:本 commit 只回滚访问器;13080f6 里夹带的 CallGenProp.vp 续行合并(非访问器改动)
已在工作区保留(未提交)。
389 lines
20 KiB
Plaintext
389 lines
20 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import lib.core.Gen.Translator as HT
|
||
|
||
from . import CallGenBase
|
||
|
||
|
||
# ============================================================
|
||
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
|
||
# ============================================================
|
||
novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
|
||
|
||
# ============================================================
|
||
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
|
||
#
|
||
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
|
||
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
|
||
# ============================================================
|
||
|
||
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
arg_node: ast.AST*,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""获取 va_list 变量的 alloca 指针(不 load)
|
||
|
||
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
|
||
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
|
||
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
|
||
"""
|
||
if arg_node is None: return None
|
||
# Name 节点:直接查符号表获取 alloca 指针(不 load)
|
||
if arg_node.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(arg_node)
|
||
if nm.id is not None:
|
||
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is not None:
|
||
return alloca
|
||
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
|
||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
||
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*,
|
||
type_node: ast.AST*,
|
||
trans: HT.Translator*) -> llvmlite.LLVMType*:
|
||
"""解析 va_arg/arg 的类型参数为 LLVMType
|
||
|
||
支持的类型表达式:
|
||
int / int32 → i32
|
||
int64 → i64
|
||
t.CDouble → double
|
||
t.CPtr / str → i8*
|
||
str / bytes → i8*
|
||
无类型参数时默认 i32(vsnprintf 中 va_arg(ap) 常用于读取 i32)
|
||
"""
|
||
if type_node is None:
|
||
return llvmlite.Int32(pool)
|
||
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
|
||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
||
if resolved is not None:
|
||
return resolved
|
||
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
|
||
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
|
||
return llvmlite.Int32(pool)
|
||
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
cl: ast.Call*,
|
||
cargs: list[ast.AST*]*,
|
||
can: sizet,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
|
||
|
||
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
|
||
"""
|
||
if can == 0 or cargs is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
|
||
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
|
||
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
|
||
ap_node: ast.AST* = cargs.get(0)
|
||
ap_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
|
||
if ap_val is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
ap_ptr: llvmlite.Value* = ap_val
|
||
|
||
# 第二个参数:目标类型(可选,默认 i32)
|
||
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||
if can >= 2:
|
||
type_node: ast.AST* = cargs.get(1)
|
||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||
|
||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73),
|
||
# 再 trunc 回目标宽度——否则返回 8 字节含高位垃圾,%d/%i 语义错误
|
||
# (viperlib vsnprintf %d 非 long 分支 `int64(va_arg(ap, int))`
|
||
# 编译成直接 va_arg i64,高 32 位未定义 → 负数当大正数 A6 根因)。
|
||
read_ty: llvmlite.LLVMType* = arg_ty
|
||
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||
if abits_va != 0 and abits_va < 64:
|
||
read_ty = llvmlite.Int64(pool)
|
||
va_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
||
if va_raw is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
# 8 字节槽 → 目标宽度(低 bits 位);调用方 int64(...) 会再 sext/zext
|
||
return llvmlite.build_trunc(builder, va_raw, arg_ty)
|
||
|
||
# 生成 va_arg 指令
|
||
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
||
def _translate_arg_sugar(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
cl: ast.Call*,
|
||
cargs: list[ast.AST*]*,
|
||
can: sizet,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
|
||
|
||
使用当前函数的隐式 va_list(trans._va_list_ptr),
|
||
简化 *args 函数体内的 va_arg 调用。
|
||
"""
|
||
if trans._va_list_ptr is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# 第一个参数:目标类型(可选,默认 i32)
|
||
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||
if can >= 1 and cargs is not None:
|
||
type_node: ast.AST* = cargs.get(0)
|
||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||
|
||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致),
|
||
# 再 trunc 回目标宽度(%d/%i 高位垃圾 A6 修复,见 _translate_va_arg_sugar)
|
||
read_ty: llvmlite.LLVMType* = arg_ty
|
||
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||
if abits_arg != 0 and abits_arg < 64:
|
||
read_ty = llvmlite.Int64(pool)
|
||
arg_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
||
if arg_raw is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
return llvmlite.build_trunc(builder, arg_raw, arg_ty)
|
||
|
||
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
|
||
return llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
||
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
cargs: list[ast.AST*]*,
|
||
can: sizet,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
|
||
|
||
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
|
||
"""
|
||
if can == 0 or cargs is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||
ap_node: ast.AST* = cargs.get(0)
|
||
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||
if ap_ptr is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
|
||
i8_ty_vs: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||
i8_ptr_ty_vs: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_vs)
|
||
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
||
if ap_i8 is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
void_ty_vs: llvmlite.LLVMType* = llvmlite.Void(pool)
|
||
llvmlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
||
return llvmlite.const_int32(pool, 0)
|
||
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
cargs: list[ast.AST*]*,
|
||
can: sizet,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
|
||
|
||
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
|
||
"""
|
||
if can == 0 or cargs is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||
ap_node: ast.AST* = cargs.get(0)
|
||
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||
if ap_ptr is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
|
||
i8_ty_ve: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||
i8_ptr_ty_ve: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_ve)
|
||
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
||
if ap_i8 is None:
|
||
return llvmlite.const_int32(pool, 0)
|
||
|
||
void_ty_ve: llvmlite.LLVMType* = llvmlite.Void(pool)
|
||
llvmlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
||
return llvmlite.const_int32(pool, 0)
|
||
def _get_func_fixed_param_count(self, func: llvmlite.Function*) -> int:
|
||
"""获取函数的固定参数数量(遍历 Params 链表)"""
|
||
if func is None:
|
||
return 0
|
||
count: int = 0
|
||
param: llvmlite.Param* = llvmlite.function_get_param_head(func)
|
||
while param is not None:
|
||
count += 1
|
||
param = llvmlite.param_get_next(param)
|
||
return count
|
||
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
||
"""已知变参 C 库函数固定参数个数(-1=未知)
|
||
|
||
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
|
||
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
|
||
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
|
||
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
|
||
"""
|
||
if name is None:
|
||
return -1
|
||
if string.strcmp(name, "snprintf") == 0:
|
||
return 3
|
||
if string.strcmp(name, "sprintf") == 0:
|
||
return 2
|
||
if string.strcmp(name, "vsprintf") == 0:
|
||
return 2
|
||
return -1
|
||
|
||
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*,
|
||
name: str, idx: int) -> llvmlite.LLVMType*:
|
||
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
|
||
|
||
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
|
||
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
|
||
"""
|
||
if pool is None or name is None or idx < 0:
|
||
return None
|
||
i8_ptr_kv: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
i64_kv: llvmlite.LLVMType* = llvmlite.Int64(pool)
|
||
if string.strcmp(name, "snprintf") == 0:
|
||
if idx == 0:
|
||
return i8_ptr_kv
|
||
if idx == 1:
|
||
return i64_kv
|
||
if idx == 2:
|
||
return i8_ptr_kv
|
||
return None
|
||
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
|
||
if idx == 0:
|
||
return i8_ptr_kv
|
||
if idx == 1:
|
||
return i8_ptr_kv
|
||
return None
|
||
return None
|
||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
call_name: str,
|
||
head: llvmlite.Value*,
|
||
can: sizet,
|
||
arg_nodes: list[ast.AST*]* = None,
|
||
self_offset: int = 0,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
|
||
|
||
返回: is_variadic (1=变长调用, 0=普通调用)
|
||
副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升)
|
||
|
||
arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断
|
||
self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0)
|
||
trans: 供 _node_type_unsigned 查询变量注解
|
||
"""
|
||
if mod is None or call_name is None or head is None:
|
||
return 0
|
||
# 查找函数对象
|
||
func: llvmlite.Function* = self.find_func_in_module(mod, call_name)
|
||
fixed_count: int = -1
|
||
if func is not None and func.IsVarArg == 1:
|
||
fixed_count = self._get_func_fixed_param_count(func)
|
||
else:
|
||
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):func 为 None(跨模块 stub 未注入,翻译时 found_func 恒 None);func 存在但 IsVarArg=0(该 declare 由 _ensure_method_declare 按实参类型临时创建,无 ... 标记,不代表真实签名)。否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → va_arg(i64) 高 4 字节读残留栈垃圾
|
||
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
||
if fixed_count < 0:
|
||
return 0
|
||
|
||
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
|
||
if can <= fixed_count:
|
||
return 1
|
||
|
||
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
|
||
cur: llvmlite.Value* = head
|
||
prev: llvmlite.Value* = None
|
||
idx: int = 0
|
||
while cur is not None and idx < fixed_count:
|
||
prev = cur
|
||
cur = cur.Next
|
||
idx += 1
|
||
|
||
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
|
||
# 对变长参数进行 C ABI 提升
|
||
i32_ty_promote: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||
double_ty_promote: llvmlite.LLVMType* = llvmlite.Double(pool)
|
||
v_idx: int = fixed_count
|
||
while cur is not None:
|
||
next_node: llvmlite.Value* = cur.Next
|
||
promoted: llvmlite.Value* = cur
|
||
# C ABI integer promotion: i8/i16 → i32
|
||
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
||
if arg_bits != 0 and arg_bits < 32:
|
||
# 无符号整型(uint8/CUInt16T 等)→ zext;有符号/未知 → sext。
|
||
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
|
||
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
|
||
is_u_va: int = 0
|
||
if arg_nodes is not None and trans is not None:
|
||
ast_idx_va: int = v_idx - self_offset
|
||
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
|
||
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
|
||
if an_va is not None:
|
||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0:
|
||
is_u_va = 1
|
||
if is_u_va != 0:
|
||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
||
else:
|
||
# is_u_va 判断失败(arg_nodes/trans 缺失或变量注解查不到)时,
|
||
# i8 按系统编程惯例默认 zext(对齐 should_zext_widen 的
|
||
# "i8 → zext(系统编程中 i8 几乎总是无符号字节)"策略),
|
||
# 否则 printf("%02x", buf[i]) 中 uint8 字节被 sext 成
|
||
# 0xFFFFFFF3,哈希摘要输出 ffffffXX 垃圾。
|
||
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8:
|
||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
||
else:
|
||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
||
if promoted is not None:
|
||
promoted.Next = next_node
|
||
if prev is not None:
|
||
prev.Next = promoted
|
||
# C ABI float promotion: float → double
|
||
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
|
||
if arg_fbits != 0 and arg_fbits < 64:
|
||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
|
||
if promoted is not None:
|
||
promoted.Next = next_node
|
||
if prev is not None:
|
||
prev.Next = promoted
|
||
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
|
||
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
|
||
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
|
||
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
|
||
# L798-811 的 found_func.IsVarArg 提升)。
|
||
i64_ty_promote: llvmlite.LLVMType* = llvmlite.Int64(pool)
|
||
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
||
if pb_va != 0 and pb_va < 64:
|
||
promoted_va2: llvmlite.Value* = None
|
||
is_u_va2: int = 0
|
||
if arg_nodes is not None and trans is not None:
|
||
ast_idx_va2: int = v_idx - self_offset
|
||
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
|
||
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
|
||
if an_va2 is not None:
|
||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
|
||
is_u_va2 = 1
|
||
if is_u_va2 != 0:
|
||
promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote)
|
||
else:
|
||
promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote)
|
||
if promoted_va2 is not None:
|
||
promoted_va2.Next = next_node
|
||
if prev is not None:
|
||
prev.Next = promoted_va2
|
||
promoted = promoted_va2
|
||
prev = promoted
|
||
cur = next_node
|
||
v_idx += 1
|
||
|
||
return 1 |