Files
ViperC/App/lib/core/Gen/CallGenCBuiltin.vp
T
TermiNexus e18609e184 feat(tpv): 支持 coro.Coroutine = c.Addr(task()) 就地解读句柄赋值
- c.Addr(Call) 分支:求值存临时槽(entry alloca)返回槽地址,
  供 coerce 指针→结构体 load 就地解读(Coroutine={__ptr__} 8 字节)
- 协程登记扩展:c.Addr(async_call) 也登记 _coro_var_async
- 新增 _coro_frame_ptr_from_arg 统一提取帧指针:pointee 是 Struct
  (Coroutine 变量,translate_value 返回 alloca 地址)GEP __ptr__ load,
  普通指针 bitcast 直通;5 处拦截函数(run/destroy/exception/msg/gather)替换
2026-08-22 01:55:54 +08:00

1423 lines
73 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
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.core.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# CallGenCBuiltin - CallGenMixin 拆分 2/9:c.* 内建 + asm + llvmir
#
# 按功能域从 CallGenMixin.vp 拆出(500~1500 行/文件架构重构):
# translate_c_deref/derefas/set/addr/load + c.Asm 全套 + c.LLVMIR。
# 继承 CallGenBase。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# _IsTypeExpr / _IsCastWrappingDeref - c.Deref 加载宽度推断的转换包裹判定
# (镜像 TPC HandlesSpecialCall:转换链包裹 Deref 时允许从赋值注解推断
# 加载宽度;ifexp/binop 包裹不放行,避免 i8* 错 bitcast 成其他位宽读)
# ============================================================
def _IsTypeExpr(node: ast.AST | t.CPtr) -> int:
"""节点是否为类型表达式(Name/Attribute/Subscript/BinOp|)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Name or k == ast.ASTKind.Attribute or k == ast.ASTKind.Subscript:
return 1
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.op == ast.OpKind.BitOr:
return 1
return 0
def _IsCastWrappingDeref(rhs: ast.AST | t.CPtr, deref_node: ast.AST | t.CPtr) -> int:
"""rhs 是否为"类型转换链包裹 c.Deref"(如 (T | t.CPtr)(t.CPtr(c.Deref(p))))"""
if rhs is None:
return 0
if t.CUInt64T(rhs) == t.CUInt64T(deref_node):
return 1
if rhs.kind() == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rhs)
if cl.args is not None and len(cl.args) == 1 and _IsTypeExpr(cl.func) != 0:
return _IsCastWrappingDeref(cl.args.get(0), deref_node)
return 0
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# CallGenCBuiltin - c.*/asm/llvmir 内建
# ============================================================
@t.NoVTable
class CallGenCBuiltin(CallGenBase.CallGenBase):
"""c.* 内建 + 内联汇编 + LLVMIR 翻译(继承 Base)"""
def translate_c_deref(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.Deref(ptr) → load 按 ptr 的 pointee 类型"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 1:
return None
ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), None, 0, trans)
if ptr_val is None:
return None
# 按 pointee 类型读取;非指针参数默认按 i8(保持向后兼容)
if self.Gen.ExprGenBase.is_ptr_type(ptr_val.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if pointee is not None:
# i8*(void*)时按赋值目标类型推断读取宽度(对齐 TPC
# HandlesSpecialCall._infer_deref_target_type):
# v: int = c.Deref(p) → bitcast i32* 后 load 4 字节。
# 背景:DictTest d3[key5]=500 读出 -12——固定按 i8 读 1 字节 +
# sext 截断(0xF4 → 0xFFFFFFF4);Test 1 的小值(10)恰好正确
# 掩盖了缺陷。
match pointee:
case llvmlite.LLVMType.Int(pointee_width):
# 允许推断:c.Deref 是赋值语句直接 RHS(v: int = c.Deref(p))
# 或处于类型转换链包裹中(如 vt: t.CVoid | t.CPtr =
# t.CVoid(c.Deref(p)))。嵌套在 while 条件/比较/binop/
# 其他调用中(如 while c.Deref(p) != 0、total +=
# t.CInt(c.Deref(p)))不推断,否则 i8* 会被残留
# CurAssignAnnot 误判为 i32* 读 4 字节 → StringIterTest
# ASCII sum/manual strcmp 失败。
allow_infer: int = 0
if trans is not None and trans.CurAssignNode is not None:
if t.CUInt64T(cl) == t.CUInt64T(trans.CurAssignNode):
allow_infer = 1
elif _IsCastWrappingDeref(trans.CurAssignNode, cl) != 0:
allow_infer = 1
if pointee_width == 8 and allow_infer != 0 \
and trans is not None and trans.CurAssignAnnot is not None:
dref_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
trans.Pool, trans.CurAssignAnnot,
trans._imported_modules, trans._from_imports, trans)
if dref_ty is not None:
dref_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dref_ty)
dref_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(dref_ty)
dref_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(dref_ty)
# 整数/浮点/指针目标均推断(指针 → bitcast 后
# load 8 字节;struct 值目标不推断,保持 i8 读)
dref_ptr: llvmlite.Value | t.CPtr = None
if (dref_bits != 0 or dref_fbits != 0 or dref_is_ptr != 0) \
and dref_bits != 8:
dref_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, dref_ty)
dref_ptr = llvmlite.build_bitcast(
builder, ptr_val, dref_ptr_ty)
if dref_ptr is not None:
return llvmlite.build_load(builder, dref_ty, dref_ptr)
case _:
pass
return llvmlite.build_load(builder, pointee, ptr_val)
# 非指针参数即类型推断退化:fail-fast,绝不按 i8 兜底读取(会读到垃圾宽度)
self.Gen.TypeGenErr.fatal_error(cl, "c.Deref 参数不是指针类型(类型推断退化)", "TypeError")
return None
def translate_c_derefas(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store)"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 2:
return None
ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), None, 0, trans)
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(1), None, 0, trans)
if ptr_val is None or val is None:
return None
# 两端均为指向同一结构体类型的指针时执行结构体拷贝(*ptr = *val),
# 而非存储指针值。这对 REnum 变体构造(c.DerefAs(ptr, LLVMType.Int(32)))至关重要:
# LLVMType.Int(32) 构造返回 alloca 指针(Ptr(LLVMType)),若直接 store 指针值,
# 只写入 8 字节指针(tag 字段仍是 0x0),导致运行时读 tag 错乱。
# 对应 TPC HandlesExprCall.py:3696-3701 的 struct 拷贝逻辑。
if self.Gen.ExprGenBase.is_ptr_type(ptr_val.Ty) != 0 and self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
ptr_pointee: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
val_pointee: llvmlite.LLVMType | t.CPtr = val.Ty.Pointee
if ptr_pointee is not None and val_pointee is not None:
# ptr_pointee 是已注册结构体类型(用 find_struct_by_type 判断)
if self.Gen.StructGen.find_struct_by_type(ptr_pointee) is not None:
if ptr_pointee is val_pointee:
# 同一结构体类型:load 值再 store(结构体拷贝)
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, val_pointee, val)
if loaded is not None:
# bitcast ptr 到结构体指针再 store
struct_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, ptr_pointee)
casted_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, ptr_val, struct_ptr_ty)
if casted_ptr is not None:
llvmlite.build_store(builder, loaded, casted_ptr)
return val
# bitcast ptr 到 val.Ty* 再 store
val_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, val.Ty)
casted_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, ptr_val, val_ptr_ty)
if casted_ptr is None:
return None
llvmlite.build_store(builder, val, casted_ptr)
return val
def translate_c_set(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.Set(a, b) → a = b(赋值语义,a 为左值)"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 2:
return None
a_node: ast.AST | t.CPtr = cargs.get(0)
b_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(1), None, 0, trans)
if b_val is None:
return None
# 形态 1: c.Set(c.Deref(ptr), b) → *ptr = b
if a_node is not None and a_node.kind() == ast.ASTKind.Call:
d_call: ast.Call | t.CPtr = (ast.Call | t.CPtr)(a_node)
if d_call.func is not None and d_call.func.kind() == ast.ASTKind.Attribute:
d_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(d_call.func)
if d_at.attr is not None and string.strcmp(d_at.attr, "Deref") == 0:
d_args: list[ast.AST | t.CPtr] | t.CPtr = d_call.args
if d_args is not None and len(d_args) >= 1:
ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, d_args.get(0), None, 0, trans)
if ptr_val is not None:
val_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, b_val.Ty)
casted_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, ptr_val, val_ptr_ty)
if casted_ptr is not None:
llvmlite.build_store(builder, b_val, casted_ptr)
return b_val
# 其他 Call 形态不可作左值
return b_val
# 形态 2: Name 变量赋值
if a_node is not None and a_node.kind() == ast.ASTKind.Name and trans is not None:
nm_set: ast.Name | t.CPtr = (ast.Name | t.CPtr)(a_node)
if nm_set.id is not None:
if HT.is_global_name(trans, nm_set.id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm_set.id)
if mod_alloca is not None:
tg_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
tg_ty = mod_alloca.Ty.Pointee
sv: llvmlite.Value | t.CPtr = b_val
if tg_ty is not None:
sv = self.Gen.ExprGenBase.coerce_to_type(builder, b_val, tg_ty, a_node)
llvmlite.build_store(builder, sv, mod_alloca)
return b_val
if HT.is_nonlocal_name(trans, nm_set.id) != 0:
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
trans, nm_set.id)
if nl_ptr is not None:
nl_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_ty = nl_ptr.Ty.Pointee
sv2: llvmlite.Value | t.CPtr = b_val
if nl_ty is not None:
sv2 = self.Gen.ExprGenBase.coerce_to_type(builder, b_val, nl_ty, a_node)
llvmlite.build_store(builder, sv2, nl_ptr)
return b_val
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.get_or_create_sym(
trans.SymTab, pool, builder, nm_set.id, b_val.Ty)
if alloca is not None:
ac_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
ac_ty = alloca.Ty.Pointee
sv3: llvmlite.Value | t.CPtr = b_val
if ac_ty is not None:
sv3 = self.Gen.ExprGenBase.coerce_to_type(builder, b_val, ac_ty, a_node)
llvmlite.build_store(builder, sv3, alloca)
return b_val
return b_val
# 形态 3: Attribute 字段赋值
if a_node is not None and a_node.kind() == ast.ASTKind.Attribute:
field_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenAttr.get_attribute_ptr(
builder, pool, mod, a_node, trans)
if field_ptr is not None:
fd_ty: llvmlite.LLVMType | t.CPtr = None
if field_ptr.Ty is not None:
fd_ty = field_ptr.Ty.Pointee
sv4: llvmlite.Value | t.CPtr = b_val
if fd_ty is not None:
sv4 = self.Gen.ExprGenBase.coerce_to_type(builder, b_val, fd_ty, a_node)
llvmlite.build_store(builder, sv4, field_ptr)
return b_val
return b_val
def translate_c_addr(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.Addr(var) → 返回变量的 alloca 指针(不 load)"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 1:
return None
arg0: ast.AST | t.CPtr = cargs.get(0)
if arg0 is None:
return None
# Name 形式: c.Addr(var) → 返回变量的 alloca 指针(不 load)
if arg0.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg0)
if nm.id is None:
return None
if trans is not None:
# self/cls 对象引用:c.Addr(self) 返回对象指针本身(Ptr(Struct)),
# 而非指针变量的地址(Ptr(Ptr(Struct)))。方法与 Attribute 分支
# (c.Addr(self.field) 先 load self)语义一致,供
# return t.CVoid(c.Addr(self), CPtr) 返回 this 使用。
if string.strcmp(nm.id, "self") == 0 or string.strcmp(nm.id, "cls") == 0:
self_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if self_entry is not None and self_entry.Alloca is not None and self_entry.Alloca.Ty is not None:
match self_entry.Alloca.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, inner2)
return llvmlite.build_load(builder, obj_ty, self_entry.Alloca)
case _:
pass
case _:
pass
return_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if return_var is not None and return_var.Ty is not None:
# 对象指针变量(如 lit_tree = zhuff.zhuff_tree(),alloca 是
# Ptr(Ptr(Struct)),存对象指针):c.Addr(对象变量) 应返回对象
# 指针本身(load 一次),对齐 self/cls 分支与 c.Addr(self.field)
# (GEP 后对象地址)。此前直接返回 &变量(Ptr(Ptr(Struct)))→
# 调用端 bitcast 成 Ptr(Struct) 后 ht 指向指针变量存储 → 字段
# 读到指针值/垃圾 → build_decode_tree 越界崩溃
# (zinflate inflate_block_fixed 的 c.Addr(lit_tree) 根因)。
match return_var.Ty:
case llvmlite.LLVMType.Ptr(addr_inner1):
match addr_inner1:
case llvmlite.LLVMType.Ptr(addr_inner2):
match addr_inner2:
case llvmlite.LLVMType.Struct(_, _, _):
addr_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, addr_inner2)
return llvmlite.build_load(builder, addr_obj_ty, return_var)
case _:
pass
case _:
pass
case _:
pass
return return_var
# Attribute 形式: c.Addr(self.field) → 取字段地址(GEP 到字段,不 load)
# 用于 atom.__atomic_test_and_set(c.Addr(self.lock_val), ...) 等锁操作
if arg0.kind() == ast.ASTKind.Attribute:
if trans is None:
return None
at_addr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg0)
if at_addr is None or at_addr.attr is None or at_addr.value is None:
return None
# 解析 self(Name)→ 查找 alloca
if at_addr.value.kind() != ast.ASTKind.Name:
return None
self_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_addr.value)
if self_nm.id is None:
return None
self_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, self_nm.id)
if self_alloca is None or self_alloca.Ty is None:
return None
# 从 self 类型提取结构体类型(Ptr(Struct) 或 Ptr(Ptr(Struct)))
struct_ty: llvmlite.LLVMType | t.CPtr = None
self_is_ptr_ptr: int = 0
match self_alloca.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
struct_ty = inner2
self_is_ptr_ptr = 1
case _:
struct_ty = inner1
case _:
return None
# 查找字段
fe_addr: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
struct_ty, at_addr.attr)
if fe_addr is None:
return None
# self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针
obj_ptr: llvmlite.Value | t.CPtr = self_alloca
if self_is_ptr_ptr == 1:
self_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty)
obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca)
if obj_ptr is None:
return None
# GEP 到字段地址
return llvmlite.build_gep_struct(
builder, struct_ty, fe_addr.Ty, obj_ptr, fe_addr.Index)
# Subscript 形式: c.Addr(arr[i]) → 数组元素地址(GEP 数组,不 load)
# 用于 self.root_directory = c.Addr(directories[directory_count]) 等。
# 此前只支持 Name/Attribute,Subscript 落到函数末尾返回 None →
# 赋值右侧为 None(devfs.vp:159 根因)。
if arg0.kind() == ast.ASTKind.Subscript:
if trans is None:
return None
sub_addr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(arg0)
if sub_addr is None or sub_addr.value is None or sub_addr.slice is None:
return None
arr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_addr.value, None, 0, trans)
if arr_val is None or arr_val.Ty is None:
return None
idx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_addr.slice, None, 0, trans)
if idx_val is None:
return None
# 数组基址 pointee 是 Array(elem_ty, N) → GEP 得元素指针
arr_pointee_ca: llvmlite.LLVMType | t.CPtr = None
if self.Gen.ExprGenBase.is_ptr_type(arr_val.Ty) != 0:
arr_pointee_ca = arr_val.Ty.Pointee
if arr_pointee_ca is not None:
match arr_pointee_ca:
case llvmlite.LLVMType.Array(arr_et_ca, _):
return llvmlite.build_gep_array(
builder, arr_pointee_ca, arr_et_ca, arr_val, idx_val)
case _:
pass
return None
# Call 形式: c.Addr(expr) → 求值 expr 存临时槽(entry alloca),返回槽地址
# 供就地解读使用:tt: Coroutine = c.Addr(task()) —— coerce 指针→结构体时
# 从槽地址 load(target 是 NoVTable 结构体,如 Coroutine={__ptr__} 8 字节)。
if arg0.kind() == ast.ASTKind.Call:
call_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg0, None, 0, trans)
if call_val is None or call_val.Ty is None:
return None
tmp_slot: llvmlite.Value | t.CPtr = llvmlite.build_alloca_at_entry(
builder, call_val.Ty)
if tmp_slot is None:
return None
llvmlite.build_store(builder, call_val, tmp_slot)
return tmp_slot
# 其他 arg0 形式(非 Name/Attribute/Subscript/Call):真实掉出函数末尾,
# 补显式 return None(fail-fast 不允许流程掉出末尾)
return None
def translate_c_load(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.Load(a, b) → *a = *b(加载 b 的值,存储到 a)"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 2:
return None
dst_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(0), None, 0, trans)
src_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cargs.get(1), None, 0, trans)
if dst_ptr is None or src_ptr is None:
return None
# 确定 pointee 类型(优先用 src 的 pointee,回退到 dst 的 pointee)
pointee: llvmlite.LLVMType | t.CPtr = None
if src_ptr.Ty is not None:
pointee = src_ptr.Ty.Pointee
if pointee is None and dst_ptr.Ty is not None:
pointee = dst_ptr.Ty.Pointee
if pointee is None:
return None
# bitcast src 到 pointee* 并 load
target_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, pointee)
src_cast: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, src_ptr, target_ptr_ty)
if src_cast is None:
return None
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, src_cast)
if loaded is None:
return None
# bitcast dst 到 pointee* 并 store
dst_cast: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, dst_ptr, target_ptr_ty)
if dst_cast is None:
return None
llvmlite.build_store(builder, loaded, dst_cast)
return loaded
def _asm_append_cstr(self, dst: str, dst_size: t.CSizeT,
src: str):
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None:
return
dlen: t.CSizeT = string.strlen(dst)
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0:
return
i: t.CSizeT = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'
def _asm_escape_for_ir(self, pool: memhub.MemBuddy | t.CPtr,
src: str) -> str:
"""转义汇编文本用于 LLVM IR 字符串字面量"""
if src is None:
return None
slen: t.CSizeT = string.strlen(src)
buf: str = pool.alloc(slen * 4 + 8)
if buf is None:
return None
pos: t.CSizeT = 0
for ch in src:
if ch == '\n':
buf[pos] = '\\'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = 'A'
pos += 1
elif ch == '"':
buf[pos] = '\\'
pos += 1
buf[pos] = '2'
pos += 1
buf[pos] = '2'
pos += 1
elif ch == '\\':
buf[pos] = '\\'
pos += 1
buf[pos] = '5'
pos += 1
buf[pos] = 'C'
pos += 1
else:
buf[pos] = ch
pos += 1
buf[pos] = '\0'
return buf
def _resolve_register(self, attr_name: str) -> str:
"""将 t.ASM_DESCR.XXX 的属性名映射到约束字符串值"""
if attr_name is None:
return ""
# Clobber 破坏描述符
if string.strcmp(attr_name, "CLOBBER_MEMORY") == 0: return "memory"
if string.strcmp(attr_name, "CLOBBER_CC") == 0: return "cc"
if string.strcmp(attr_name, "CLOBBER_RAX") == 0: return "rax"
if string.strcmp(attr_name, "CLOBBER_RBX") == 0: return "rbx"
if string.strcmp(attr_name, "CLOBBER_RCX") == 0: return "rcx"
if string.strcmp(attr_name, "CLOBBER_RDX") == 0: return "rdx"
if string.strcmp(attr_name, "CLOBBER_RSI") == 0: return "rsi"
if string.strcmp(attr_name, "CLOBBER_RDI") == 0: return "rdi"
if string.strcmp(attr_name, "CLOBBER_RBP") == 0: return "rbp"
if string.strcmp(attr_name, "CLOBBER_RSP") == 0: return "rsp"
if string.strcmp(attr_name, "CLOBBER_R8") == 0: return "r8"
if string.strcmp(attr_name, "CLOBBER_R9") == 0: return "r9"
if string.strcmp(attr_name, "CLOBBER_R10") == 0: return "r10"
if string.strcmp(attr_name, "CLOBBER_R11") == 0: return "r11"
if string.strcmp(attr_name, "CLOBBER_R12") == 0: return "r12"
if string.strcmp(attr_name, "CLOBBER_R13") == 0: return "r13"
if string.strcmp(attr_name, "CLOBBER_R14") == 0: return "r14"
if string.strcmp(attr_name, "CLOBBER_R15") == 0: return "r15"
if string.strcmp(attr_name, "CLOBBER_EAX") == 0: return "eax"
if string.strcmp(attr_name, "CLOBBER_EBX") == 0: return "ebx"
if string.strcmp(attr_name, "CLOBBER_ECX") == 0: return "ecx"
if string.strcmp(attr_name, "CLOBBER_EDX") == 0: return "edx"
if string.strcmp(attr_name, "CLOBBER_ESI") == 0: return "esi"
if string.strcmp(attr_name, "CLOBBER_EDI") == 0: return "edi"
if string.strcmp(attr_name, "CLOBBER_DX") == 0: return "dx"
if string.strcmp(attr_name, "CLOBBER_AX") == 0: return "ax"
if string.strcmp(attr_name, "CLOBBER_CX") == 0: return "cx"
if string.strcmp(attr_name, "CLOBBER_SI") == 0: return "si"
if string.strcmp(attr_name, "CLOBBER_DI") == 0: return "di"
# Output 输出约束
if string.strcmp(attr_name, "OUTPUT_REG") == 0: return "=r"
if string.strcmp(attr_name, "OUTPUT_MEM") == 0: return "=m"
if string.strcmp(attr_name, "OUTPUT_EAX") == 0: return "=a"
if string.strcmp(attr_name, "OUTPUT_EBX") == 0: return "=b"
if string.strcmp(attr_name, "OUTPUT_ECX") == 0: return "=c"
if string.strcmp(attr_name, "OUTPUT_EDX") == 0: return "=d"
if string.strcmp(attr_name, "OUTPUT_ESI") == 0: return "=S"
if string.strcmp(attr_name, "OUTPUT_EDI") == 0: return "=D"
if string.strcmp(attr_name, "OUTPUT_RAX") == 0: return "=a"
if string.strcmp(attr_name, "OUTPUT_RBX") == 0: return "=b"
if string.strcmp(attr_name, "OUTPUT_RCX") == 0: return "=c"
if string.strcmp(attr_name, "OUTPUT_RDX") == 0: return "=d"
if string.strcmp(attr_name, "OUTPUT_RSI") == 0: return "=S"
if string.strcmp(attr_name, "OUTPUT_RDI") == 0: return "=D"
# Input/Register 输入约束
if string.strcmp(attr_name, "REG_ANY") == 0: return "r"
if string.strcmp(attr_name, "REG_EAX") == 0: return "a"
if string.strcmp(attr_name, "REG_EBX") == 0: return "b"
if string.strcmp(attr_name, "REG_ECX") == 0: return "c"
if string.strcmp(attr_name, "REG_EDX") == 0: return "d"
if string.strcmp(attr_name, "REG_ESI") == 0: return "S"
if string.strcmp(attr_name, "REG_EDI") == 0: return "D"
if string.strcmp(attr_name, "REG_RAX") == 0: return "a"
if string.strcmp(attr_name, "REG_RBX") == 0: return "b"
if string.strcmp(attr_name, "REG_RCX") == 0: return "c"
if string.strcmp(attr_name, "REG_RDX") == 0: return "d"
if string.strcmp(attr_name, "REG_RSI") == 0: return "S"
if string.strcmp(attr_name, "REG_RDI") == 0: return "D"
if string.strcmp(attr_name, "INPUT_REG") == 0: return "r"
if string.strcmp(attr_name, "INPUT_MEM") == 0: return "m"
if string.strcmp(attr_name, "INPUT_EAX") == 0: return "a"
if string.strcmp(attr_name, "INPUT_EBX") == 0: return "b"
if string.strcmp(attr_name, "INPUT_ECX") == 0: return "c"
if string.strcmp(attr_name, "INPUT_EDX") == 0: return "d"
if string.strcmp(attr_name, "INPUT_ESI") == 0: return "S"
if string.strcmp(attr_name, "INPUT_EDI") == 0: return "D"
if string.strcmp(attr_name, "INPUT_RAX") == 0: return "a"
if string.strcmp(attr_name, "INPUT_RBX") == 0: return "b"
if string.strcmp(attr_name, "INPUT_RCX") == 0: return "c"
if string.strcmp(attr_name, "INPUT_RDX") == 0: return "d"
if string.strcmp(attr_name, "INPUT_RSI") == 0: return "S"
if string.strcmp(attr_name, "INPUT_RDI") == 0: return "D"
return ""
def _resolve_asm_descr(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr) -> str:
"""递归解析约束表达式,返回约束字符串"""
if node is None:
return ""
k: int = node.kind()
# BinOp with BitOr: 拼接左右
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.op == ast.OpKind.BitOr:
left_str: str = self._resolve_asm_descr(pool, bop.left)
right_str: str = self._resolve_asm_descr(pool, bop.right)
llen: t.CSizeT = string.strlen(left_str)
rlen: t.CSizeT = string.strlen(right_str)
buf: str = pool.alloc(llen + rlen + 1)
if buf is None:
return ""
string.strcpy(buf, left_str)
for i in range(rlen):
buf[llen + i] = right_str[i]
buf[llen + rlen] = '\0'
return buf
return ""
# Attribute: t.ASM_DESCR.XXX 或 t.XXX
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
# t.ASM_DESCR.XXX
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
inner: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
if inner.value is not None and inner.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0:
if inner.attr is not None and string.strcmp(inner.attr, "ASM_DESCR") == 0:
return self._resolve_register(at.attr)
# t.XXX 简化形式
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "t") == 0:
return self._resolve_register(at.attr)
return ""
# Constant: 字符串常量
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_STR:
return cn.str_val
return ""
return ""
def _asm_get_operand(self, base: GenTypes.AsmOperand | t.CPtr,
idx: int) -> GenTypes.AsmOperand | t.CPtr:
"""获取操作数数组中第 idx 个元素的指针"""
entry_size: t.CSizeT = GenTypes.AsmOperand.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(base) + idx * entry_size
return (GenTypes.AsmOperand | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
def _asm_is_c_call(self, node: ast.Call | t.CPtr) -> str:
"""检查 Call 节点是否是 c.XXX 调用,返回属性名或 None"""
if node is None or node.func is None:
return None
if node.func.kind() != ast.ASTKind.Attribute:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return None
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm.id is None or string.strcmp(mod_nm.id, "c") != 0:
return None
return at.attr
def _asm_add_operand(self, operands: GenTypes.AsmOperand | t.CPtr,
count: int,
val: llvmlite.Value | t.CPtr,
constraint: str,
is_output: int) -> int:
"""添加操作数到数组,返回新计数(失败返回原计数)
去重:f-string 模板内嵌的 AsmOut/AsmInp 与 out=/inp= 声明指向
同一变量时会重复收集(如 vrandom.vp 的 c.Asm(f"...{AsmOut(v)}",
out=[AsmOut(v)])),同一 (Value, IsOutput) 只保留第一个,
避免约束数多于 asm 返回元素数(llc: output constraints mismatch)。
"""
if count >= ASM_MAX_OPERANDS:
return count
if val is not None:
for ei in range(count):
eop: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, ei)
if eop is not None and eop.Value == val and eop.IsOutput == is_output:
return count
op: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, count)
op.Value = val
op.Constraint = constraint
op.IsOutput = is_output
return count + 1
def translate_c_asm(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译 c.Asm 内联汇编,返回结果 Value(无输出时返回 None)"""
if cl is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 1:
return None
# 分配操作数数组
operand_size: t.CSizeT = GenTypes.AsmOperand.__sizeof__()
operands: GenTypes.AsmOperand | t.CPtr = pool.alloc(operand_size * ASM_MAX_OPERANDS)
if operands is None:
return None
string.memset(operands, 0, operand_size * ASM_MAX_OPERANDS)
# 分配 clobber 名数组
clobbers: str | t.CPtr = pool.alloc(8 * ASM_MAX_OPERANDS)
if clobbers is None:
return None
string.memset(clobbers, 0, 8 * ASM_MAX_OPERANDS)
# 分配 asm 文本缓冲区
asm_text: str = pool.alloc(ASM_TEXT_BUF_SIZE)
if asm_text is None:
return None
asm_text[0] = '\0'
operand_count: int = 0
clobber_count: int = 0
fv_count: int = 0
# 分配 fv 索引数组(记录每个 FormattedValue 对应的操作数原始索引)
fv_orig_indices: int | t.CPtr = pool.alloc(4 * ASM_MAX_OPERANDS)
if fv_orig_indices is None:
return None
string.memset(fv_orig_indices, 0, 4 * ASM_MAX_OPERANDS)
# ============================================================
# 1. 解析汇编模板(JoinedStr 或 Constant)— 第一遍:收集操作数
# 不生成 asm 文本(占位符 $N 需在重排后才能确定编号)
# ============================================================
tmpl_node: ast.AST | t.CPtr = cargs.get(0)
if tmpl_node is not None:
tk: int = tmpl_node.kind()
if tk == ast.ASTKind.JoinedStr:
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(tmpl_node)
jvalues: list[ast.AST | t.CPtr] | t.CPtr = js.values
if jvalues is not None:
jvlen: t.CSizeT = len(jvalues)
for vi in range(jvlen):
part: ast.AST | t.CPtr = jvalues.get(vi)
if part is None:
continue
pk: int = part.kind()
if pk == ast.ASTKind.FormattedValue:
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
fexpr: ast.AST | t.CPtr = fv.value
if fexpr is None or fexpr.kind() != ast.ASTKind.Call:
continue
fcall: ast.Call | t.CPtr = (ast.Call | t.CPtr)(fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
if attr_nm is None:
continue
is_out: int = 0
if string.strcmp(attr_nm, "AsmOut") == 0:
is_out = 1
elif string.strcmp(attr_nm, "AsmInp") == 0:
is_out = 0
else:
continue
fargs: list[ast.AST | t.CPtr] | t.CPtr = fcall.args
if fargs is None or len(fargs) < 1:
continue
val_node: ast.AST | t.CPtr = fargs.get(0)
cons_str: str = ""
if len(fargs) >= 2:
cons_str = self._resolve_asm_descr(pool, fargs.get(1))
if is_out != 0:
# 输出:获取变量 alloca
if val_node is None or val_node.kind() != ast.ASTKind.Name:
continue
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node)
if nm.id is None:
continue
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None:
continue
operand_count = self._asm_add_operand(
operands, operand_count, alloca, cons_str, 1)
else:
# 输入:翻译值
ival: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, val_node, None, 0, trans)
if ival is None:
continue
operand_count = self._asm_add_operand(
operands, operand_count, ival, cons_str, 0)
# 记录 fv 的原始操作数索引
if fv_count < ASM_MAX_OPERANDS:
fv_orig_indices[fv_count] = operand_count - 1
fv_count += 1
# ============================================================
# 2. 解析关键字参数 out=/inp=/op=/clobber=
# ============================================================
keywords: list[ast.AST | t.CPtr] | t.CPtr = cl.keywords
if keywords is not None:
kwlen: t.CSizeT = len(keywords)
for ki in range(kwlen):
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
kwk: int = kw.value.kind()
if string.strcmp(kw.arg, "out") == 0:
# 输出操作数列表
if kwk != ast.ASTKind.List:
continue
out_lst: ast.List | t.CPtr = (ast.List | t.CPtr)(kw.value)
out_elts: list[ast.AST | t.CPtr] | t.CPtr = out_lst.elts
if out_elts is None:
continue
oelen: t.CSizeT = len(out_elts)
for oi in range(oelen):
oelt: ast.AST | t.CPtr = out_elts.get(oi)
if oelt is None or oelt.kind() != ast.ASTKind.Call:
continue
ocall: ast.Call | t.CPtr = (ast.Call | t.CPtr)(oelt)
oattr: str = self._asm_is_c_call(ocall)
if oattr is None or string.strcmp(oattr, "AsmOut") != 0:
continue
oargs: list[ast.AST | t.CPtr] | t.CPtr = ocall.args
if oargs is None or len(oargs) < 1:
continue
oval_node: ast.AST | t.CPtr = oargs.get(0)
ocons: str = ""
if len(oargs) >= 2:
ocons = self._resolve_asm_descr(pool, oargs.get(1))
if oval_node is None or oval_node.kind() != ast.ASTKind.Name:
continue
onm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(oval_node)
if onm.id is None:
continue
oalloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, onm.id)
if oalloca is None:
continue
operand_count = self._asm_add_operand(
operands, operand_count, oalloca, ocons, 1)
elif string.strcmp(kw.arg, "inp") == 0 or string.strcmp(kw.arg, "inputs") == 0:
# 输入操作数列表
if kwk != ast.ASTKind.List:
continue
in_lst: ast.List | t.CPtr = (ast.List | t.CPtr)(kw.value)
in_elts: list[ast.AST | t.CPtr] | t.CPtr = in_lst.elts
if in_elts is None:
continue
ielen: t.CSizeT = len(in_elts)
for ii in range(ielen):
ielt: ast.AST | t.CPtr = in_elts.get(ii)
if ielt is None or ielt.kind() != ast.ASTKind.Call:
continue
icall: ast.Call | t.CPtr = (ast.Call | t.CPtr)(ielt)
iattr: str = self._asm_is_c_call(icall)
if iattr is None or string.strcmp(iattr, "AsmInp") != 0:
continue
iargs: list[ast.AST | t.CPtr] | t.CPtr = icall.args
if iargs is None or len(iargs) < 1:
continue
ival_node: ast.AST | t.CPtr = iargs.get(0)
icons: str = ""
if len(iargs) >= 2:
icons = self._resolve_asm_descr(pool, iargs.get(1))
ival2: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, ival_node, None, 0, trans)
if ival2 is None:
continue
operand_count = self._asm_add_operand(
operands, operand_count, ival2, icons, 0)
elif string.strcmp(kw.arg, "op") == 0 or string.strcmp(kw.arg, "clobber") == 0:
# 破坏列表
if kwk != ast.ASTKind.List:
continue
cl_lst: ast.List | t.CPtr = (ast.List | t.CPtr)(kw.value)
cl_elts: list[ast.AST | t.CPtr] | t.CPtr = cl_lst.elts
if cl_elts is None:
continue
clen: t.CSizeT = len(cl_elts)
for ci in range(clen):
celt: ast.AST | t.CPtr = cl_elts.get(ci)
if celt is None:
continue
cstr: str = self._resolve_asm_descr(pool, celt)
if cstr is None or cstr[0] == '\0':
continue
if clobber_count < ASM_MAX_OPERANDS:
cl_addr: t.CUInt64T = t.CUInt64T(clobbers) + clobber_count * 8
cl_ptr: str | t.CPtr = (str | t.CPtr)(t.CVoid(cl_addr, t.CPtr))
cl_ptr[0] = cstr
clobber_count += 1
# ============================================================
# 3. 解析第二个位置参数作为破坏列表
# ============================================================
if len(cargs) >= 2:
pos_node: ast.AST | t.CPtr = cargs.get(1)
if pos_node is not None and pos_node.kind() == ast.ASTKind.List:
pos_lst: ast.List | t.CPtr = (ast.List | t.CPtr)(pos_node)
pos_elts: list[ast.AST | t.CPtr] | t.CPtr = pos_lst.elts
if pos_elts is not None:
plen: t.CSizeT = len(pos_elts)
for pi in range(plen):
pelt: ast.AST | t.CPtr = pos_elts.get(pi)
if pelt is None:
continue
pstr: str = self._resolve_asm_descr(pool, pelt)
if pstr is None or pstr[0] == '\0':
continue
if clobber_count < ASM_MAX_OPERANDS:
pcl_addr: t.CUInt64T = t.CUInt64T(clobbers) + clobber_count * 8
pcl_ptr: str | t.CPtr = (str | t.CPtr)(t.CVoid(pcl_addr, t.CPtr))
pcl_ptr[0] = pstr
clobber_count += 1
# ============================================================
# 4. 统计输出/输入数量,确定返回类型
# ============================================================
output_count: int = 0
input_count: int = 0
first_output_idx: int = -1
for oi2 in range(operand_count):
op2: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, oi2)
if op2.IsOutput != 0:
if first_output_idx < 0:
first_output_idx = oi2
output_count += 1
else:
input_count += 1
# 确定返回类型
# 多输出(如 vrandom rdrand*_step 的 val+ok 两个 AsmOut)返回
# 匿名 struct {ty1, ty2, ...},元素数 = 输出操作数(LLVM asm
# 要求输出约束数 = 返回 struct 元素数)。
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
if output_count > 0 and first_output_idx >= 0:
if output_count > 1:
out_pn_head: llvmlite.ParamNode | t.CPtr = None
out_pn_last: llvmlite.ParamNode | t.CPtr = None
out_pn_n: int = 0
for oi5 in range(operand_count):
op5: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, oi5)
if op5 is None or op5.IsOutput == 0:
continue
if op5.Value is not None and op5.Value.Ty is not None \
and op5.Value.Ty.Pointee is not None:
pn5: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(
pool, op5.Value.Ty.Pointee)
if pn5 is not None:
if out_pn_head is None:
out_pn_head = pn5
else:
out_pn_last.Next = pn5
out_pn_last = pn5
out_pn_n += 1
if out_pn_head is not None and out_pn_n > 0:
ret_ty = llvmlite.Struct(pool, out_pn_head, out_pn_n, None)
else:
self.Gen.TypeGenErr.fatal_error(
cl, "c.Asm 输出操作数类型缺失(变量 alloca 类型退化)", "TypeError")
ret_ty = llvmlite.Int32(pool)
else:
out_op: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, first_output_idx)
if out_op.Value is not None and out_op.Value.Ty is not None:
# alloca 的类型是指针,Pointee 是目标类型
if out_op.Value.Ty.Pointee is not None:
ret_ty = out_op.Value.Ty.Pointee
else:
self.Gen.TypeGenErr.fatal_error(
cl, "c.Asm 输出操作数类型缺失(变量 alloca 类型退化)", "TypeError")
ret_ty = llvmlite.Int32(pool)
# 判断 void — 直接基于 output_count 判断,避免 match 类型匹配问题
is_void: int = 1
if output_count > 0:
is_void = 0
# ============================================================
# 4.5 重排操作数:输出在前,输入在后(LLVM IR 要求)
#
# new_order[new_idx] = orig_idx — 新位置对应原索引
# orig_to_new[orig_idx] = new_idx — 原索引映射到新位置(用于占位符编号)
# ============================================================
new_order: int | t.CPtr = pool.alloc(4 * (operand_count + 1))
orig_to_new: int | t.CPtr = pool.alloc(4 * (operand_count + 1))
if new_order is None or orig_to_new is None:
return None
new_idx: int = 0
# 先放输出
for oi3 in range(operand_count):
op3: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, oi3)
if op3.IsOutput != 0:
new_order[new_idx] = oi3
new_idx += 1
# 再放输入
for oi4 in range(operand_count):
op4: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, oi4)
if op4.IsOutput == 0:
new_order[new_idx] = oi4
new_idx += 1
# 建立映射 orig -> new
for ni2 in range(operand_count):
orig_to_new[new_order[ni2]] = ni2
# ============================================================
# 4.6 生成汇编文本(第二遍遍历 f-string,用重排后的索引生成占位符)
# 项目约定使用 Intel 语法,通过 IR 的 inteldialect 关键字启用
# ============================================================
if tmpl_node is not None:
tk2: int = tmpl_node.kind()
if tk2 == ast.ASTKind.JoinedStr:
js2: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(tmpl_node)
jvalues2: list[ast.AST | t.CPtr] | t.CPtr = js2.values
if jvalues2 is not None:
jvlen2: t.CSizeT = len(jvalues2)
fv_fi: int = 0
for vi2 in range(jvlen2):
part2: ast.AST | t.CPtr = jvalues2.get(vi2)
if part2 is None:
continue
pk2: int = part2.kind()
if pk2 == ast.ASTKind.Constant:
cn3: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part2)
if cn3.const_kind == ast.CONST_STR and cn3.str_val is not None:
self._asm_append_cstr(asm_text, ASM_TEXT_BUF_SIZE, cn3.str_val)
elif pk2 == ast.ASTKind.FormattedValue:
if fv_fi < fv_count:
orig_idx2: int = fv_orig_indices[fv_fi]
new_idx2: int = orig_to_new[orig_idx2]
ph_buf2: str = pool.alloc(16)
if ph_buf2 is not None:
viperlib.snprintf(ph_buf2, 16, "$%d", new_idx2)
self._asm_append_cstr(asm_text, ASM_TEXT_BUF_SIZE, ph_buf2)
fv_fi += 1
elif tk2 == ast.ASTKind.Constant:
cn4: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(tmpl_node)
if cn4.const_kind == ast.CONST_STR and cn4.str_val is not None:
self._asm_append_cstr(asm_text, ASM_TEXT_BUF_SIZE, cn4.str_val)
# ============================================================
# 5. 构建约束字符串(按重排后的顺序:输出在前,输入在后)
# ============================================================
const_buf: str = pool.alloc(ASM_CONST_BUF_SIZE)
if const_buf is None:
return None
const_buf[0] = '\0'
first_const: int = 1
for ci2 in range(operand_count):
orig_i: int = new_order[ci2]
cop: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, orig_i)
cons_val: str = cop.Constraint
if cons_val is None:
cons_val = "r"
if first_const == 0:
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, ",")
first_const = 0
if cop.IsOutput != 0:
# 输出约束:确保以 = 开头
if cons_val[0] != '=':
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "=")
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cons_val)
else:
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cons_val)
# 追加 clobber 约束 ~{name}
for cidx in range(clobber_count):
cl_addr2: t.CUInt64T = t.CUInt64T(clobbers) + cidx * 8
cname: str = (str | t.CPtr)(t.CVoid(cl_addr2, t.CPtr))[0]
if cname is None or cname[0] == '\0':
continue
if first_const == 0:
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, ",")
first_const = 0
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "~{")
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cname)
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "}")
# ============================================================
# 6. 构建参数列表文本(仅输入操作数)
# ============================================================
args_buf: str = pool.alloc(ASM_ARGS_BUF_SIZE)
if args_buf is None:
return None
args_buf[0] = '\0'
first_arg: int = 1
for ai3 in range(operand_count):
orig_i2: int = new_order[ai3]
aop: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, orig_i2)
if aop.IsOutput != 0:
continue
aval: llvmlite.Value | t.CPtr = aop.Value
if aval is None or aval.Ty is None or aval.Name is None:
continue
if first_arg == 0:
self._asm_append_cstr(args_buf, ASM_ARGS_BUF_SIZE, ", ")
first_arg = 0
# 类型文本
ty_buf: str = pool.alloc(64)
if ty_buf is not None:
ty_buf[0] = '\0'
llvmlite.TypePrint(ty_buf, 64, aval.Ty, pool)
self._asm_append_cstr(args_buf, ASM_ARGS_BUF_SIZE, ty_buf)
self._asm_append_cstr(args_buf, ASM_ARGS_BUF_SIZE, " ")
self._asm_append_cstr(args_buf, ASM_ARGS_BUF_SIZE, aval.Name)
# ============================================================
# 7. 转义汇编文本
# ============================================================
escaped_asm: str = self._asm_escape_for_ir(pool, asm_text)
if escaped_asm is None:
return None
# ============================================================
# 8. 生成 IR 行并发射
# ============================================================
line: str = pool.alloc(ASM_LINE_BUF_SIZE)
if line is None:
return None
line[0] = '\0'
ssa_name: str = None
if is_void == 0:
ssa_name = pool.alloc(16)
if ssa_name is not None:
viperlib.snprintf(ssa_name, 16, "%%%d", builder.Counter)
builder.Counter += 1
# 获取返回类型文本
ret_ty_buf: str = pool.alloc(64)
if ret_ty_buf is not None:
ret_ty_buf[0] = '\0'
llvmlite.TypePrint(ret_ty_buf, 64, ret_ty, pool)
if is_void == 1:
viperlib.snprintf(line, ASM_LINE_BUF_SIZE,
"call %s asm sideeffect inteldialect \"%s\", \"%s\"(%s)",
ret_ty_buf, escaped_asm, const_buf, args_buf)
else:
viperlib.snprintf(line, ASM_LINE_BUF_SIZE,
"%s = call %s asm sideeffect inteldialect \"%s\", \"%s\"(%s)",
ssa_name, ret_ty_buf, escaped_asm, const_buf, args_buf)
# 发射 IR 到当前块
llvmlite.block_append_text(pool, builder.CurBlock, line)
# ============================================================
# 9. 处理输出:存储结果到输出变量
# ============================================================
if is_void == 0 and first_output_idx >= 0:
result_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, ret_ty, ssa_name)
out_emit_idx: int = 0
for oi_out in range(operand_count):
orig_o: int = new_order[oi_out]
oo: GenTypes.AsmOperand | t.CPtr = self._asm_get_operand(operands, orig_o)
if oo is None or oo.IsOutput == 0:
continue
if output_count > 1:
# 多输出:extractvalue 取第 out_emit_idx 个元素后 store
if oo.Value is not None and oo.Value.Ty is not None \
and oo.Value.Ty.Pointee is not None:
ev_name: str = pool.alloc(32)
ev_line: str = pool.alloc(ASM_LINE_BUF_SIZE)
if ev_name is not None and ev_line is not None:
# ssa_name 形如 "%N"(纯数字 ID),构造唯一名
# "%asm.N.e{i}"(字母开头,避免 %N.e{i} 被解析为
# 数字 ID + 非法后缀导致 "expected '=' after id")
viperlib.snprintf(ev_name, 32, "%%asm.%s.e%d", ssa_name + 1, out_emit_idx)
viperlib.snprintf(ev_line, ASM_LINE_BUF_SIZE,
"%s = extractvalue %s %s, %d",
ev_name, ret_ty_buf, ssa_name, out_emit_idx)
llvmlite.block_append_text(pool, builder.CurBlock, ev_line)
ev_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(
pool, oo.Value.Ty.Pointee, ev_name)
llvmlite.build_store(builder, ev_val, oo.Value)
else:
if oo.Value is not None:
llvmlite.build_store(builder, result_val, oo.Value)
out_emit_idx += 1
return result_val
return None
def translate_c_llvmir(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""c.LLVMIR 内联 LLVM IR:无返回值,直接嵌入原始 IR 文本
不做指令解析,模板文本(c.LInp/c.LOut 替换后)用 block_append_text
嵌入当前基本块。c.LOut 变量由唯一 SSA 寄存器 %llvmirN 接收结果,
嵌入后自动追加 store 写回变量 alloca。返回 None(无返回值)。
"""
if cl is None or builder is None or pool is None or trans is None:
return None
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or len(cargs) < 1:
self.Gen.TypeGenErr.fatal_error(cl, "c.LLVMIR 缺少模板参数", "TypeError")
# LOut 写回记录(alloca 指针 + 寄存器名),最多 LLVMIR_MAX_OPS 个
out_alloca: t.CSizeT | t.CPtr = pool.alloc(8 * LLVMIR_MAX_OPS)
out_regname: t.CSizeT | t.CPtr = pool.alloc(8 * LLVMIR_MAX_OPS)
if out_alloca is None or out_regname is None:
return None
string.memset(out_alloca, 0, 8 * LLVMIR_MAX_OPS)
string.memset(out_regname, 0, 8 * LLVMIR_MAX_OPS)
out_count: int = 0
# 模板文本缓冲区
text: str = pool.alloc(LLVMIR_TEMPLATE_BUF)
if text is None:
return None
text[0] = '\0'
# ============================================================
# 1. 解析 IR 模板(JoinedStr 或 Constant)— 替换 LInp/LOut/表达式为值文本
# ============================================================
first_arg: ast.AST | t.CPtr = cargs.get(0)
if first_arg is None:
return None
tk: int = first_arg.kind()
if tk == ast.ASTKind.JoinedStr:
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(first_arg)
jvalues: list[ast.AST | t.CPtr] | t.CPtr = js.values
if jvalues is not None:
jvlen: t.CSizeT = len(jvalues)
for vi in range(jvlen):
part: ast.AST | t.CPtr = jvalues.get(vi)
if part is None:
continue
pk: int = part.kind()
if pk == ast.ASTKind.Constant:
# 字符串字面量片段
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
if cn.str_val is not None:
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn.str_val)
elif pk == ast.ASTKind.FormattedValue:
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
fexpr: ast.AST | t.CPtr = fv.value
if fexpr is None:
continue
# 检测 c.LInp / c.LOut
if fexpr.kind() == ast.ASTKind.Call:
fcall: ast.Call | t.CPtr = (ast.Call | t.CPtr)(fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
if attr_nm is not None:
fargs: list[ast.AST | t.CPtr] | t.CPtr = fcall.args
if fargs is None or len(fargs) < 1:
continue
val_node: ast.AST | t.CPtr = fargs.get(0)
if string.strcmp(attr_nm, "LInp") == 0:
# 输入操作数:翻译值,替换为 LLVM 值引用文本(%x 或常量文本)
inp_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, val_node, None, 0, trans)
if inp_val is not None and inp_val.Name is not None:
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, inp_val.Name)
elif string.strcmp(attr_nm, "LOut") == 0:
# 输出操作数:为变量分配唯一 SSA 寄存器 %llvmirN,
# 记录写回(模板嵌入后 store 到变量 alloca)
if val_node is not None and val_node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node)
if nm.id is not None:
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, nm.id)
if alloca is None or out_count >= LLVMIR_MAX_OPS:
self.Gen.TypeGenErr.fatal_error(
fcall,
f"c.LOut 变量 '{nm.id}' 未定义或输出数超过 {LLVMIR_MAX_OPS}",
"TypeError")
self.Gen.CallGenBase.LLVMIRRegSeq += 1
reg_buf: str = pool.alloc(32)
if reg_buf is not None:
viperlib.snprintf(reg_buf, 32, "%%llvmir_%d", self.Gen.CallGenBase.LLVMIRRegSeq)
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, reg_buf)
out_alloca[out_count] = t.CSizeT(alloca)
out_regname[out_count] = t.CSizeT(reg_buf)
out_count += 1
continue
# 非 c.LInp/c.LOut 的 f-string 表达式:翻译值并嵌入值文本
ival: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fexpr, None, 0, trans)
if ival is not None and ival.Name is not None:
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, ival.Name)
elif tk == ast.ASTKind.Constant:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(first_arg)
if cn2.str_val is not None:
# 裸 strcpy 无界:模板常量串 ≥ LLVMIR_TEMPLATE_BUF 会越界写 pool 块,
# 改用与 JoinedStr 分支一致的有界追加
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn2.str_val)
# ============================================================
# 2. 直接嵌入模板文本(不做指令解析)+ LOut 写回
# ============================================================
llvmlite.block_append_text(pool, builder.CurBlock, text)
# LOut 写回: store <ty> %llvmirN, <ty>* %var
oi: int
for oi in range(out_count):
oa: llvmlite.Value | t.CPtr = (llvmlite.Value | t.CPtr)(t.CVoid(out_alloca[oi], t.CPtr))
rn: str = (str)(t.CVoid(out_regname[oi], t.CPtr))
if oa is None or oa.Ty is None or oa.Ty.Pointee is None or rn is None:
continue
reg_val: llvmlite.Value | t.CPtr = llvmlite.new_value(pool)
if reg_val is None:
continue
reg_val.Ty = oa.Ty.Pointee
reg_val.Name = rn
reg_val.IsConst = 0
llvmlite.build_store(builder, reg_val, oa)
return None
# ============================================================
# _apply_struct_defaults — 应用结构体字段的默认值
#
# 在 zeroinitializer 之后、显式参数之前调用
# 遍历所有字段,对有默认值的字段翻译并 store
# 显式参数(位置/关键字)后续会覆盖默认值
# ============================================================