Files
ViperC/App/lib/core/Gen/Call/CallGenCBuiltin.vp
T
TermiNexus 1ad7abf33d refactor(TransPyV): 统一自举写法(novirtual / define / rawptr→* / 去续行反斜杠)
- @t.NoVTable class X → novirtual class X
- t.CDefine = N → define N
- rawptr[X]、X | t.CPtr → X*
- t.CPtr → rawptr(裸指针标记)
- t.Callable[...] → callable[...]
- 去掉 if/elif 条件的行续反斜杠(改单行写法)
- w32.win32file.X → win32file.X(已 import as)
- core/__init__.vp 去掉未使用的 import t, c / from stdint import *

涉及:CompilerFactory、IncludesScanner、Repl、core/__init__、
Gen/Call/{CallGenBase,CallGenCBuiltin,CallGenCall,CallGenDispatch,CallGenLeafKit}、
Gen/Class/ClassGenGeneric。
2026-10-02 02:16:37 +08:00

1659 lines
95 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 t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
from . import CallGenBase
# ============================================================
# CallGenCBuiltin
# ============================================================
# ============================================================
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
# ============================================================
define _CEXPORT_MAX = 256
define ASM_MAX_OPERANDS = 16
# clobber(~{...})容量独立于操作数上限:vipersimd ×8 内核单条 c.Asm
# 需 21 项(memory + ymm0-15 + rcx/r8-r11)。此前复用 ASM_MAX_OPERANDS=16
# 导致 21 项静默截断为 16,靠后的 GPR clobber(rcx/r8-r11)丢失 → llc 把
# "r" 输入分进 asm 硬编码寄存器 → 现场 r8/r10=4、vmovupd [r8] 读 0x4 崩溃。
define ASM_MAX_CLOBBERS = 32
define ASM_TEXT_BUF_SIZE = 2048
define ASM_CONST_BUF_SIZE = 512
define ASM_ARGS_BUF_SIZE = 512
define ASM_LINE_BUF_SIZE = 4096
define LLVMIR_MAX_OPS = 16
define LLVMIR_TEMPLATE_BUF = 512
# ============================================================
# CallGenCBuiltin - c.*/asm/llvmir 内建
# ============================================================
novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
"""c.* 内建 + 内联汇编 + LLVMIR 翻译(继承 Base)"""
def translate_c_deref(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""c.Deref(ptr) → load,宽度只由 ptr 的 pointee 类型决定
pointee 是 i8(t.CPtr / void* / str / bytes)恒读 1 字节;不再按赋值
目标注解(CurAssignAnnot)反推宽度——那会让同一处 c.Deref(i8*) 的读取
宽度取决于它是否恰好出现在带注解赋值的 RHS 里。需要按更宽类型读时,
由调用点显式给出元素类型指针后再解引用(见下方读取分支注释)。
"""
if cl is None: return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1: return None
ptr_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), None, 0, trans)
if ptr_val is None: return None
# rawptr[t.CVolatile | T] 实参(volatile 指针指向物):c.Deref(p) 的
# 解引用 load 带 volatile。仅当实参可解析到 VarEntry(Name)/ FieldEntry
# (Attribute)时为 1;其余(函数返回/调用/复杂表达式)为 0(明确不支持)。
vol_deref: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, cargs.get(0))
# 读取宽度只由指针类型决定:按 pointee 类型 load 一个元素。
# pointee 是 i8(t.CPtr / void* / str / bytes)即恒读 1 字节。
# 需要 4/8 字节等更宽读取时,必须在调用点把指针显式变成有元素类型的
# 指针再解引用(c.Deref((rawptr[T])(p)) / c.Deref((T | t.CPtr)(x))),
# 不再由赋值目标注解反推——否则同一处 c.Deref(i8*) 的宽度会随上下文
# (是否带注解赋值的 RHS)而变。
if self.Gen.ExprGenBase.is_ptr_type(ptr_val.Ty) != 0:
pointee: llvmlite.LLVMType* = ptr_val.Ty.Pointee
if pointee is not None:
return llvmlite.build_load(builder, pointee, ptr_val, vol_deref)
# 非指针参数即类型推断退化:fail-fast,绝不按 i8 兜底读取(会读到垃圾宽度)
self.Gen.TypeGenErr.fatal_error(cl, "c.Deref 参数不是指针类型(类型推断退化)", "TypeError")
return None
def translate_c_derefas(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""c.DerefAs(ptr, val) → store val to ptr(bitcast 后 store)
单参形式 c.DerefAs(ptr) → 读取 ptr 指向的值(等价 c.Deref(ptr))。
"""
if cl is None:
return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
# 单参形式:deref 读(paging.vp save_pt_range L263
# saved = c.DerefAs(save_count)、restore_pt_range L277-278
# c.DerefAs(entry_ptr) 根因——此前 len<2 直接 return None →
# 跌落跨模块函数调用生成 call @DerefAs → ld.lld undefined symbol:
# DerefAs。读取宽度与 c.Deref 同口径(只认 pointee 类型),
# 故直接复用 translate_c_deref。
if len(cargs) < 2:
return self.translate_c_deref(pool, builder, mod, cl, trans)
ptr_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), None, 0, trans)
val: llvmlite.Value* = 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* = ptr_val.Ty.Pointee
val_pointee: llvmlite.LLVMType* = 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* = llvmlite.build_load(builder, val_pointee, val)
if loaded is not None:
# bitcast ptr 到结构体指针再 store
struct_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, ptr_pointee)
casted_ptr: llvmlite.Value* = 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* = llvmlite.Ptr(pool, val.Ty)
casted_ptr: llvmlite.Value* = 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*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""c.Set(a, b) → a = b(赋值语义,a 为左值)"""
if cl is None: return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 2: return None
a_node: ast.AST* = cargs.get(0)
b_val: llvmlite.Value* = 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* = ast.Call*(a_node)
if d_call.func is not None and d_call.func.kind() == ast.ASTKind.Attribute:
d_at: ast.Attribute* = ast.Attribute*(d_call.func)
if d_at.attr is not None and string.strcmp(d_at.attr, "Deref") == 0:
d_args: list[ast.AST*]* = d_call.args
if d_args is not None and len(d_args) >= 1:
ptr_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, d_args.get(0), None, 0, trans)
if ptr_val is not None:
# 写入宽度以「左值指针自身的元素类型」为准:
# c.Set(c.Deref(<T*>(p)), v) 应先把 v 转换到 T,再按 T 宽度写入。
# 此前用 Ptr(b_val.Ty) 重解释指针并按 RHS 宽度 store,导致
# c.Deref(uint16(p)) 被 i32 字面量写成 4 字节(越界 2 字节)、
# c.Deref(uint32(p)) 被 0xFFFFFFFF(i64) 写成 8 字节(越界 4 字节)。
# 无类型指针(元素为 i8,如 t.CPtr/i8* 形参)维持原行为:
# 按 RHS 类型重解释指针 store(如 paging save_count 写 i32)。
store_ptr: llvmlite.Value* = ptr_val
store_val: llvmlite.Value* = b_val
typed_elem: int = 0
elem_ty: llvmlite.LLVMType* = None
if ptr_val.Ty is not None:
elem_ty = ptr_val.Ty.Pointee
if elem_ty is not None:
eb: int = self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty)
if eb != 0 and eb != 8:
typed_elem = 1
elif self.Gen.ExprGenBase.get_llvm_float_bits(elem_ty) != 0:
typed_elem = 1
elif self.Gen.ExprGenBase.is_ptr_type(elem_ty) != 0:
typed_elem = 1
elif self.Gen.ExprGenBase.is_array_type(elem_ty) != 0:
typed_elem = 1
if typed_elem != 0:
store_val = self.Gen.ExprGenBase.coerce_to_type(builder, b_val, elem_ty, a_node)
else:
val_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, b_val.Ty)
casted_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, ptr_val, val_ptr_ty)
if casted_ptr is not None:
store_ptr = casted_ptr
if store_ptr is not None and store_val is not None:
llvmlite.build_store(builder, store_val, store_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* = ast.Name*(a_node)
if nm_set.id is not None:
if GenApi.is_global_name(trans, nm_set.id) != 0:
mod_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm_set.id)
if mod_alloca is not None:
tg_ty: llvmlite.LLVMType* = None
if mod_alloca.Ty is not None:
tg_ty = mod_alloca.Ty.Pointee
sv: llvmlite.Value* = 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 GenApi.is_nonlocal_name(trans, nm_set.id) != 0:
nl_ptr: llvmlite.Value* = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(trans, nm_set.id)
if nl_ptr is not None:
nl_ty: llvmlite.LLVMType* = None
if nl_ptr.Ty is not None:
nl_ty = nl_ptr.Ty.Pointee
sv2: llvmlite.Value* = 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* = 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* = None
if alloca.Ty is not None:
ac_ty = alloca.Ty.Pointee
sv3: llvmlite.Value* = 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* = self.Gen.ExprGenAttr.get_attribute_ptr(builder, pool, mod, a_node, trans)
if field_ptr is not None:
fd_ty: llvmlite.LLVMType* = None
if field_ptr.Ty is not None:
fd_ty = field_ptr.Ty.Pointee
sv4: llvmlite.Value* = 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*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""c.Addr(var) → 返回变量的 alloca 指针(不 load)"""
if cl is None:
return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
arg0: ast.AST* = cargs.get(0)
if arg0 is None:
return None
# Name 形式: c.Addr(var) → 返回变量的 alloca 指针(不 load)
if arg0.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(arg0)
if nm.id is None:
return None
if trans is not None:
# self/cls 对象引用:c.Addr(self) 返回对象指针本身(Ptr(Struct)),
# 而非指针变量的地址(Ptr(Ptr(Struct)))。方法与 Attribute 分支
if string.strcmp(nm.id, "self") == 0 or string.strcmp(nm.id, "cls") == 0:
self_entry: GenTypes.VarEntry* = 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* = llvmlite.Ptr(pool, inner2)
return llvmlite.build_load(builder, obj_ty, self_entry.Alloca)
case _:
pass
case _:
pass
# nonlocal 变量:提升函数内从 env 读对象指针(load_nonlocal_var),
# 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱崩溃)。
# 与普通局部 c.Addr(obj) 返回对象指针(load 一次)语义一致。
if trans is not None:
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
nl_addr: llvmlite.Value* = self.Gen.StmtGenDecl.load_nonlocal_var(trans, nm.id)
if nl_addr is not None:
return nl_addr
return_var: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
# REPL 模式(M2)会话变量取址:c.Addr(x) → ctx 内该槽的地址(地址同一性)。
# 位置在局部查找之后(片段自己声明的局部名优先,与读路径同序)、
# 模块全局/函数引用之前。守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 不变。
if return_var is None and trans is not None and trans.ReplCtx is not None:
sess_addr: llvmlite.Value* = self.Gen.VarGen.repl_try_addr(trans, nm.id, arg0)
if sess_addr is not None:
return sess_addr
if return_var is None and mod is not None:
# 模块全局变量 c.Addr(模块全局) → 返回全局变量指针:必须经 make_global_ref 转成带引号的 Ptr(Ty) 引用(直接返回 GlobalVariable 节点时其 Ty 是 pointee、Name 无 @ 引号,参与 ptrtoint 会渲染成 "ptrtoint void to i64")
gv_ref_nm: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id)
if gv_ref_nm is not None and gv_ref_nm.Name is not None and gv_ref_nm.Ty is not None:
return_var = self.Gen.ExprGenAttr.make_global_ref(pool, gv_ref_nm.Name, gv_ref_nm.Ty)
# 模块函数地址:c.Addr(_on_int_complete) → 函数符号本身(@sha1.func)。
# 此前 Name 分支只查局部/模块全局变量,函数取地址失败 return None
# → 跌落跨模块函数调用生成 call @Addr → ld.lld undefined symbol:
# Addr(uhci.vp L130、coroutine.vp L133 c.Addr(_coro_entry)、
# sched.vp L209 c.Addr(_thread_entry) 根因)。
if return_var is None and mod is not None:
f_addr_ref: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, nm.id)
if f_addr_ref is not None:
f_addr_name: str = llvmlite.function_get_name(f_addr_ref)
if f_addr_name is not None:
# 形参链是 Param 链(__function.vp),而 llvmlite.Func 的类型构造链是
# ParamNode 链(__types.vp)——不相关类,统一经 _FuncTyFromFunction 重建。
fn_ty_ca: llvmlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, f_addr_ref)
# 函数引用名需按需加引号(数字开头/含 '.' 的 sha1 前缀名
# 必须 @\"...\",否则 llc "expected 'to' after cast value",
# uhci.vp c.Addr(_on_int_complete) 根因)。
fnlen_ca: sizet = string.strlen(f_addr_name)
needs_q_ca: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(f_addr_name)
faddr_buf_ca: str = pool.alloc(fnlen_ca + 4)
if faddr_buf_ca is not None and fn_ty_ca is not None:
if needs_q_ca != 0:
viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@\"%s\"", f_addr_name)
else:
viperlib.snprintf(faddr_buf_ca, fnlen_ca + 4, "@%s", f_addr_name)
return_var = llvmlite.SSAValue(pool, llvmlite.Ptr(pool, fn_ty_ca), faddr_buf_ca)
# 闭包变量:c.Addr(闭包名) → 返回闭包对象指针(load 一次)。
# 闭包 alloca 是 i8**(存 {fn_ptr, env_ptr} 结构地址);不 load 的话
# 会把 &闭包指针变量(i8**)当对象地址传,回调侧按闭包结构读
# fn_ptr 会读到指针变量的存储地址而非函数地址(与 Struct 对象
# 变量 c.Addr(obj) load 一次的语义一致)。
if return_var is not None and return_var.Ty is not None:
if self.Gen.CallGenLeafKit.is_closure_name(nm.id) != 0:
i8_ty_cb: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_cb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_cb)
return llvmlite.build_load(builder, i8_ptr_ty_cb, return_var)
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* = 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* = ast.Attribute*(arg0)
if at_addr is None or at_addr.attr is None or at_addr.value is None:
return None
# 跨模块函数地址 c.Addr(serial.puts) → 函数符号本身(@sha1.puts):不可回退 ExprGenAttr 当全局变量处理,否则与 vpi 注入的 puts 函数 declare 同名冲突(llc "redefinition of function")
if at_addr.value.kind() == ast.ASTKind.Name and trans is not None and trans._imported_modules is not None:
mod_nm2: ast.Name* = ast.Name*(at_addr.value)
if mod_nm2.id is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_nm2.id) != 0:
f_ref2: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, at_addr.attr)
if f_ref2 is not None:
fname2: str = llvmlite.function_get_name(f_ref2)
if fname2 is not None:
# 构造带 @ 名的 SSAValue(Ptr(FuncTy)):直接返回 Function 节点会被 IRBuilder 格式化成 RetTy(void)→ "bitcast void to i8*"。Function 无 Ty 字段,用 RetTy+Params 重建函数类型;Params 是 GSList,必须经 function_get_param_head 取链表头(当 Param 直接遍历会读出垃圾参数类型)
# 同上:Function 的 Param 形参链 → 重建 ParamNode 链再交 Func
fn_ty2: llvmlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, f_ref2)
# 函数引用名按需加引号(数字开头/含 '.' 的
# sha1 前缀名必须 @\"...\")。
fnlen2: sizet = string.strlen(fname2)
needs_q2: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(fname2)
faddr_buf2: str = pool.alloc(fnlen2 + 4)
if faddr_buf2 is not None and fn_ty2 is not None:
if needs_q2 != 0:
viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@\"%s\"", fname2)
else:
viperlib.snprintf(faddr_buf2, fnlen2 + 4, "@%s", fname2)
return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, fn_ty2), faddr_buf2)
else:
# 模块限定全局 c.Addr(gdt.gp) → 全局符号本身(@gp):跨模块全局(含 CExport 裸名)已注入为 external global,经 make_global_ref 转成带引号的 Ptr(Ty) 引用(直接返回 GlobalVariable 节点会渲染 "ptrtoint void")
g_ref2: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(mod, at_addr.attr)
if g_ref2 is not None and g_ref2.Name is not None and g_ref2.Ty is not None:
return self.Gen.ExprGenAttr.make_global_ref(pool, g_ref2.Name, g_ref2.Ty)
# 解析 base(Name 变量 / Subscript 数组元素)→ 对象指针
obj_ptr: llvmlite.Value* = None
struct_ty: llvmlite.LLVMType* = None
if at_addr.value.kind() == ast.ASTKind.Subscript:
# c.Addr(s.threads[0].rsp):base 是数组元素(Subscript)→ 先取
# 元素指针(GEP 数组),再 GEP 到字段。此前仅支持 Name base,
# Subscript 落到下方 return None → 跌落跨模块函数调用生成
# call @Addr(sched.vp L90/L182/L188/L196、coroutine.vp
# L99/L106/L112/L120 根因)。
sub_base: ast.Subscript* = ast.Subscript*(at_addr.value)
if sub_base.value is None or sub_base.slice is None:
return None
bval: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub_base.value, None, 0, trans)
bidx: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub_base.slice, None, 0, trans)
if bval is None or bidx is None or bval.Ty is None:
return None
bpointee: llvmlite.LLVMType* = None
if self.Gen.ExprGenBase.is_ptr_type(bval.Ty) != 0:
bpointee = bval.Ty.Pointee
if bpointee is not None:
match bpointee:
case llvmlite.LLVMType.Array(bet, _):
obj_ptr = llvmlite.build_gep_array(builder, bpointee, bet, bval, bidx)
struct_ty = bet
case _:
return None
else:
return None
elif at_addr.value.kind() == ast.ASTKind.Name:
self_nm: ast.Name* = ast.Name*(at_addr.value)
if self_nm.id is None:
return None
self_alloca: llvmlite.Value* = 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)))
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
# self_alloca 是 Ptr(Ptr(Struct)) 时先 load 成 Ptr(Struct) 对象指针
obj_ptr = self_alloca
if self_is_ptr_ptr == 1:
self_obj_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, struct_ty)
obj_ptr = llvmlite.build_load(builder, self_obj_ty, self_alloca)
else:
return None
if obj_ptr is None or struct_ty is None:
return None
# 查找字段
fe_addr: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(struct_ty, at_addr.attr)
if fe_addr 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]) / c.Addr(a.b[i][j]) → 元素地址(GEP,不 load)
# 统一走 get_subscript_ptr(递归处理嵌套下标、数组/指针/str|t.CPtr 形态)。
# 此前仅处理单层数组 GEP:嵌套下标(c.Addr(self.lines[line_idx][0]),
# vpui.vp L985)的 value 是内层 Subscript,translate_value 把内层加载成
# 数组值([80 x i8])→ 非 Ptr → return None → 跌落跨模块函数调用生成
# call @Addr(ld.lld undefined symbol: Addr 根因)。
if arg0.kind() == ast.ASTKind.Subscript:
if trans is None: return None
elem_ptr_ca: llvmlite.Value* = self.Gen.ExprGenName.get_subscript_ptr(builder, pool, mod, arg0, trans)
if elem_ptr_ca is not None: return elem_ptr_ca
# 兜底:单层数组基址 GEP(get_subscript_ptr 未命中的形态)
sub_addr: ast.Subscript* = ast.Subscript*(arg0)
if sub_addr is None or sub_addr.value is None or sub_addr.slice is None: return None
arr_val: llvmlite.Value* = 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* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub_addr.slice, None, 0, trans)
if idx_val is None: return None
arr_pointee_ca: llvmlite.LLVMType* = 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 _:
# 标量/指针元素(如字符串 a[4])→ 简单 GEP 元素地址。
# 此前仅 Array 走 GEP,标量 pointee 落 return None →
# 跌落跨模块函数调用生成 call @Addr(main.vp L272
# c.Addr(a[4]) 根因)。
return llvmlite.build_gep(builder, arr_pointee_ca, arr_val, idx_val)
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* = 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* = 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):fail-fast。
# c.Addr 是编译期指令,绝不跌落成运行时 call @Addr(ld.lld undefined
# symbol: Addr 根因——未识别形态 return None 会被 translate_call 当作
# 普通函数调用处理)。
self.Gen.TypeGenErr.fatal_error(cl, f"c.Addr 不支持的操作数形式(kind={arg0.kind()}),c.Addr 仅支持 变量/全局/函数/数组元素/字段", "TypeError")
return None
def translate_c_load(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""c.Load(a, b) → *a = *b(加载 b 的值,存储到 a)"""
if cl is None: return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 2: return None
dst_ptr: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), None, 0, trans)
src_ptr: llvmlite.Value* = 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* = 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* = llvmlite.Ptr(pool, pointee)
src_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, src_ptr, target_ptr_ty)
if src_cast is None: return None
loaded: llvmlite.Value* = llvmlite.build_load(builder, pointee, src_cast)
if loaded is None: return None
# bitcast dst 到 pointee* 并 store
dst_cast: llvmlite.Value* = 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: sizet,
src: str):
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None:
return
dlen: sizet = string.strlen(dst)
slen: sizet = string.strlen(src)
remain: sizet = dst_size - dlen
if remain <= 0:
return
i: sizet = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'
def _asm_mangle_global_symbols(self, pool: memhub.MemBuddy*,
asm_text: str, asm_size: sizet,
mod: llvmlite.LLVMModule*) -> str:
"""asm 模板中裸符号引用模块全局的检查(fail-fast)
内联汇编 `lea rax, [rip + gdt]` 的 gdt 是汇编器符号,若对应模块全局
带 SHA1 混淆头(@"054f5e56cbefcaa4.gdt")则无法在 LLVM MC 中引用:
数字开头的符号名会被解析成数字 → llc "Unexpected offset"(VKernel
gdt.vp L76/L93、sched.vp `[rip + _switch_old_rsp]` 根因)。因此引用
的全局必须标记 t.CExport 使用裸名(@gdt),此处按词边界匹配(前后
非标识符字符,避免误伤寄存器名/局部标签/指令助记符)命中即报错。
"""
if asm_text is None or mod is None:
return asm_text
src_len: sizet = string.strlen(asm_text)
if src_len == 0:
return asm_text
out: str = pool.alloc(asm_size * 2)
if out is None:
return asm_text
out[0] = '\0'
out_pos: sizet = 0
i: sizet = 0
while i < src_len:
matched: int = 0
cur: llvmlite.GlobalVariable* = mod.GlobalHead
while cur is not None:
gname: str = cur.Name
if gname is not None and string.strchr(gname, 46) is not None:
dot_g: str = string.strchr(gname, 46)
bare_g: str = dot_g + 1
bare_len: sizet = string.strlen(bare_g)
if bare_len > 0 and i + bare_len <= src_len:
if string.strncmp(asm_text + i, bare_g, bare_len) == 0:
# 词边界:前后非标识符字符(字母/数字/_)
prev_ok: int = 1
next_ok: int = 1
if i > 0:
prev_ch: char = asm_text[i - 1]
if (prev_ch >= 'a' and prev_ch <= 'z') \
or (prev_ch >= 'A' and prev_ch <= 'Z') \
or (prev_ch >= '0' and prev_ch <= '9') \
or prev_ch == '_':
prev_ok = 0
if i + bare_len < src_len:
next_ch: char = asm_text[i + bare_len]
if (next_ch >= 'a' and next_ch <= 'z') \
or (next_ch >= 'A' and next_ch <= 'Z') \
or (next_ch >= '0' and next_ch <= '9') \
or next_ch == '_':
next_ok = 0
if prev_ok != 0 and next_ok != 0:
# fail-fast:asm 模板引用的模块全局带 SHA1 混淆头
# 不可在 LLVM MC 内联汇编中使用(数字开头的符号名
# 会被解析成数字而报 "Unexpected offset"),必须
# 标记 t.CExport 使用裸名(如 gdt → @gdt)。
self.Gen.TypeGenErr.fatal_error(None, f"asm 模板引用的全局符号 '{bare_g}' 带 SHA1 混淆头({gname}),内联汇编必须引用裸名符号,请将该全局定义标记为 t.CExport", "TypeError")
return asm_text
cur = cur.Next
if matched == 0:
if out_pos + 1 < asm_size * 2:
out[out_pos] = asm_text[i]
out_pos += 1
i += 1
out[out_pos] = '\0'
return out
def _asm_escape_for_ir(self, pool: memhub.MemBuddy*,
src: str) -> str:
"""转义汇编文本用于 LLVM IR 字符串字面量"""
if src is None:
return None
slen: sizet = string.strlen(src)
buf: str = pool.alloc(slen * 4 + 8)
if buf is None:
return None
pos: sizet = 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"
# 8 位寄存器破坏描述符(对齐 t.py 标准常量集;缺 AL 曾致 AllocPage
# `rep stosb` 的 ~{al} 丢失——LLVM 不知 AL 被改,页地址 $0 可能被分配
# 进 RAX 被 mov al,0 破坏,A7 根因)
if string.strcmp(attr_name, "CLOBBER_AL") == 0: return "al"
if string.strcmp(attr_name, "CLOBBER_AH") == 0: return "ah"
if string.strcmp(attr_name, "CLOBBER_BL") == 0: return "bl"
if string.strcmp(attr_name, "CLOBBER_BH") == 0: return "bh"
if string.strcmp(attr_name, "CLOBBER_CL") == 0: return "cl"
if string.strcmp(attr_name, "CLOBBER_CH") == 0: return "ch"
if string.strcmp(attr_name, "CLOBBER_DL") == 0: return "dl"
if string.strcmp(attr_name, "CLOBBER_DH") == 0: return "dh"
# 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*,
node: ast.AST*) -> str:
"""递归解析约束表达式,返回约束字符串"""
if node is None:
return ""
k: int = node.kind()
# BinOp with BitOr: 拼接左右
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(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: sizet = string.strlen(left_str)
rlen: sizet = 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* = ast.Attribute*(node)
# t.ASM_DESCR.XXX
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
inner: ast.Attribute* = ast.Attribute*(at.value)
if inner.value is not None and inner.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(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* = ast.Name*(at.value)
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "t") == 0:
return self._resolve_register(at.attr)
# 裸名 ASM_DESCR.XXX:省去 t. 前缀的等价写法
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "ASM_DESCR") == 0:
return self._resolve_register(at.attr)
return ""
# Constant: 字符串常量
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_STR:
return cn.str_val
return ""
return ""
def _asm_get_operand(self, base: rawptr[GenTypes.AsmOperand],
idx: int) -> rawptr[GenTypes.AsmOperand]:
"""获取操作数数组中第 idx 个元素的指针"""
entry_size: sizet = GenTypes.AsmOperand.__sizeof__()
entry_addr: uint64 = uint64(base) + idx * entry_size
return rawptr[GenTypes.AsmOperand](entry_addr)
def _asm_is_c_call(self, node: ast.Call*) -> str:
"""检查 Call 节点是否是操作数标记内建调用,返回规范名或 None
支持两种等价写法(裸名写法省去 c. 前缀),裸名统一返回规范名,
使调用点的 string.strcmp 判定无需分支:
- 内联汇编:c.AsmInp/c.AsmOut ↔ asminp/asmout("AsmInp"/"AsmOut")
- LLVM IR:c.LInp/c.LOut ↔ irinp/irout("LInp"/"LOut")
"""
if node is None or node.func is None:
return None
if node.func.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node.func)
if nm.id is not None:
if string.strcmp(nm.id, "asminp") == 0:
return "AsmInp"
if string.strcmp(nm.id, "asmout") == 0:
return "AsmOut"
if string.strcmp(nm.id, "irinp") == 0:
return "LInp"
if string.strcmp(nm.id, "irout") == 0:
return "LOut"
return None
if node.func.kind() != ast.ASTKind.Attribute:
return None
at: ast.Attribute* = ast.Attribute*(node.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return None
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is None or string.strcmp(mod_nm.id, "c") != 0:
return None
return at.attr
def _asm_name_is_mangled(self, name: str) -> int:
"""判断 LLVM 名称是否带 SHA1 混淆头("{16位小写hex}."),返回 1=是
兼容 make_global_ref 生成的 '@"sha1.name"' 引用名和 GlobalVariable
裸名 "sha1.name"。'@name' 裸引用与局部 SSA(%N)都不算混淆。
判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex,
也不再扫描任意位置的 '.' 后回看 16 字节,避免 "a.b..." 形态误判)。
"""
if name is None:
return 0
n: str = name
nlen: sizet = string.strlen(n)
# 去掉 '@"..."' 包装(引用名)
if nlen >= 3 and n[0] == '@' and n[1] == '"' and n[nlen - 1] == '"':
n = n + 2
elif nlen >= 1 and n[0] == '@':
n = n + 1
if GenApi.sha1_prefix_len(n) != 0:
return 1
return 0
def _asm_add_operand(self, operands: rawptr[GenTypes.AsmOperand],
count: int,
val: llvmlite.Value*,
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
# fail-fast:AsmInp/AsmOut 操作数不允许带 SHA1 混淆头的符号
# (模块级全局/函数)。LLVM MC 内联汇编无法解析数字开头的符号名,
# 且裸机链接要求裸名 —— 必须将对应定义标记为 t.CExport。
if val is not None and val.Name is not None:
if self._asm_name_is_mangled(val.Name) != 0:
self.Gen.TypeGenErr.fatal_error(None, f"c.Asm 操作数(AsmInp/AsmOut)不允许带 SHA1 混淆头的符号 '{val.Name}',请将对应函数/全局定义标记为 t.CExport(裸名)", "TypeError")
if val is not None:
for ei in range(count):
eop: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, ei)
if eop is not None and eop.Value == val and eop.IsOutput == is_output:
return count
op: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, count)
op.Value = val
# 缺省约束:AsmInp(reg) 只传 1 参无 descr → 约束空 → llc
# "number of input constraints does not match number of parameters"
# (mm.vp L730 c.Asm(f"mov al, {AsmInp(reg)}\nout 0x70, al") 根因)。
# 输入默认 "r"(通用寄存器)、输出默认 "=r"(寄存器输出),
# 与 _resolve_register 的 REG_ANY/OUTPUT_REG 一致。
if constraint is None or string.strlen(constraint) == 0:
if is_output != 0:
constraint = "=r"
else:
constraint = "r"
op.Constraint = constraint
op.IsOutput = is_output
return count + 1
def translate_c_asm(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译 c.Asm 内联汇编,返回结果 Value(无输出时返回 None)"""
if cl is None:
return None
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
# 分配操作数数组
operand_size: sizet = GenTypes.AsmOperand.__sizeof__()
operands: rawptr[GenTypes.AsmOperand] = pool.alloc(operand_size * ASM_MAX_OPERANDS)
if operands is None:
return None
string.memset(operands, 0, operand_size * ASM_MAX_OPERANDS)
# 分配 clobber 名数组(每槽 8 字节存字符串指针;容量=ASM_MAX_CLOBBERS,
# 与操作数上限解耦——长 clobber 集如 vipersimd ×8 的 21 项不得截断,
# 否则硬编码 GPR(rcx/r8-r11)声明丢失 → 输入被 llc 错分配进被 clobber
# 的寄存器 → 寄存器错乱/零页崩溃)
# 类型必须是**单级**裸缓冲(t.CPtr = i8*):pool.alloc 返回 t.CPtr、
# string.memset 首参也是 t.CPtr;每槽按 8 字节字符串指针写入由下方的
# `cl_ptr: str | t.CPtr = (str | t.CPtr)(cl_addr)` 显式 cast 完成
# (与 includes/_dict.vp 的一致模式:缓冲单级、槽视图 str | t.CPtr)。
# 此前声明成 str | t.CPtr(=i8**) 与 alloc/memset 层数不符,隐式指针转换
# 禁止"层数不同",首行即报错。
clobbers: t.CPtr = pool.alloc(8 * ASM_MAX_CLOBBERS)
if clobbers is None:
return None
string.memset(clobbers, 0, 8 * ASM_MAX_CLOBBERS)
# 分配 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
# 首个输出操作数的元素类型缓存(对齐 python 参考 HandlesExprAsm 的
# "收集时即定 ret 类型":不依赖事后从 pool 槽二次读回 Value.Ty.Pointee,
# 避免读回不稳定 → ret 静默 Void → 带名 void asm(atom.vp 根因)。
out_ret_ty_first: llvmlite.LLVMType* = None
# 全量输出元素类型按序缓存(多输出如 vrandom rdrand*_step 的 val+ok):
# 同样在收集时直接从 alloca 取 Pointee,绕开 AsmOperand 槽读不稳定。
out_ptys: sizet* = pool.alloc(8 * ASM_MAX_OPERANDS)
out_pty_n: int = 0
if out_ptys is not None:
string.memset(out_ptys, 0, 8 * ASM_MAX_OPERANDS)
# 分配 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* = cargs.get(0)
if tmpl_node is not None:
tk: int = tmpl_node.kind()
if tk == ast.ASTKind.JoinedStr:
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](tmpl_node)
jvalues: list[ast.AST*]* = js.values
if jvalues is not None:
jvlen: sizet = len(jvalues)
for vi in range(jvlen):
part: ast.AST* = jvalues.get(vi)
if part is None:
continue
pk: int = part.kind()
if pk == ast.ASTKind.FormattedValue:
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part)
fexpr: ast.AST* = fv.value
if fexpr is None or fexpr.kind() != ast.ASTKind.Call:
continue
fcall: ast.Call* = ast.Call*(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*]* = fcall.args
if fargs is None or len(fargs) < 1:
continue
val_node: ast.AST* = 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(Name 局部/全局 或
# Attribute 字段地址)
if val_node is None:
continue
alloca: llvmlite.Value* = None
if val_node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(val_node)
if nm.id is None:
continue
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None:
# 模块级全局变量(process.vp _saved_cr3 来自
# isr.s .comm):lookup_var 只查局部 → 全局
# 落空 → 操作数缺失 → asm 模板 "$0" 空 →
# "mov , cr3" llc unknown token in expression。
# 回退查模块全局,并**经 make_global_ref 转成 Value 引用**:
# GlobalVariable 与 Value 是不相关类(不可互转,且字段顺序不同
# —— GlobalVariable.Name 在 Ty 之前,旧的无条件 bitcast 会让
# .Ty 读到 Name),必须按 c.Addr(模块全局) 分支的同一写法构造
# 带引号的 Ptr(Ty) 引用(见 L309 注释)。
gv_asm: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id)
if gv_asm is not None and gv_asm.Name is not None and gv_asm.Ty is not None:
alloca = self.Gen.ExprGenAttr.make_global_ref(pool, gv_asm.Name, gv_asm.Ty)
elif val_node.kind() == ast.ASTKind.Attribute:
# AsmOut(p.pml4_root)(p 为 Process 指针):
# GEP 到字段地址(对齐 translate_c_addr Attribute
# 分支)。仅 Name 检查时 Attribute 被跳过 →
# 空操作数 "mov , cr3"(process.vp L127/L208 根因)。
at_a: ast.Attribute* = ast.Attribute*(val_node)
if at_a.attr is not None and at_a.value is not None and at_a.value.kind() == ast.ASTKind.Name:
base_nm_a: ast.Name* = ast.Name*(at_a.value)
if base_nm_a.id is not None:
base_alloca_a: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, base_nm_a.id)
if base_alloca_a is not None and base_alloca_a.Ty is not None:
struct_ty_a: llvmlite.LLVMType* = None
self_is_ptr_ptr_a: int = 0
match base_alloca_a.Ty:
case llvmlite.LLVMType.Ptr(inner1):
match inner1:
case llvmlite.LLVMType.Ptr(inner2):
struct_ty_a = inner2
self_is_ptr_ptr_a = 1
case _:
struct_ty_a = inner1
case _:
pass
if struct_ty_a is not None:
fe_a: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(struct_ty_a, at_a.attr)
if fe_a is not None:
obj_ptr_a: llvmlite.Value* = base_alloca_a
if self_is_ptr_ptr_a == 1:
obj_ptr_a = llvmlite.build_load(builder, llvmlite.Ptr(pool, struct_ty_a), base_alloca_a)
if obj_ptr_a is not None:
alloca = llvmlite.build_gep_struct(builder, struct_ty_a, fe_a.Ty, obj_ptr_a, fe_a.Index)
if alloca is None:
continue
operand_count = self._asm_add_operand(operands, operand_count, alloca, cons_str, 1)
if out_ret_ty_first is None and alloca is not None and alloca.Ty is not None and alloca.Ty.Pointee != 0:
out_ret_ty_first = alloca.Ty.Pointee
if out_ptys is not None and out_pty_n < ASM_MAX_OPERANDS and alloca is not None and alloca.Ty is not None and alloca.Ty.Pointee != 0:
out_ptys[out_pty_n] = uint64(alloca.Ty.Pointee)
out_pty_n += 1
else:
# 输入:翻译值
ival: llvmlite.Value* = 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*]* = cl.keywords
if keywords is not None:
kwlen: sizet = len(keywords)
for ki in range(kwlen):
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
kwk: int = kw.value.kind()
if string.strcmp(kw.arg, "out") == 0:
# 输出操作数列表
if kwk != ast.ASTKind.List: continue
out_lst: ast.List* = ast.List*(kw.value)
out_elts: list[ast.AST*]* = out_lst.elts
if out_elts is None: continue
oelen: sizet = len(out_elts)
for oi in range(oelen):
oelt: ast.AST* = out_elts.get(oi)
if oelt is None or oelt.kind() != ast.ASTKind.Call: continue
ocall: ast.Call* = ast.Call*(oelt)
oattr: str = self._asm_is_c_call(ocall)
if oattr is None or string.strcmp(oattr, "AsmOut") != 0: continue
oargs: list[ast.AST*]* = ocall.args
if oargs is None or len(oargs) < 1: continue
oval_node: ast.AST* = 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* = ast.Name*(oval_node)
if onm.id is None: continue
oalloca: llvmlite.Value* = 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)
if out_ret_ty_first is None and oalloca is not None and oalloca.Ty is not None and oalloca.Ty.Pointee != 0:
out_ret_ty_first = oalloca.Ty.Pointee
if out_ptys is not None and out_pty_n < ASM_MAX_OPERANDS and oalloca is not None and oalloca.Ty is not None and oalloca.Ty.Pointee != 0:
out_ptys[out_pty_n] = uint64(oalloca.Ty.Pointee)
out_pty_n += 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* = ast.List*(kw.value)
in_elts: list[ast.AST*]* = in_lst.elts
if in_elts is None: continue
ielen: sizet = len(in_elts)
for ii in range(ielen):
ielt: ast.AST* = in_elts.get(ii)
if ielt is None or ielt.kind() != ast.ASTKind.Call: continue
icall: ast.Call* = ast.Call*(ielt)
iattr: str = self._asm_is_c_call(icall)
if iattr is None or string.strcmp(iattr, "AsmInp") != 0: continue
iargs: list[ast.AST*]* = icall.args
if iargs is None or len(iargs) < 1: continue
ival_node: ast.AST* = iargs.get(0)
icons: str = ""
if len(iargs) >= 2:
icons = self._resolve_asm_descr(pool, iargs.get(1))
ival2: llvmlite.Value* = 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* = ast.List*(kw.value)
cl_elts: list[ast.AST*]* = cl_lst.elts
if cl_elts is None: continue
clen: sizet = len(cl_elts)
for ci in range(clen):
celt: ast.AST* = 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_CLOBBERS:
cl_addr: uint64 = uint64(clobbers) + clobber_count * 8
cl_ptr: str | t.CPtr = (str | t.CPtr)(cl_addr)
cl_ptr[0] = cstr
clobber_count += 1
# ============================================================
# 3. 解析第二个位置参数作为破坏列表
# ============================================================
if len(cargs) >= 2:
pos_node: ast.AST* = cargs.get(1)
if pos_node is not None and pos_node.kind() == ast.ASTKind.List:
pos_lst: ast.List* = ast.List*(pos_node)
pos_elts: list[ast.AST*]* = pos_lst.elts
if pos_elts is not None:
plen: sizet = len(pos_elts)
for pi in range(plen):
pelt: ast.AST* = 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_CLOBBERS:
pcl_addr: uint64 = uint64(clobbers) + clobber_count * 8
pcl_ptr: str | t.CPtr = (str | t.CPtr)(pcl_addr)
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: rawptr[GenTypes.AsmOperand] = 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* = llvmlite.Void(pool)
if output_count > 0 and first_output_idx >= 0:
if output_count > 1:
# 多输出返回类型 {T1, T2, ...} 的字段链表是 ParamNode 链
# (new_param_node 返回 rawptr[ParamNode]、Struct 形参 rawptr[ParamNode]、
# ParamNode.Next: ParamNode|CPtr),原先误注解为 Function*(不相关类)
# → 1264/1268 两处会被判 struct↔struct 隐式转换禁止。
out_pn_head: llvmlite.ParamNode* = None
out_pn_last: llvmlite.ParamNode* = None
out_pn_n: int = 0
out_seq: int = 0
for oi5 in range(operand_count):
op5: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, oi5)
if op5 is None or op5.IsOutput == 0:
continue
pty5: llvmlite.LLVMType* = None
# 优先收集时按序缓存的直接类型,再回退从槽读回
if out_ptys is not None and out_seq < out_pty_n:
pty5 = llvmlite.LLVMType*(out_ptys[out_seq])
elif op5.Value is not None and op5.Value.Ty is not None and op5.Value.Ty.Pointee != 0:
pty5 = op5.Value.Ty.Pointee
out_seq += 1
if pty5 is None:
continue
pn5: llvmlite.ParamNode* = llvmlite.new_param_node(pool, pty5)
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: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, first_output_idx)
out_pt: llvmlite.LLVMType* = None
# 优先用收集时缓存的元素类型(对齐 python 参考),再回退从槽读回
if out_ret_ty_first is not None:
out_pt = out_ret_ty_first
elif out_op is not None and out_op.Value is not None and out_op.Value.Ty is not None and out_op.Value.Ty.Pointee != 0:
out_pt = out_op.Value.Ty.Pointee
if out_pt is not None:
ret_ty = out_pt
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
# [FIX] 有输出但 ret 仍是 Void:选择阶段(L1394 读 first_output_idx)
# 偶发读不到输出操作数 Value/类型(静默留 Void → 发射带名 void asm →
# llc "instructions returning void cannot have a name")。发射前重扫
# 全部输出操作数再定一次(store 阶段同一数组可读到 Value);仍缺失则
# fail-fast,拒绝静默发射非法 IR。
if is_void == 0:
match ret_ty:
case llvmlite.LLVMType.Void():
ret_re: llvmlite.LLVMType* = None
for re_i in range(operand_count):
re_op: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, re_i)
if re_op is None or re_op.IsOutput == 0: continue
if re_op.Value is not None and re_op.Value.Ty is not None and re_op.Value.Ty.Pointee != 0:
ret_re = re_op.Value.Ty.Pointee
break
if ret_re is None:
self.Gen.TypeGenErr.fatal_error(cl, "c.Asm 输出操作数类型缺失(变量 alloca 类型退化)", "TypeError")
ret_ty = ret_re
case _:
pass
# ============================================================
# 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: rawptr[GenTypes.AsmOperand] = 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: rawptr[GenTypes.AsmOperand] = 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: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](tmpl_node)
jvalues2: list[ast.AST*]* = js2.values
if jvalues2 is not None:
jvlen2: sizet = len(jvalues2)
fv_fi: int = 0
for vi2 in range(jvlen2):
part2: ast.AST* = jvalues2.get(vi2)
if part2 is None:
continue
pk2: int = part2.kind()
if pk2 == ast.ASTKind.Constant:
cn3: ast.Constant* = ast.Constant*(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* = ast.Constant*(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: rawptr[GenTypes.AsmOperand] = 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: uint64 = uint64(clobbers) + cidx * 8
cname: str = (str | t.CPtr)(cl_addr2)[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: rawptr[GenTypes.AsmOperand] = self._asm_get_operand(operands, orig_i2)
if aop.IsOutput != 0: continue
aval: llvmlite.Value* = 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. 转义汇编文本
# 7.0 先做裸符号→混淆名替换(lea rax, [rip + gdt] → [rip + 054f5e56cbefcaa4.gdt])
mangled_asm: str = self._asm_mangle_global_symbols(pool, asm_text, ASM_TEXT_BUF_SIZE, mod)
if mangled_asm is None:
mangled_asm = asm_text
escaped_asm: str = self._asm_escape_for_ir(pool, mangled_asm)
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* = 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: rawptr[GenTypes.AsmOperand] = 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 != 0:
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* = 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*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""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*]* = 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: sizet* = pool.alloc(8 * LLVMIR_MAX_OPS)
out_regname: sizet* = 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* = cargs.get(0)
if first_arg is None:
return None
tk: int = first_arg.kind()
if tk == ast.ASTKind.JoinedStr:
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](first_arg)
jvalues: list[ast.AST*]* = js.values
if jvalues is not None:
jvlen: sizet = len(jvalues)
for vi in range(jvlen):
part: ast.AST* = jvalues.get(vi)
if part is None:
continue
pk: int = part.kind()
if pk == ast.ASTKind.Constant:
# 字符串字面量片段
cn: ast.Constant* = ast.Constant*(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: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part)
fexpr: ast.AST* = fv.value
if fexpr is None:
continue
# 检测 c.LInp / c.LOut
if fexpr.kind() == ast.ASTKind.Call:
fcall: ast.Call* = ast.Call*(fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
if attr_nm is not None:
fargs: list[ast.AST*]* = fcall.args
if fargs is None or len(fargs) < 1:
continue
val_node: ast.AST* = fargs.get(0)
if string.strcmp(attr_nm, "LInp") == 0:
# 输入操作数:翻译值,替换为 LLVM 值引用文本(%x 或常量文本)
inp_val: llvmlite.Value* = 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* = ast.Name*(val_node)
if nm.id is not None:
alloca: llvmlite.Value* = 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] = sizet(alloca)
out_regname[out_count] = sizet(reg_buf)
out_count += 1
continue
# 非 c.LInp/c.LOut 的 f-string 表达式:翻译值并嵌入值文本
ival: llvmlite.Value* = 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* = ast.Constant*(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* = llvmlite.Value*(out_alloca[oi])
rn: str = str(out_regname[oi])
if oa is None or oa.Ty is None or oa.Ty.Pointee == 0 or rn is None:
continue
reg_val: llvmlite.Value* = 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
# 显式参数(位置/关键字)后续会覆盖默认值
# ============================================================