- @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。
1659 lines
95 KiB
Plaintext
1659 lines
95 KiB
Plaintext
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
|
||
# 显式参数(位置/关键字)后续会覆盖默认值
|
||
# ============================================================
|