326 lines
17 KiB
Plaintext
326 lines
17 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import stdio
|
||
import stdlib
|
||
import sys
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Handles.HandlesBase as HandlesBase
|
||
import lib.core.Handles.HandlesTranslator as HT
|
||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||
import lib.Stub.StubMerger as StubMerger
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
import lib.core.CompilerFactory as CompilerFactory
|
||
|
||
import lib.core.Gen.CallGenBase as CallGenBase
|
||
|
||
|
||
# ============================================================
|
||
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
|
||
#
|
||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||
# @t.NoVTable:静态绑定。
|
||
# ============================================================
|
||
@t.NoVTable
|
||
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 | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
arg_node: ast.AST | t.CPtr,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""获取 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 | t.CPtr = (ast.Name | t.CPtr)(arg_node)
|
||
if nm.id is not None:
|
||
alloca: llvmlite.Value | t.CPtr = 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 | t.CPtr,
|
||
type_node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||
"""解析 va_arg/arg 的类型参数为 LLVMType
|
||
|
||
支持的类型表达式:
|
||
t.CInt / t.CInt32T → i32
|
||
t.CInt64T → 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 | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||
pool, type_node, trans._imported_modules, trans._from_imports, trans)
|
||
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 | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
cl: ast.Call | t.CPtr,
|
||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||
can: t.CSizeT,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""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 | t.CPtr = cargs.get(0)
|
||
ap_val: llvmlite.Value | t.CPtr = 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 | t.CPtr = ap_val
|
||
|
||
# 第二个参数:目标类型(可选,默认 i32)
|
||
arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
if can >= 2:
|
||
type_node: ast.AST | t.CPtr = cargs.get(1)
|
||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||
|
||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73)
|
||
read_ty: llvmlite.LLVMType | t.CPtr = 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_arg 指令
|
||
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
||
def _translate_arg_sugar(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
cl: ast.Call | t.CPtr,
|
||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||
can: t.CSizeT,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""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 | t.CPtr = llvmlite.Int32(pool)
|
||
if can >= 1 and cargs is not None:
|
||
type_node: ast.AST | t.CPtr = cargs.get(0)
|
||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||
|
||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致)
|
||
read_ty: llvmlite.LLVMType | t.CPtr = 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)
|
||
|
||
# 生成 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 | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||
can: t.CSizeT,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""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 | t.CPtr = cargs.get(0)
|
||
ap_ptr: llvmlite.Value | t.CPtr = 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 | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_vs)
|
||
ap_i8: llvmlite.Value | t.CPtr = 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 | t.CPtr = 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 | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||
can: t.CSizeT,
|
||
funcs_ptr: t.CPtr,
|
||
func_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""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 | t.CPtr = cargs.get(0)
|
||
ap_ptr: llvmlite.Value | t.CPtr = 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 | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_ve)
|
||
ap_i8: llvmlite.Value | t.CPtr = 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 | t.CPtr = 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 | t.CPtr) -> int:
|
||
"""获取函数的固定参数数量(遍历 Params 链表)"""
|
||
if func is None:
|
||
return 0
|
||
count: int = 0
|
||
param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
|
||
while param is not None:
|
||
count += 1
|
||
param = llvmlite.param_get_next(param)
|
||
return count
|
||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
call_name: str,
|
||
head: llvmlite.Value | t.CPtr,
|
||
can: t.CSizeT,
|
||
arg_nodes: list[ast.AST | t.CPtr] | t.CPtr = None,
|
||
self_offset: int = 0,
|
||
trans: HT.Translator | t.CPtr = 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 | t.CPtr = self.find_func_in_module(mod, call_name)
|
||
if func is None:
|
||
return 0
|
||
if func.IsVarArg != 1:
|
||
return 0
|
||
|
||
# 获取固定参数数量
|
||
fixed_count: int = self._get_func_fixed_param_count(func)
|
||
|
||
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
|
||
if can <= fixed_count:
|
||
return 1
|
||
|
||
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
|
||
cur: llvmlite.Value | t.CPtr = head
|
||
prev: llvmlite.Value | t.CPtr = 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 | t.CPtr = llvmlite.Int32(pool)
|
||
double_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||
v_idx: int = fixed_count
|
||
while cur is not None:
|
||
next_node: llvmlite.Value | t.CPtr = cur.Next
|
||
promoted: llvmlite.Value | t.CPtr = 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:
|
||
# 无符号整型(t.CUInt8T/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 | t.CPtr = 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
|
||
prev = promoted
|
||
cur = next_node
|
||
v_idx += 1
|
||
|
||
return 1 |